Developers

Get your API key

Anonymous scans are rate-limited at 10 per day per IP. An API key raises that to 100 scans per day per key — enough to monitor your own fleet of staging + prod domains from CI without hitting the wall.

Authenticate to get started

No account, no email — just click the button. You'll get a token that looks like ak_XXXXXXXXXX…. Present it as Authorization: Bearer <token> on every POST /api/v1/scans call.

Quotas

  • Anonymous (no key): 10 scans / day / IP.
  • Keyed (this tier): 100 scans / day / key.
  • Authenticated (signed-in): 500 scans / day / key.

Key mints are themselves rate-limited at 5 per hour per IP — enough to recover from a lost token, tight enough that hoarding across the anonymous quota isn't practical.

Libraries + SDKs

Rather than hitting the REST API directly, grab an official client library. More languages follow.

Python

pip install agentdisco
from agentdisco import AgentDisco

with AgentDisco(token="ak_…") as client:
    scan = client.submit_scan("https://your-site.example")
    print(scan.id, scan.status)

Webhooks

Get a signed JSON POST to a URL of your choice every time a scan of a host you registered completes. Useful for CI integrations, status-page updates, or pushing grade changes into your own dashboards.

Setup

Sign in, open your account, scroll to Scan webhooks, fill in the host you want to subscribe to + the receiver URL, and submit. We mint a 32-byte HMAC signing secret and show it to you exactly once — copy it into your receiver's secret store.

Payload shape

On every completed scan we POST a JSON body that looks like this:

POST https://your-receiver.example/agent-disco
Content-Type: application/json
X-Agent-Disco-Event: scan.completed
X-Agent-Disco-Webhook-Id: 019d...
X-Agent-Disco-Signature: sha256=...

{
    "event": "scan.completed",
    "scan": {
        "id": "019d...",
        "host": "your-site.example",
        "grade": "B",
        "score": 72,
        "completedAt": "2026-04-25T10:00:00+00:00",
        "statusUrl": "/api/v1/scans/019d...",
        "resultUrl": "/report/your-site.example"
    }
}

Verifying signatures

Every payload carries an X-Agent-Disco-Signature header of the form sha256=<hex>. Recompute HMAC-SHA256(secret, raw_body) and compare in constant time:

# Python
import hmac, hashlib

def verify(body: bytes, signature: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode(), body, hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

Retries + auto-pause

We expect a 2xx response within 10 seconds. Anything else (4xx, 5xx, timeout, DNS failure) is a delivery failure that retries 3× with exponential backoff. After 5 consecutive terminal failures we auto-pause the webhook — the row stays in your account but no further deliveries fire. Resume by deleting + re-creating it, or by ensuring the next attempt succeeds (the counter resets on any 2xx).

Replay protection

The scan.id in every payload is a UUIDv7 — its embedded timestamp lets your receiver reject replays older than a window you choose (we recommend 5 minutes). The signature alone doesn't include a nonce; if you need stricter replay protection, discard payloads whose scan.completedAt is older than your tolerance.