Rate limits
VerifyAI caps how fast you can call the API so one customer's traffic spike can't degrade everyone else's verifications. Limits are generous for normal use — the goal is to absorb bursts and shed runaway loops, not to throttle real workloads. This page explains what the limits are, how the API signals when you've hit one, and how to back off cleanly.
What's limited
Two distinct things are metered, and both surface as a 429:
- Throughput — requests per second against
POST /api/v1/verify. This is the short-window rate limit that protects the service. - Monthly usage cap — the total verifications included in your plan.
Exhausting it also returns
429, but aRetry-Afterwon't help — you're out of quota until the cycle resets or you raise the cap.
The same status code covers both because the client behavior is similar
(stop hammering), but the remedies differ: a throughput 429 clears in
seconds; a usage-cap 429 clears at billing rollover.
Per-plan throughput
| Plan | Sustained throughput | | --------------- | ------------------------- | | Pay-as-you-go | ~100 requests/second | | Enterprise | Custom, negotiated higher |
Pay-as-you-go accounts get roughly 100 requests/second of sustained throughput, with short bursts above that tolerated. Enterprise contracts raise the ceiling to whatever your peak load requires — if you're planning a fleet-wide rollout that will spike end-of-ride verifications, talk to us about provisioning ahead of the launch rather than discovering the limit in production.
Throughput is metered per vai_… key. If you run separate keys for
staging and production, they have independent budgets — and a test
loop on a staging key won't eat into your production throughput.
The 429 response
When you exceed a limit, the API returns 429 Too Many Requests with a
Retry-After header telling you how many seconds to wait:
HTTP/1.1 429 Too Many Requests
Retry-After: 2
X-Request-Id: req_8x92m4k9Retry-After is the source of truth for throughput limits — prefer it
over guessing. Every response also carries an X-Request-Id; include it
if you contact support about unexpected throttling.
A 429 means the request was never processed — no verification was
created and no usage was charged. It is always safe to retry after the
indicated delay. Don't treat it like a 500 or surface it to the end
user as a verification failure.
Handling 429s
The correct response to a 429 is to wait and retry, not to abandon the
request:
- Honor
Retry-After. If the header is present, wait at least that long before retrying. - Fall back to exponential backoff when there's no header — start around 1s and double each attempt (1s, 2s, 4s, 8s), capping at a sane maximum.
- Add jitter. Randomize the delay slightly so a fleet of clients that all hit the limit at once doesn't retry in a synchronized thundering herd.
- Cap total retries. After a handful of attempts, surface the failure to your own monitoring rather than retrying forever.
async function verifyWithBackoff(form, key, maxRetries = 5) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const res = await fetch("https://verify.switchlabs.dev/api/v1/verify", {
method: "POST",
headers: { "X-API-Key": key },
body: form,
});
if (res.status !== 429) return res; // success or a non-rate-limit error
// Honor Retry-After when present, else exponential backoff + jitter.
const retryAfter = Number(res.headers.get("Retry-After"));
const base = Number.isFinite(retryAfter) && retryAfter > 0
? retryAfter * 1000
: Math.min(1000 * 2 ** attempt, 30000);
const jitter = Math.random() * 250;
await new Promise((r) => setTimeout(r, base + jitter));
}
throw new Error("verify: exhausted retries after repeated 429s");
}If you call VerifyAI through the React Native or Flutter SDK, this backoff is built into the client — you don't need to implement it yourself. Hand-roll backoff only when you're calling the HTTP API directly.
Pair idempotency with retries
When you retry a 429, send the same Idempotency-Key header you used
on the original request. If a request slipped through right before the
limit tripped, the retry returns the cached result instead of running a
second verification. See
Idempotency on the verify endpoint.
Staying under the limit
A few patterns keep you well clear of the throughput ceiling:
- Smooth out bursts. Queue verifications client-side and drain at a steady rate rather than firing a whole fleet's end-of-ride photos in the same instant.
- Don't poll in tight loops. Use webhooks to learn about results instead of hammering the API.
- Provision ahead of launches. If you expect sustained load above ~100 req/s, request a higher Enterprise limit before the event, not during it.
What's next
- Verify endpoint — the full request and
response schema, including the
Retry-Afterheader and idempotency. - Webhooks — push results to your backend so you don't poll into the rate limit.
- Retries — the SDK-side capture loop, distinct from HTTP-level rate-limit backoff.