PrimaryCheck

Rate limits

Two token buckets apply to every authenticated request: one per credential (requests per second) and one per account (requests per minute). Either bucket being empty refuses the request with 429; the refused request is not counted against the other bucket.

ScopeDefaultNotes
Per API key10 / secondBurst of 10, refilled continuously. Raised per key on request (api_keys.rateLimit.rps).
Per dashboard session30 / secondDashboard pages fan out several requests at once.
Per account600 / minuteAcross every key and session on the account. Raised for high-volume integrations.
Claims per verification50Hard limit, 400 validation_error above it.
Consumer portal20 / hour / IPPublic endpoints.

Headers

Every authenticated response, including a 429, carries the account bucket's state. A 429 adds Retry-After in whole seconds.

X-RateLimit-Limit: 600
X-RateLimit-Remaining: 597
Retry-After: 2                      (on 429 only)

The 429 body

{ "error": { "type": "rate_limit_error", "code": "rate_limited",
             "message": "Rate limit exceeded (10 req/s per credential, 600 req/min per account).",
             "requestId": "req_…", "docUrl": "https://primarycheck.com/docs/errors#rate_limited" } }

What to do

  • Wait Retry-After seconds and retry the same request with the same Idempotency-Key. The SDKs do this twice by default before raising RateLimitError.
  • Use ?wait=25 and webhooks instead of polling in a loop; one long-poll costs one request.
  • Batch work per key: several keys on one account share the per-minute account bucket, so more keys do not mean more throughput.
  • Need more? Ask from the dashboard; limits are raised per key and per account, not by plan.

Source politeness

Behind the API, every primary source has its own concurrency and rate budget so a burst from one customer never gets a state board blocked for everyone. Bursts are queued, not refused; use ?wait= or webhooks rather than tight polling.