API Conventions

Response envelopes, pagination, problem details, rate limits, and tenant scope.

Authentication

Send the API key in the Authorization header. Both of these are accepted:

Authorization: Bearer $EFFLUX_TOKEN
Authorization: $EFFLUX_TOKEN

WebSocket streams use a different header. See Streams.

Pass tenant_view=true on list and get routes when the key can read the tenant. The response then includes resources for the tenant, not only the key owner.

Responses

A single resource is a DocumentResponse:

{
  "data": {},
  "links": {
    "self": "https://api.efflux.io/v3/scans/scan_abc123"
  }
}

links.self is always present. Extra links (results, summary, screenshot variants, and so on) appear when that resource has them.

A list is a PagedResponse:

{
  "data": [],
  "pagination": {
    "limit": 20,
    "page": 1,
    "total": 150,
    "total_pages": 8,
    "has_next": true,
    "has_prev": false
  }
}

Pass page (starting at 1) and limit. Read pagination for the page size, the totals, and whether another page exists. A limit above the maximum is capped. It is not an error.

EndpointsDefaultMax
Scans, tasks, surveys, schedules201000
Schedule history1001000
Permutations, checks, captures, usage1001000
Capture network logs and cookies5001000
Lists, rules, webhooks, callback deliveries, monitoring20200
Certificates50500
Host search50200
Your host search20100
API keys1050
Advanced search20100

Advanced search does not use page. It returns a SearchPagedResponse with next_token. Send that value back as search_after. See Search.

Scan results, the scan summary, and callback reads return the resource directly, without a data wrapper.

Errors

Errors use Content-Type: application/problem+json (RFC 7807).

{
  "type": "https://api.efflux.io/v3/docs/index.html#/Scans",
  "title": "Bad Request",
  "status": 400,
  "detail": "target is required",
  "instance": "/v3/scans",
  "errors": [
    { "field": "hosts", "message": "required" }
  ]
}

type is a URI for the error. Validation failures include errors with field and message. Quota failures use a type under /v3/limits.

Rate limits

Every response includes:

HeaderMeaning
RateLimit-LimitRequests allowed in the current window
RateLimit-RemainingRequests left in that window
RateLimit-ResetUnix time when the window resets

X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset repeat those three values. A 429 also sends Retry-After, the seconds to wait.

The 429 body adds rate_limit:

{
  "type": "https://api.efflux.io/v3/limits",
  "title": "Rate Limit Exceeded",
  "status": 429,
  "detail": "rate limit exceeded",
  "rate_limit": {
    "limit": 60,
    "remaining": 0,
    "reset_at": "2026-03-13T12:00:00Z",
    "retry_after_seconds": 42
  }
}
FieldMeaning
limitRequests allowed in the window that was exceeded
remainingRequests left in that window
reset_atWhen the window resets
retry_after_secondsSeconds to wait before retrying

Plan quotas (pending scans, monthly request budget, list sizes) are separate and are returned by GET /limits.