Rate limits

Limits are applied per API key and per client IP. Publishable keys get a tighter ceiling because they live in places anyone can read.

Current limits

ScopeSustainedBurst
Per secret key (sk_)20 requests/second40 requests
Per publishable key (pk_)5 requests/second10 requests
Per client IP50 requests/second100 requests

Limits use a token bucket: the burst figure is how many requests you can make at once after being idle, and the sustained figure is how fast the bucket refills. A steady rate below the sustained number never trips the limit.

Per-key and per-IP limits apply independently — a request must satisfy both. Higher limits are available on Growth and Enterprise plans; write to sales@emnetid.com with your expected throughput.

Why publishable keys are capped harder

A publishable key ships inside a web page or a mobile binary, so anyone can extract it. The tighter ceiling limits what a stranger can do with one they found, without affecting normal use — a browser starting one verification per user is nowhere near five per second.

When you are limited

429 response
HTTP/1.1 429 Too Many Requests
Retry-After: 5
Content-Type: application/json

{
  "error": "rate_limited",
  "message": "Too many requests. Retry after 5 seconds.",
  "status": 429
}

Every throttled response carries Retry-After in seconds. Honour it rather than inventing your own delay — retrying sooner just consumes the budget you are waiting for.

Backing off correctly

Retry-After, then exponential backoff with jitter
int attempt = 0;
while (true) {
    try {
        return emnet.startPersonVerification();

    } catch (EmnetApiException e) {
        if (e.status() != 429 || ++attempt > 4) {
            throw e;
        }
        // Prefer the server's advice; otherwise back off exponentially.
        Duration wait = retryAfter(e).orElseGet(
                () -> Duration.ofMillis((long) (Math.pow(2, attempt) * 250)));

        // Jitter matters: without it, every throttled client retries in lockstep
        // and you rebuild the same spike that caused the limit.
        Thread.sleep(wait.toMillis() + ThreadLocalRandom.current().nextInt(250));
    }
}
Add jitter

If a hundred clients are throttled at the same moment and all wait exactly five seconds, they all return at exactly the same moment. Randomising the delay spreads them out and is the difference between recovering and oscillating.

Staying under the limit

  • Verify at human pace. A verification is driven by a person clicking through a redirect. If you are near the limit, something is looping.
  • Don’t poll sessions. The result arrives on your callback. Read a session for reconciliation, not in a loop waiting for a status change.
  • Cache in your own database. Once you have a verified identity, store it. Re-reading Emnet for data you already hold spends quota for nothing.
  • Use separate keys per service. Each key gets its own budget, so a batch job cannot exhaust the limit your signup flow depends on.
  • Spread scheduled work. If you reconcile on a schedule, add jitter to the schedule too.

During an infrastructure incident

If our rate-limiting infrastructure itself has a problem, the edge falls back to a conservative per-IP floor rather than removing limits altogether. You may briefly see 429 at lower volumes than the table above. The response is the same: honour Retry-After and back off. Normal limits resume automatically.

Related

  • Errors — the full status-code reference and retry policy.
  • Authentication — why key type affects your limit.