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 a Retry-After won'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.

Limits are per API key

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:

text
HTTP/1.1 429 Too Many Requests
Retry-After: 2
X-Request-Id: req_8x92m4k9

Retry-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 is not a failed verification

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:

  1. Honor Retry-After. If the header is present, wait at least that long before retrying.
  2. 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.
  3. 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.
  4. 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");
}
The SDK already does this

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-After header 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.

Get in Touch

Questions about pricing, integrations, or custom deployments? We'd love to hear from you.