Skip to content

Error Codes

All errors follow the envelope format with a SCREAMING_SNAKE_CASE code field.

Retry guidance

Every error also says, in machine-readable form, whether to retry, when, and what to change instead. The fields sit next to code and message on every error from API v1, API v2, check-windows, the CVE endpoints and authentication:

Field Type Meaning
retryable boolean true only when repeating the exact same request later may succeed. Never retry when false.
retry_after_seconds integer or null Seconds to wait before retrying, when the server knows (rate limits, server errors). The same value is sent as the standard Retry-After header.
action string What to do: retry_later, wait_until_reset, reduce_batch, fix_input, use_different_key or contact_us
reset_at string Only on usage limits that reset: when they do (ISO 8601, UTC). Also in detail as resets_at (partner keys) or reset_at
max integer Only on batch limits: the per-call maximum. Also in detail.max

The existing code, message and detail are unchanged.

{
  "api": { "version": "1.0.0", "request_id": "bs-req-...", "timestamp": "2026-10-01T15:00:00Z" },
  "error": {
    "code": "BATCH_TOO_LARGE",
    "message": "At most 25 hosts per call. Split the hosts into batches of 25 or fewer and send one call per batch.",
    "retryable": false,
    "retry_after_seconds": null,
    "action": "reduce_batch",
    "max": 25,
    "detail": { "code": "BATCH_TOO_LARGE", "error": "batch_too_large", "message": "...", "max": 25, "received": 30 }
  }
}

Classification

HTTP error.code (detail.error) retryable retry_after_seconds action
429 RATE_LIMITED (rate_limited): per-key limit, v1 and v2 true seconds until the limit window ends retry_later
429 RATE_LIMITED: edge limit for /api/ (nginx) true 10 retry_later
429 PARTNER_LIMIT: a partner key's monthly asset checks are used up false null wait_until_reset (reset_at)
429 TRIAL_ENDED (detail.reason = limit): the trial's asset checks are used up; they do not reset false null contact_us
429 RATE_LIMITED (query_limit): AI assistant monthly or daily limit false null wait_until_reset (reset_at)
429 RATE_LIMITED (free_query_limit): AI assistant without an account false null use_different_key
403 TRIAL_ENDED (expired or ended) false null contact_us
409 TRIAL_ENDED, TRIAL_UNAVAILABLE (starting a trial) false null contact_us
413 BATCH_TOO_LARGE (batch_too_large), TRIAL_BATCH_LIMIT false null reduce_batch (max)
400 UNKNOWN_PARAMETER, other bad input false null fix_input
422 VALIDATION_ERROR; refused hosts in data.rejected (identifying fields and other per-host codes) false null fix_input
404, 405, 410 NOT_FOUND, METHOD_NOT_ALLOWED, gone false null fix_input
409 conflicts such as "already watching" false null fix_input
403 FORBIDDEN (confirmation_required) false null fix_input
401 AUTH_REQUIRED: missing, invalid, expired or revoked key, or an expired session false null use_different_key
403 PARTNER_SCOPE, TRIAL_SCOPE: the key cannot call this endpoint false null contact_us
403 TRIAL_REQUIRED, API_ONLY_ACCOUNT, API_KEY_REQUIRED, DEMO_EXAMPLES_ONLY false null use_different_key
403 FORBIDDEN (insufficient_scope, viewer_role, demo_read_only including permission: demo_read_only, session_required, other) false null use_different_key
402, 403 TIER_GATE, FORBIDDEN (upgrade_required, tier_required, cap_exceeded, limit_reached) false null contact_us
500 INTERNAL_ERROR, including database timeouts true 5 retry_later
502 upstream failure (ERROR from the API, SERVICE_UNAVAILABLE from the edge) true 10 retry_later
503 ERROR (database unavailable), SERVICE_UNAVAILABLE from the edge true 30 retry_later
503 ERROR (demo_unavailable): the public demo account is switched off false null contact_us
504 GATEWAY_TIMEOUT from the edge true 30 retry_later

Each host refused in data.rejected (API v2 and check-windows) carries retryable: false, retry_after_seconds: null and action: fix_input.

Cloudflare's own pages (for example its 524 timeout or its own 429) are outside BreachSpider and carry no guidance. Treat them by status: 429 and 5xx are retryable, everything else is not. The official Python SDK does this automatically.

Code HTTP Status Meaning
UNKNOWN_PARAMETER 400 A query parameter the endpoint does not accept; detail lists the accepted parameters
AUTH_REQUIRED 401 Valid session cookie or API key required. The message says Invalid API key, API key expired or API key revoked
FORBIDDEN 403 Authenticated but not authorized, including tier upgrades required (detail carries error: upgrade_required)
viewer_role 403 Read-only (viewer) member attempted a write operation
TIER_GATE 402 Returned by a small number of endpoints (audit log export, AI assistant chat). Most tier gating returns 403 FORBIDDEN
NOT_FOUND 404 Resource does not exist
METHOD_NOT_ALLOWED 405 HTTP method not supported on this endpoint
BATCH_TOO_LARGE 413 Batch too large: more than 200 assets in one correlate-cves call, or more than 25 hosts in one Windows call (detail.max, detail.received; detail.error = batch_too_large)
VALIDATION_ERROR 422 Request parameters failed validation, e.g. sort not one of priority, score, exploit, newest
RATE_LIMITED 429 Too many requests: wait retry_after_seconds (or the Retry-After header) and retry
PARTNER_SCOPE 403 A partner key called an endpoint outside its scope. Read-only partner keys can call correlate-cves, correlate-cves/check, check-windows and the CVE endpoints
PARTNER_LIMIT 429 A partner key's monthly asset-check limit would be passed (detail.used, limit, remaining, requested, resets_at). Nothing was processed
INTERNAL_ERROR 500 Server error - our team has been notified

Free API Trial Errors

Returned only for keys on the free API trial. Each carries detail.contact_url, the scoping email.

Code HTTP Status Meaning
TRIAL_ENDED 403 The trial is over: detail.reason is expired (14 days passed) or ended (ended early). The key is genuine but no longer accepted
TRIAL_ENDED 429 The trial's 750 asset checks are used up, or this request needs more than remain (detail.reason = limit, with used, limit, remaining and requested). Nothing in the request was processed
TRIAL_SCOPE 403 Trial keys can call POST /assets/correlate-cves, POST /assets/correlate-cves/check and GET /cves/{cve_id} only
TRIAL_BATCH_LIMIT 413 More than 25 assets in one request (detail.max, detail.received)
TRIAL_REQUIRED 403 A free account called correlate-cves or its check. Checking your own devices needs a free trial (detail.trial_url)

API v2 (Windows Patch Level) Errors

API v2 uses the same envelope. error.detail.error carries the specific code: insufficient_scope (403), environment_not_found (404), batch_too_large (413), rate_limited (429, with retry_after). Hosts refused by validation are listed per host in data.rejected with codes such as forbidden_field, placeholder_value and missing_field. See Windows Patch Level.

Tier Upgrade Response

When a feature is gated by tier, most endpoints return HTTP 403 with code FORBIDDEN and an upgrade_required detail block. For example, generating an API key on Free or Standard returns:

{
  "api": { "version": "1.0.0", "request_id": "bs-req-..." },
  "error": {
    "code": "FORBIDDEN",
    "message": "API access is available on the Professional tier and above.",
    "retryable": false,
    "retry_after_seconds": null,
    "action": "contact_us",
    "detail": {
      "error": "upgrade_required",
      "permission": "api_access",
      "current_tier": "standard",
      "upgrade_url": "/account/upgrade",
      "message": "API access is available on the Professional tier and above."
    }
  }
}

A small number of endpoints (audit log export, AI assistant chat) instead return HTTP 402 with code TIER_GATE. Handle both 402 and 403 as "upgrade required."

VALIDATION_ERROR Response

Validation errors include per-field detail:

{
  "api": { "version": "1.0.0", "request_id": "bs-req-..." },
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed.",
    "retryable": false,
    "retry_after_seconds": null,
    "action": "fix_input",
    "detail": [
      {
        "field": "query.severity",
        "message": "String should match pattern '^(CRITICAL|HIGH|MEDIUM|LOW)$'",
        "type": "string_pattern_mismatch"
      }
    ]
  }
}

Rate Limiting

When rate limited, wait error.retry_after_seconds (also the Retry-After header) and retry. The per-key limiter also keeps it in error.detail.retry_after. Without either, back off exponentially. A 429 with retryable: false (PARTNER_LIMIT, a used-up trial) will not clear by waiting seconds: see action.

{
  "api": { "version": "1.0.0", "request_id": "bs-req-..." },
  "error": {
    "code": "RATE_LIMITED",
    "message": "Per-key limit is 60 requests and 5000 hosts per minute.",
    "retryable": true,
    "retry_after_seconds": 36,
    "action": "retry_later",
    "detail": { "error": "rate_limited", "message": "Per-key limit is 60 requests and 5000 hosts per minute.", "retry_after": 36 }
  }
}
# Check your current rate limit status
curl -I -H "Authorization: Bearer bs_live_..." \
  "https://breachspider.com/api/v1/health"