Codes
The codes the public API returns, and what to do about each one.
| Status | code | When | What to do |
|---|---|---|---|
| 400 | VALIDATION_ERROR | window > 90 days, from > to, unreadable cursor | fix the parameter |
| 401 | INVALID_API_KEY | missing header, malformed, or unknown key | verify the Authorization header |
| 401 | API_KEY_REVOKED | the key was revoked | generate another |
| 401 | API_KEY_EXPIRED | the key is past its expiry | generate another |
| 403 | API_KEY_SCOPE_DENIED | the key lacks the endpoint scope; today every key is created with tracking:read | should not happen in this version; contact support |
| 403 | API_QUOTA_EXCEEDED | the account reached its usage quota for the period | contact support; waiting does not help |
| 404 | — | no such endpoint | verify the path and the /v1 prefix |
| 429 | — | per-key limit exceeded | wait for Retry-After |
| 5xx | — | internal error in the PassKey API | retry with growing backoff; if it persists, contact support |
401 × 403
The difference is operational, not cosmetic: 401 asks for a different credential, 403 asks for a different permission. Retrying a 403 with the same key will never work.
When to retry
- 400, 401, 403, 404 — no. The same call fails again; fix the request.
- 429 — yes, after the time given in Retry-After.
- 5xx — yes, with growing backoff (1s, 2s, 4s…) and a retry ceiling.