Rate limits
Rate limits protect the service and your balance from runaway loops. They are counted in requests per minute, separately from credits.
The limits
Section titled “The limits”| Caller | Limit |
|---|---|
sk_live_ key |
The key’s own limit, shown in the dashboard |
pk_ key |
The key’s own limit, counted per visitor IP, and the key’s overall limit |
Client token (ct_) |
The limit of the key that created it, shared with that key and all its other tokens |
sk_test_ keys |
10 requests per minute and a daily quota of 100 per UTC day counted in credits, shared by the organization |
demo |
10 requests per minute per IP |
GET /v1/places/search |
An extra 30 requests per minute per key or token, on top of the above |
Limits work as a token bucket: a key with a limit of 120 can burst up to 120 requests, and capacity refills continuously at 120 per minute.
Headers
Section titled “Headers”Successful and rate-limited responses carry the IETF RateLimit headers for the bucket that applied:
| Header | Meaning |
|---|---|
ratelimit-limit |
Requests per minute for this bucket |
ratelimit-remaining |
Requests you can make right now |
ratelimit-reset |
Seconds until the bucket is full again |
When you hit a limit
Section titled “When you hit a limit”The API returns 429 with code: "rate_limited" and a Retry-After header in seconds. Nothing is charged.
HTTP/1.1 429 Too Many RequestsRetry-After: 5ratelimit-limit: 10ratelimit-remaining: 0ratelimit-reset: 59{ "type": "https://navapatra.com/docs/errors#rate_limited", "title": "Too many requests", "status": 429, "detail": "Rate limit exceeded. Retry in 5 s.", "code": "rate_limited", "requestId": "7b02b780-7584-4888-b40f-0d701653609e"}When a test key passes 100 requests in a day, the detail says so and Retry-After points to the next UTC midnight.
Retrying well
Section titled “Retrying well”Wait for Retry-After, then retry. For bulk jobs, pace requests below the limit instead of relying on retries:
async function call(url: string, init: RequestInit, attempts = 3): Promise<Response> { for (let i = 0; ; i++) { const res = await fetch(url, init); if (res.status !== 429 || i >= attempts - 1) return res; const wait = Number(res.headers.get('retry-after') ?? '1'); await new Promise((r) => setTimeout(r, Math.min(wait, 60) * 1000)); }}The TypeScript SDK does this for you. For many guna milan scores, use batch matching: 100 pairs in one request count once against the rate limit.
Need a higher limit? Write to hello@navapatra.com with your expected volume.