API Documentation
BruteSight provides real-time threat intelligence feeds generated from a global network of honeypots.
Use this API to programmatically access attack data, generate dynamic blocklists for firewalls, or ingest logs from your own servers into the collective intelligence pool.
Authentication
All API requests to secured endpoints must include your unique API key. Keys can be passed in one of two ways:
- HTTP Header (Recommended):
X-API-Key: YOUR_API_KEY - Query Parameter (For legacy devices):
?api_key=YOUR_API_KEY
1. Ingestion API (Agent Reporting)
Use this endpoint to report brute-force attacks (e.g., SSH, RDP, FTP) from your local servers to the global BruteSight network. This endpoint is highly optimized for rapid, high-volume ingestion.
Headers: Content-Type: application/json, X-API-Key: YOUR_API_KEY_HERE
Request Payload Example
You can send logs individually or batch them in an array to save bandwidth.
{
"meta": {
"version": "1.8.5",
"source_ip": "198.51.100.12"
},
"logs": [
{
"username": "root",
"hostname": "203.0.113.45",
"login_time": "2026-05-02 14:32:01",
"raw_entry": "Failed password for root from 203.0.113.45 port 49152 ssh2",
"subnet_24": "203.0.113.0/24"
}
]
}
Response Example
{
"status": "success",
"inserted": 1
}
*Note: If your agent is outdated, the response headers will include X-Agent-Update-Required: true along with the latest script hash in X-Latest-Agent-Version.*
2. Threat Feed Consumption APIs
Our consumption APIs are designed for automated firewalls, routers, and SIEMs.
Global Feed Filters
All /list/* endpoints support the following universal query parameters to help you tailor your blocklists:
type: Output format.ipfor exact single IPs, orcidrfor grouped/24subnets. (Default:ip)mincount: Minimum number of attack hits required to be included. (Default:1)days: Look back window in days (e.g.,7). (Default: All-Time)country: Filter by a specific 2-letter Country Code (e.g.,CN,RU).asn: Filter by a specific ASN (e.g.,64496).
Generic Text Feed
For pfSense, iptables, and generic firewalls. Returns a flat, line-by-line list.
Example Request: GET /api/v1/list/plain?mincount=10&type=cidr&days=7
192.168.1.0/24
10.0.0.0/24
172.16.55.0/24
Mikrotik RouterOS
Dynamically generates a .rsc script that can be imported directly into Mikrotik routers.
list_name(string): The name of the address list in RouterOS (Default:Blacklist_SSH).timeout(string): Expiration time for the block rule (e.g.,2d,12h).
Example Request: GET /api/v1/list/mikrotik?list_name=BruteSight&timeout=7d&mincount=50
/ip firewall address-list
add list=BruteSight address=203.0.113.45 timeout=7d comment="ASN:64496 CC:US Hits:450"
add list=BruteSight address=198.51.100.22 timeout=7d comment="ASN:64500 CC:GB Hits:112"
Cisco IOS ACL
Generates standard Access Control Lists with calculated wildcard masks for Cisco environments.
acl_name(string): Name of the Cisco Access List (Default:BLOCK_SSH).
Example Request: GET /api/v1/list/cisco?acl_name=BLOCK_SSH&type=cidr
ip access-list standard BLOCK_SSH
deny 192.168.1.0 0.0.0.255
deny 10.0.0.0 0.0.0.255
permit any
Juniper Networks
Generates policy options and prefix-lists natively formatted for Juniper routing environments.
list_name(string): Name of the prefix-list (Default:BAD-SSH).
Example Request: GET /api/v1/list/juniper?list_name=BAD-SSH&type=cidr
policy-options {
prefix-list BAD-SSH {
192.168.1.0/24;
10.0.0.0/24;
}
}
Enterprise JSON
Returns heavily structured metadata for advanced threat hunting and ingestion pipelines (SIEMs, Palo Alto, Splunk).
{
"meta": {
"count": 1,
"filters": {
"asn": null,
"country": null,
"days": 30,
"min_count": 5,
"type": "ip"
}
},
"data": [
{
"target": "203.0.113.45",
"asn": "64496",
"country_code": "US",
"hit_count": 450
}
]
}
3. Real-Time Statistics API
This endpoint powers our public dashboards and your custom internal visualizations, providing instant data retrieval across millions of records.
days(int): Number of days to look back, or leave blank for All-Time. Any positive integer is accepted;1,7, and30are pre-warmed server-side and respond fastest.filter_type(string): Search parameter. Valid options:username,asn, orcountry.filter_value(string): The value corresponding to thefilter_type(e.g.,root,64496, orCN).
Example Request: GET /api/v1/stats?days=7&filter_type=country&filter_value=CN
{
"totals": {
"hits": 1450230,
"ips": 85401,
"last_seen": "2026-05-02 14:32:05"
},
"top_countries": [
{"country_code": "CN", "c": 1450230}
],
"top_asns": [
{"asn": "64511", "asn_description": "EXAMPLE-BACKBONE", "c": 210000}
],
"top_users": [
{"username": "root", "c": 980123}
],
"recent_logs": [
{"login_time": "2026-05-02 14:32:05", "hostname": "203.0.113.45", "username": "root", "country_code": "CN", "asn": "64511"}
]
}
Hardware Integrations
Use these scripts to automatically download and apply BruteSight threat feeds directly to your networking hardware. Every snippet below requires a valid API key — replace YOUR_API_KEY_HERE with the key from your dashboard before using it.
Mikrotik Auto-Updater Script
Fetches the .rsc file into a staging address-list and only swaps it onto the live list once /import has succeeded (a bad key or rate-limited request returns an error body that fails to import, which is caught automatically) and produced at least one entry. This means a transient API error, bad key, or an over-aggressive mincount filter that matches nothing can never leave you with an empty or half-updated block list. Note: don't try to validate the response by reading the downloaded file back with /file get contents — on many RouterOS builds that returns nothing for files beyond a few KB, which silently breaks the check; let /import's own error handling do that job instead. Because the API provides a timeout=7d parameter, Mikrotik automatically expires old IPs on its own. The API key is passed as a query parameter since /tool fetch does not support custom headers on all RouterOS versions.
Run the first two lines as normal CLI commands, then use the built-in editor for the script body — RouterOS interpolates $variables and "quotes" inside any double-quoted string, so a script this size is much safer pasted into an editor buffer than hand-escaped onto a single source="..." line:
/system script add name=UpdateBruteSight policy=read,write,policy,test
/system script edit UpdateBruteSight source
Paste the following into the editor that opens, then save and exit (Ctrl+O):
:local startTime [/system resource get uptime]
:local listName "BruteSight"
:local stagingList ($listName . "_staging")
:local tempFile "brutesight.rsc"
:local url ("https://api.brutesight.com/api/v1/list/mikrotik?list_name=" . $stagingList . "&timeout=7d&type=cidr&mincount=10&api_key=YOUR_API_KEY_HERE")
/ip firewall address-list remove [find list=$stagingList]
:if ([:len [/file find name=$tempFile]] > 0) do={ /file remove [find name=$tempFile] }
:local fetchOk true
do {
/tool fetch url=$url mode=https dst-path=$tempFile check-certificate=yes
} on-error={
:set fetchOk false
:log error "BruteSight: download failed, keeping existing list"
}
:if ($fetchOk) do={
:local fileSize 0
:if ([:len [/file find name=$tempFile]] > 0) do={ :set fileSize [/file get [find name=$tempFile] size] }
:if ($fileSize > 0) do={
:local importOk true
do {
/import file-name=$tempFile
} on-error={
:set importOk false
:log error "BruteSight: import failed (bad key, rate limited, or malformed response?)"
}
:if ($importOk) do={
:local newCount [:len [/ip firewall address-list find list=$stagingList]]
:if ($newCount > 0) do={
/ip firewall address-list remove [find list=$listName]
/ip firewall address-list set [find list=$stagingList] list=$listName
:local elapsed ([/system resource get uptime] - $startTime)
:log info ("BruteSight: Updated " . $listName . " with " . $newCount . " entries, took " . $elapsed . " to process.")
} else={
:log warning "BruteSight: staged list was empty, skipping swap"
}
}
} else={
:log error "BruteSight: downloaded file was empty"
}
}
/ip firewall address-list remove [find list=$stagingList]
:if ([:len [/file find name=$tempFile]] > 0) do={ /file remove [find name=$tempFile] }
Then schedule it to run hourly:
/system scheduler add interval=1h name=ScheduleBruteSight on-event=UpdateBruteSight policy=read,write,policy,test start-time=startup
Mikrotik BGP RTBH Integration
BGP Remote Triggered Black Hole (RTBH) is the fastest way to respond to attacks at scale. BruteSight peers with your router over eBGP and announces
/32 threat prefixes tagged with BGP community 65535:666 (RFC 7999 BLACKHOLE well-known community)
and a trigger next-hop of 192.0.2.1. You install a static null route for 192.0.2.1/32 on your router; any threat prefix that arrives via BGP then
recursively resolves to that null route, dropping the traffic at line rate before it reaches your servers.
BruteSight announces only prefixes that have been seen attacking systems within the last 30 days and that meet the minimum hit-count threshold configured for your account. Your input filter (step 2 below) restricts acceptance to your own IP space, so no third-party prefixes are ever null-routed. See the RouterOS v7 Route Selection and Filters reference for full filter DSL syntax.
Step 1 — Add the RTBH trigger null route
This static blackhole is the anchor. Every BGP route BruteSight sends uses 192.0.2.1 as its next-hop; this route makes that next-hop resolve to discard.
Distance 254 keeps it below any real route you may already have for this address.
/ip route add dst-address=192.0.2.1/32 type=blackhole distance=254 comment="BruteSight RTBH trigger"
Step 2 — Create the BGP route filter chains
BruteSight announces attacker IPs — host routes (/32) for source addresses seen actively attacking systems worldwide.
These can be any IP on the internet; they are not your own prefixes, so the input filter must not restrict by destination address range.
Instead, the chain is built from safeguard rules that run first, followed by a single community-triggered accept:
- Prefix-length guard — reject anything that is not a
/32. This blocks the default route (0.0.0.0/0), any subnet aggregate, and any attempt to blackhole a whole address block in a single route. - Whitelist guard — reject routes for any IP address or range that must never be null-routed on your network, even if BruteSight flags it with the BLACKHOLE community. This typically includes your own assigned address blocks, loopback and management IPs, upstream transit provider addresses, critical DNS resolvers, NTP servers, and any trusted partner ranges. If a whitelisted IP ever appears in the BruteSight feed (e.g. a shared hosting provider that also hosts attackers), this rule ensures your router ignores the blackhole signal for it.
- Community accept — any
/32that survived the guards and carries community65535:666is marked as a blackhole route and accepted. Theset blackhole yesaction marks the route type directly; combined with the192.0.2.1trigger next-hop from step 1 this gives two independent layers of null-routing. - Default reject — everything else (missing community, wrong source, etc.) is dropped.
# --- Input chain ---
# Rule 1: only accept /32 host routes — rejects 0.0.0.0/0 and all subnets
/routing filter rule add chain=brutesight-bgp-in \
rule="if (dst-len != 32) { reject; }"
# Rule 2: whitelist — reject any IP or range that must never be blackholed.
# Typical entries: your own address blocks, upstream transit IPs, management
# networks, DNS resolvers, NTP servers, and trusted partner ranges.
# Add one reject rule per entry; order does not matter within this group.
/routing filter rule add chain=brutesight-bgp-in \
rule="if (dst in YOUR_OWN_ADDRESS_BLOCK/MASK) { reject; }"
/routing filter rule add chain=brutesight-bgp-in \
rule="if (dst in YOUR_UPSTREAM_TRANSIT_BLOCK/MASK) { reject; }"
/routing filter rule add chain=brutesight-bgp-in \
rule="if (dst in YOUR_MANAGEMENT_SUBNET/MASK) { reject; }"
# Continue adding reject rules for every range that needs protection
# Rule 3: accept and blackhole any /32 carrying the RFC 7999 BLACKHOLE community
/routing filter rule add chain=brutesight-bgp-in \
rule="if (bgp-communities includes 65535:666) { set blackhole yes; accept; }"
# Rule 4: reject everything that did not match above
/routing filter rule add chain=brutesight-bgp-in rule="reject;"
# --- Output chain: advertise nothing back to BruteSight ---
/routing filter rule add chain=brutesight-bgp-out rule="reject;"
Replace each placeholder in rule 2 with the actual CIDR blocks that are critical to your network.
Think of these as addresses that, if accidentally blackholed, would break your own services or connectivity —
your assigned public IP space, your router's transit-facing addresses, management access ranges, authoritative DNS servers, and any partner IPs you exchange traffic with.
There is no limit on the number of whitelist rules; add as many as your network requires.
Rule order matters: rules are evaluated top-to-bottom and the first match wins, so all whitelist
reject rules must appear before the community accept in rule 3.
The bgp-communities includes and set blackhole actions follow the
RouterOS v7 filter DSL.
Step 3 — Add the BGP connection
Use local.role=ebgp (standard eBGP peer). Do not use ebgp-rs (Route Server) — that role activates
RFC 9234
role negotiation and will suppress routes from peers that don't advertise the BGP Role capability, which GoBGP does not.
Replace the placeholders with the values from your BruteSight session.
/routing bgp connection add \
name=peer-brutesight \
remote.address=45.126.191.243 \
remote.as=64512 \
remote.port=46179 \
local.address=YOUR_ROUTER_BGP_IP \
local.role=ebgp \
as=YOUR_ASN \
input.filter=brutesight-bgp-in \
output.filter-chain=brutesight-bgp-out \
connect=yes \
afi=ip
Step 4 — Apply the TCP MD5 session key
The MD5 key is shown once when the session is created in the admin portal. It authenticates the BGP TCP stream (RFC 2385) and must match exactly what is stored in the BruteSight session record.
/routing bgp connection set [find name=peer-brutesight] tcp-md5-key=YOUR_MD5_KEY_FROM_PORTAL
Verification
After the admin enables your session in the portal, the BGP connection will come up within a few seconds. Use these commands to confirm routes are flowing and resolving to the blackhole:
# Check session state and received prefix count
/routing bgp session print where peer=peer-brutesight
# List BGP routes installed in the routing table (should show /32s via 192.0.2.1)
/ip route print where bgp
# Confirm 192.0.2.1 resolves to the null route
/ip route print where dst-address=192.0.2.1/32
A healthy session will show prefix-count greater than zero (the exact number depends on how many threat sources have been seen targeting
your specific address blocks). Each installed BGP route will have gateway=192.0.2.1 which recursively resolves to the
type=blackhole static route added in step 1.
BruteSight server:
45.126.191.243 — AS: 64512 — BGP port: 46179Announced next-hop:
192.0.2.1 — Community: 65535:666 (RFC 7999 BLACKHOLE)
Cisco IOS EEM Applet
Cisco IOS uses the Embedded Event Manager (EEM) to automate tasks. This applet triggers every 3600 seconds (1 hour), safely copying the latest standard ACL over HTTP(S) directly into the running configuration.
conf t
event manager applet UPDATE_BRUTESIGHT
event timer watchdog time 3600
action 1.0 cli command "enable"
action 2.0 cli command "copy https://api.brutesight.com/api/v1/list/cisco?acl_name=BLOCK_SSH&api_key=YOUR_API_KEY_HERE running-config" pattern "\[running-config\]\?"
action 3.0 cli command "\r"
action 4.0 syslog msg "BruteSight ACL Successfully Updated"
end
wr mem
Juniper OS Push Script (Linux Jump Server)
Because Junos OS requires a strict "configure → commit" model, the most reliable way to update Juniper firewalls is by using a scheduled Bash script on a Linux jump-server to pull the feed and push it to the router via SSH.
#!/bin/bash
# Save this to /usr/local/bin/update_juniper.sh and schedule it via crontab
ROUTER_IP="192.168.1.1"
USER="admin"
API_KEY="YOUR_API_KEY_HERE"
API_URL="https://api.brutesight.com/api/v1/list/juniper?list_name=BAD-SSH&type=cidr"
# 1. Download the latest prefix-list
curl -s -H "X-API-Key: $API_KEY" "$API_URL" > /tmp/brutesight.conf
# 2. SCP the configuration file to the Juniper router
scp /tmp/brutesight.conf $USER@$ROUTER_IP:/var/tmp/brutesight.conf
# 3. Execute the configuration load and commit via SSH
ssh $USER@$ROUTER_IP << 'EOF'
configure
load replace /var/tmp/brutesight.conf
commit
exit
EOF
Installing the Remote Agent
The BruteSight remote agent is a lightweight, dependency-minimal Python script (push_remote_logs.py) designed to parse local authentication logs and securely push them to the BruteSight ingestion API in batches.
1. Prerequisites
- Python 3.6 or higher
- The
requestslibrary (Install viapip install requestsorapt install python3-requests)
2. Download & Install
Fetch the latest stable release of the agent script directly from the API and make it executable.
curl -o /usr/local/bin/push_remote_logs.py https://api.brutesight.com/api/v1/update-script
chmod +x /usr/local/bin/push_remote_logs.py
3. Configuration
The agent looks for its configuration file at /etc/brutesight/config.conf by default. Create this directory and file, replacing the placeholder with your actual API key.
mkdir -p /etc/brutesight
nano /etc/brutesight/config.conf
[brutesight]
api_key = YOUR_API_KEY_HERE
# Optional overrides (Defaults shown below)
# log_file = /var/log/auth.log
# state_file = /var/tmp/push_remote_logs.state
# batch_size = 50
4. Set up the Cron Job
Schedule the script to run every 5 minutes to continuously parse new logs and push them to the cloud.
crontab -e
*/5 * * * * root /usr/bin/python3 /usr/local/bin/push_remote_logs.py >> /var/log/brutesight_agent.log 2>&1
Updating the Agent
The BruteSight agent is completely self-aware of its version. Every time it pushes logs to the ingestion API, the server responds with a hash of the latest available script codebase via the X-Latest-Agent-Version HTTP header.
If your agent is outdated, you will see the following warning printed in your console or cron logs (/var/log/brutesight_agent.log):
WARNING: Script outdated! Update from docs.
How to Apply an Update
To update the agent, simply re-download the script over your existing file. Your configuration and state files will remain completely untouched.
curl -o /usr/local/bin/push_remote_logs.py https://api.brutesight.com/api/v1/update-script
chmod +x /usr/local/bin/push_remote_logs.py
The next time your cron job runs, the new version will seamlessly pick up log processing right from where the previous version left off.
Best Practices & Limits
1. Use HTTP ETag headers for Threat Feeds
All /api/v1/list/* endpoints support If-None-Match. Send back the ETag value you received on your previous poll; if the underlying list hasn't changed, you'll get a 304 Not Modified with no body, saving bandwidth and CPU on both ends.
2. Data Freshness
Threat feed data is aggregated daily and refreshed every 30 minutes, with an additional 10-minute response cache. A brand-new attack may take up to ~40 minutes to appear in a downloaded list — consistent with the recommended poll interval below.
3. Rate Limits
/ingest: 1000 requests per minute, per IP (higher than other endpoints to accommodate large backlogs after an agent outage)./list/*: 100 requests per minute, per IP (Recommended poll interval: 15-60 minutes).
4. Automated Agent Updates
If you deploy the agent across a fleet of servers using Ansible, Puppet, or Chef, you can read the X-Agent-Update-Required header directly from the /api/v1/ingest response to trigger your orchestration tool to automatically fetch the latest version.