Тема
Войти Регистрация

HTTP API (user & admin)

The agent-swarm API is documented separately in [AGENT_API.md](AGENT_API.md). This page covers the platform API used by the dashboard, integrations and scripts.

  • Base URL: https://abuseipdb.ru/api/v1/
  • Specification: GET /api/v1/openapi.json (OpenAPI 3.1, generated from code)
  • Authentication: Authorization: Bearer <api-token>
  • Envelope: { "ok": true, "data": …, "meta": … } / { "ok": false, "error": { … } }
  • Versioning: the major version lives in the path (/api/v1/…). Backwards

compatibility is guaranteed within a major version; breaking changes require a new major and a deprecation window announced in the changelog.

Tokens

Create tokens in Dashboard → API tokens. A token is shown once; only its SHA-256 hash is stored, together with a human-readable prefix (iya_u_9f3a…) so you can recognise it in a list.

Each token carries:

  • scopes — links:read, links:write, stats:read, subdomain:write,

team:read, admin (the last one only for staff roles);

  • an optional IP allowlist (CIDR);
  • an optional expiry;
  • last_used_at / last_used_ip for auditing.

A token outside its scopes or allowlist answers 403 forbidden. Revoking is immediate.

Endpoints

Discovery & health

MethodPathAuthPurpose
GET/api/v1/–API index
GET/api/v1/health–liveness + dependency probe
GET/api/v1/status–version, features, limits, counters
GET/api/v1/openapi.json–OpenAPI 3.1 document
GET/api/v1/errors–error catalogue
GET/api/v1/languages–supported locales
GET/metrics–Prometheus-style metrics

Public read (no auth, owner-controlled)

MethodPathNotes
GET/api/v1/subdomains/{name}public page payload
GET/api/v1/links?subdomain=…public links
GET/api/v1/stats?subdomain=…aggregates, only if the owner enabled them
GET/api/v1/visits?subdomain=…recent visits, only if the list is public

Each of these respects the subdomain's privacy policy: stats_public, stats_show_visits_list, stats_show_ip, stats_show_internal_ip, stats_show_geo, stats_show_ua, stats_show_referrer, stats_show_links, stats_show_owner, stats_publish_delay_sec and stats_max_items. Fields the owner hid are removed server-side, not merely hidden in the UI.

Authenticated

MethodPathScopePurpose
GET/POST/api/v1/linkslinks:read / links:writelist and create links
GET/PATCH/DELETE/api/v1/links/{id}links:*read, update, soft-delete
POST/api/v1/links/{id}/duplicatelinks:writeclone
GET/api/v1/links/{id}/qrlinks:readQR code (SVG/PNG)
GET/api/v1/stats/{scope}/{id}stats:readreport data (subdomain, link, user)
GET/api/v1/visitsstats:readfiltered visit list with cursor pagination
GET`/api/v1/export?format=csv\json\xlsx\pdf`stats:readexport
UI/account/webhookssession (CSRF)manage webhooks (create/test/toggle/rotate-secret/delete; no JSON CRUD — P20/R1 doc-fix)
GET/POST/api/v1/tokens–manage API tokens

Tracker (browser beacon)

MethodPathPurpose
GET/POST/tracker/collectconsent-gated telemetry enrichment
POST/tracker/consentrecord a consent decision

The beacon only ever adds client-side signals (time zone, screen, network, capability probes, and — with explicit consent — the LAN IP discovered via WebRTC). It can never change the server-determined ip_public.

Legacy compatibility

The original abuseipdb.ru v0 API is preserved so existing integrations keep working:

GET /api/rest.php/ip            → current visitor
GET /api/rest.php/ip/{ip}       → visitor by IP
GET /api/rest.php/visits?limit= → recent visits
GET /api/rest.php/bots          → recent bots
GET /api/rest.php/hacks         → recent attack attempts
GET /api/rest.php/stats         → counters

The apex also serves two public read-only endpoints inherited from the original service — both behind the same privacy contract (public-feed filter, no-oracle 404s, explicit allow-list projections, ADR-022/048):

GET /ip/{ip}     → visitor information page (HTML; ?format=json twin)
GET /visit/{id}  → JSON projection of one public-feed visit (ADR-048)

New code should use /api/v1/…; the v0 shape is frozen and will only receive security fixes.

Pagination

Cursor-based for large collections:

{ "data": { "visits": [ … ] },
  "meta": { "count": 50, "total": 183442, "limit": 50,
            "cursor": "…", "next_cursor": "…", "has_more": true } }

Pass ?cursor=<next_cursor> for the following page. Offset pagination (?page=) is available for human-facing tables only.

Webhooks

Configure them in Dashboard → Account → Event webhooks (/account/webhooks; UI-managed — there is no JSON CRUD API for hooks). Events: visit.new, visit.unique, link.limit_reached, security.suspicious, link.created, link.target_down, agent.task, plus the platform-owner fan-out pair contrib.promoted / contrib.rolled_back (ADR-028: emitted to the first user account only — a regular user's hook subscribed to them never fires). The Test button delivers a webhook.test event outside the subscription.

Machine-readable catalogue (payload schemas + examples, signing reference, delivery/retry policy, connector hints): GET /api/v1/webhooks/catalog (frozen append-only registry, P21/T4 — ADR-065). Canonical human reference: docs/WEBHOOKS.md. Agent-scoped signed push (contrib events): see POST /api/v1/agents/push and WEBHOOKS.md §3.

  • Payload: the same envelope, plus event and delivered_at.
  • Delivery: exponential backoff, 5 attempts, then the webhook is disabled and

the owner notified. Every attempt is logged (webhook_deliveries, incl. the signed flag).

  • Outbound requests go through the SSRF guard: private ranges, cloud metadata

endpoints and dangerous ports are refused.

Delivery headers (P20/R1 signing contract)

HeaderValue
X-Iya-Signaturesha256=<hex HMAC-SHA256("<X-Iya-Timestamp>.<raw body>", secret)>
X-Iya-Timestampunix seconds when the delivery was signed
X-Iya-Delivery-Idunique id of this delivery attempt chain (dlv-…)
X-Iya-Eventevent name (visit.new, webhook.test, …)
X-Iya-Signature-Prevpresent only during the 24 h rotation grace: the same signature under the previous secret

Signing can be disabled platform-wide (WEBHOOK_SIGNING_ENABLED=false) — then no signature/timestamp headers are sent.

Verifying signatures

Recompute the HMAC over "<timestamp>.<raw body>" (the RAW request body, not the parsed form), compare in constant time, and reject deliveries whose X-Iya-Timestamp deviates from your clock by more than WEBHOOK_SIGNATURE_TTL (default 300 s) — that bounds replay attacks. During the grace window accept a delivery when EITHER X-Iya-Signature (new secret) or X-Iya-Signature-Prev (old secret) verifies.

// PHP
$ts   = $_SERVER['HTTP_X_IYA_TIMESTAMP'] ?? '';
$sig  = $_SERVER['HTTP_X_IYA_SIGNATURE'] ?? '';        // "sha256=<hex>"
$body = file_get_contents('php://input');               // raw body
$want = 'sha256=' . hash_hmac('sha256', $ts . '.' . $body, $secret);
$ok   = hash_equals($want, $sig) && abs(time() - (int) $ts) <= 300;
# Python
import hashlib, hmac, time
want = "sha256=" + hmac.new(secret.encode(),
           f"{ts}.{body}".encode(), hashlib.sha256).hexdigest()
ok = hmac.compare_digest(want, sig) and abs(time.time() - int(ts)) <= 300

Secrets and rotation

  • The signing secret (whsec_…) is generated at creation, sealed at rest

(AES-256-GCM, WEBHOOK_SECRET_ENC_KEY/APP_KEY-derived key) and shown to you exactly once — right after create/rotate. It is never re-readable.

  • Lost the secret? Rotate (POST /account/webhooks/{id}/rotate-secret,

CSRF-protected, audited). The old secret keeps verifying for 24 h (X-Iya-Signature-Prev rides along); after the grace it is destroyed.

  • Pre-R1 hooks (plaintext-era rows) keep working until you rotate them.

Errors and debugging

See [ERRORS.md](ERRORS.md). Every response carries X-Request-Id; the same id appears in meta.request_id and in the server access log, which makes a single request traceable end to end.

Версия 2.2.0 · AbuseIPDB.RU · webmaster@abuseipdb.ru · source: docs/API.md