Developer Documentation

BadBot Gateway API

Use BadBot Gateway to look up IP and domain reputation, submit abuse reports, consume high-risk feeds, and correlate suspicious indicators.

Base URL
https://gateway.badbot.net

Quickstart

Start with a health check, create or generate an API key, submit a report, then query reputation for the same indicator.

1. Health check

curl https://gateway.badbot.net/api/v1/health
# → {"status":"ok","time":"2026-05-12T10:00:00+00:00"}

2. Get an API key

Sign in and generate a personal key under My API Keys. Copy it immediately; it is shown only once.

export BADBOT_API_KEY="paste-your-key-here"

3. Submit an abuse report

curl -X POST https://gateway.badbot.net/api/v1/report \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: $BADBOT_API_KEY" \
  -d '{
    "indicator": "198.51.100.23",
    "category_id": 1,
    "severity": 6,
    "comment": "Credential stuffing"
  }'
# → {"status":"reported","id":42}

4. Query reputation

curl https://gateway.badbot.net/api/v1/reputation/198.51.100.23

Authentication

Write endpoints require the X-Api-Key request header.

Authenticated users can generate and revoke personal API keys from /api-keys. User API keys authorize report submission and feed access just like other API keys.

Admin-token endpoints use X-Admin-Token and are intended for operators, not normal client integrations.

Read-only endpoints such as health, reputation, search, suggestions, feeds, and correlation can be used without an API key unless deployment policy changes.

Endpoint Overview

Method Path Auth Description
GET/api/v1/reputation/{indicator}Look up score, reports, categories, and last report time.
GET/api/v1/searchSearch indicators with pagination and country filter.
GET/api/v1/suggestAutocomplete prefix search for indicators.
POST/api/v1/reportAPI keySubmit one abuse report.
POST/api/v1/report/bulkAPI keySubmit many reports in one request.
GET/api/v1/feeds/high-riskFetch high-risk indicators as JSON, CSV, or TXT.
GET/api/v1/correlation/{indicator}Find related indicators through ASN and reporter overlap.
POST /api/v1/report API key required

Submit Report

Submit a single abuse report for an IP address or domain.

Request body

FieldTypeRequiredDescription
indicatorstringyesIP address or domain name (max 253 chars).
category_idintegeryesAbuse category ID. See categories.
severityintegerno1–10 (default: 1). Higher values increase the reputation score.
commentstringnoFree-text note (max 500 chars).

Example request

curl -X POST https://gateway.badbot.net/api/v1/report \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: $BADBOT_API_KEY" \
  -d '{
    "indicator": "198.51.100.23",
    "category_id": 1,
    "severity": 6,
    "comment": "Credential stuffing"
  }'

Response 201

{
  "status": "reported",
  "id": 42
}
POST /api/v1/report/bulk API key required

Bulk Report

Submit up to 1 000 reports in one request. The request body can be a JSON array or an object with a reports key. Each item uses the same fields as a single report. Invalid rows are collected in the errors array without blocking valid ones.

Example request

curl -X POST https://gateway.badbot.net/api/v1/report/bulk \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: $BADBOT_API_KEY" \
  -d '[
    {"indicator":"198.51.100.23","category_id":1,"severity":6},
    {"indicator":"evil.example.com","category_id":9,"severity":8}
  ]'

Response 201

{
  "created": 2,
  "errors": [],
  "total": 2
}

Partial failure

{
  "created": 1,
  "errors": [
    {"index": 1, "error": "Category 99 does not exist"}
  ],
  "total": 2
}
GET /api/v1/reputation/{indicator}

Reputation Lookup

Returns the reputation score, report count, category breakdown, and last report timestamp for an IP address or domain. Scores decay over time using a configurable half-life.

Example request

curl https://gateway.badbot.net/api/v1/reputation/198.51.100.23

Response 200

{
  "indicator": "198.51.100.23",
  "found": true,
  "score": 42.5,
  "total_reports": 3,
  "categories": {
    "Credential Stuffing": 3
  },
  "last_reported_at": "2026-01-01T12:00:00+00:00"
}

When the indicator is unknown, found is false and score is 0.

GET /api/v1/suggest

Autocomplete Suggestions

Lightweight prefix search returning indicator values and types. Useful for search-as-you-type UIs. When q is omitted, returns the most recently reported indicators.

Query parameters

ParamTypeDefaultDescription
qstringPrefix to match (optional, max 100 chars).
limitinteger10Max results (1–50).

Example request

curl "https://gateway.badbot.net/api/v1/suggest?q=198.51&limit=3"

Response 200

[
  {"value": "198.51.100.23", "type": "ip"},
  {"value": "198.51.100.44", "type": "ip"},
  {"value": "198.51.200.1", "type": "ip"}
]
GET /api/v1/correlation/{indicator}

Correlation

Find IP indicators related through shared ASN, reporter overlap, passive DNS, and activity patterns. Currently supports IP addresses only.

Example request

curl https://gateway.badbot.net/api/v1/correlation/198.51.100.23

Response 200 (abbreviated)

{
  "seed": {
    "indicator": "198.51.100.23",
    "type": "ip",
    "found": true,
    "score": 42.5,
    "total_reports": 3,
    "meta": {
      "asn": 64496,
      "organization": "Example ISP",
      "country": "US"
    }
  },
  "matches": [...],
  "infrastructure": {
    "meta": {...},
    "domains": [...]
  },
  "spread_summary": {...},
  "activity_timeline": [...]
}

The response includes correlated matches, passive DNS infrastructure, activity timelines, and regional spread data.

Feeds

Feeds are available through stable web aliases and the API route. Use TXT for simple blocklists, CSV for imports, and JSON for structured automation.

Stable web aliases

/feeds/high-riskJSON feed (default limit: 1 000)
/feeds/high-risk.csvCSV — columns: indicator, score, reports, country_iso, asn
/feeds/high-risk.txtPlain text — one indicator per line
/feeds/high-risk-full-ipv4.txtFull IPv4 snapshot (all qualifying IPs, no limit cap)
/feeds/high-risk-full-ipv6.txtFull IPv6 snapshot (all qualifying IPs, no limit cap)

API route query parameters

ParamTypeDefaultDescription
min_scorefloat5Minimum reputation score to include.
limitinteger1 000Max results (1–10 000).
typestringipv4, ipv6, or domain.
formatstringjsonjson, csv, or txt.

Example request

curl "https://gateway.badbot.net/api/v1/feeds/high-risk?type=ipv4&min_score=10&limit=100"

JSON response item

{
  "indicator": "198.51.100.23",
  "score": 42.5,
  "reports": 3,
  "country_iso": "US",
  "asn": 64496
}
Truncation: When results exceed the limit, the response includes X-Truncated: true and X-Truncated-Limit response headers. The full snapshot feeds (-full-ipv4.txt, -full-ipv6.txt) are not subject to the limit cap.

Abuse Categories

Every report requires a category_id. The default severity is used when you omit the severity field.

ID Category Default Severity
1Credential Stuffing3
2SMTP Abuse4
3Web App Attack6
4DDoS8
5Spam5
6Botnet Drone7
7Malware9
8Scanning2
9Phishing8
10Fraud7
11Web Probe3
12Config Exposure Probe6
13Web Stack Probe5
14Admin Panel Probe5
15API Probe5

Rate Limits

All endpoints are rate-limited per client IP. When a limit is exceeded the API returns 429 Too Many Requests with a Retry-After header.

Endpoint Limit
GET /api/v1/health30 / min
GET /api/v1/reputation/{indicator}120 / min
GET /api/v1/search60 / min
GET /api/v1/suggest120 / min
POST /api/v1/report30 / min
POST /api/v1/report/bulk10 / min
GET /api/v1/feeds/high-risk10 / min
GET /api/v1/correlation/{indicator}30 / min

Errors

Error responses use a consistent JSON shape. The HTTP status code is mirrored in the body.

Error response format

{
  "error": "Bad Request",
  "message": "Category 99 does not exist",
  "status": 400
}

Status codes

  • 200 success (reads).
  • 201 success (writes).
  • 400 invalid input or payload shape.
  • 401 missing API key.
  • 403 invalid API key or token.
  • 404 resource not found.
  • 409 duplicate or conflicting resource.
  • 429 rate limit exceeded — see Retry-After header.

Troubleshooting

  • 403 Invalid API key: regenerate the key if it was copied wrong or the API key salt changed.
  • 400 Category N does not exist: use one of the category IDs listed above.
  • Slow first report: enrichment may be performing RDAP, DNSBL, GeoIP, or WHOIS lookups in the background.
  • Feed format errors: use format=json|csv|txt on the API route or the stable web aliases under Feeds.

Code Examples

Python requests

import os
import requests

base_url = "https://gateway.badbot.net"
api_key = os.environ["BADBOT_API_KEY"]

payload = {
    "indicator": "198.51.100.23",
    "category_id": 1,
    "severity": 6,
    "comment": "Credential stuffing",
}

response = requests.post(
    f"{base_url}/api/v1/report",
    json=payload,
    headers={"X-Api-Key": api_key},
    timeout=10,
)
response.raise_for_status()

reputation = requests.get(
    f"{base_url}/api/v1/reputation/{payload['indicator']}",
    timeout=10,
).json()
print(reputation)

JavaScript fetch

const baseUrl = "https://gateway.badbot.net";
const apiKey = process.env.BADBOT_API_KEY;

const report = {
  indicator: "198.51.100.23",
  category_id: 1,
  severity: 6,
  comment: "Credential stuffing",
};

const response = await fetch(`${baseUrl}/api/v1/report`, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Api-Key": apiKey,
  },
  body: JSON.stringify(report),
});

if (!response.ok) {
  throw new Error(`BadBot API error ${response.status}`);
}

PHP curl

<?php
$baseUrl = 'https://gateway.badbot.net';
$apiKey = getenv('BADBOT_API_KEY');
$payload = json_encode([
    'indicator' => '198.51.100.23',
    'category_id' => 1,
    'severity' => 6,
    'comment' => 'Credential stuffing',
]);

$ch = curl_init($baseUrl . '/api/v1/report');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
        'X-Api-Key: ' . $apiKey,
    ],
    CURLOPT_POSTFIELDS => $payload,
    CURLOPT_RETURNTRANSFER => true,
]);

$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

if ($status >= 400) {
    throw new RuntimeException('BadBot API error ' . $status . ': ' . $body);
}

Go net/http

package main

import (
	"bytes"
	"encoding/json"
	"net/http"
	"os"
)

func main() {
	payload, _ := json.Marshal(map[string]any{
		"indicator":   "198.51.100.23",
		"category_id": 1,
		"severity":    6,
		"comment":     "Credential stuffing",
	})

	req, _ := http.NewRequest("POST", "https://gateway.badbot.net/api/v1/report", bytes.NewReader(payload))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-Api-Key", os.Getenv("BADBOT_API_KEY"))

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		panic(resp.Status)
	}
}

Machine-Readable Docs

Use these endpoints for generated clients, API discovery, and agent-friendly context.

/openapi.json OpenAPI 3.0.3 service description
/.well-known/api-catalog API catalog linkset
/llms.txt LLM-friendly site and API index