Skip to content

Errors & rate limits

Errors are JSON with a human-readable error and, where useful, a stable machine code:

{ "error": "this key is missing the 'analytics' scope", "code": "insufficient_scope" }
Status Meaning
400 Validation failed — the error says which field and why
401 Missing or unknown API key
402 Plan limit reached (quota or feature gate) — the console’s Billing page resolves it
403 Key lacks a required scope (insufficient_scope)
404 Resource not found (or not yours — we don’t distinguish)
429 Rate limit exceeded — see below
5xx Our fault; safe to retry with backoff

Requests are counted per minute against your plan’s allowance and enforced with real 429 semantics:

HTTP/1.1 429 Too Many Requests
retry-after: 23
x-ratelimit-limit: 120
{ "error": "rate limit exceeded (120 requests/min on your plan)", "code": "rate_limited" }

Honour retry-after and you’ll never see two in a row. Windows are aligned to the minute, so a burst that trips the limit clears quickly.

  • Webhook latency: seconds, not sub-second (queue-decoupled from the redirect hot path).
  • Delivery: at-least-once; de-dupe on webhook-id (details).
  • Raw analytics retention: 90 days; aggregates kept indefinitely.
  • Bulk create: 500 rows per request; monthly caps by plan.
  • Custom stubs: ≤64 characters. Tags: ≤25 per link.
  • Region: the platform runs in AWS eu-west-2 (London) — one region, by design, with the trust story documented at xengo.io/trust.