Rate limits
Trust tiers T0 to T3, requests a second and daily spend limits, the RateLimit headers on every response, and how to back off when you get a 429.
Two limits apply to a live key: how many requests a second, and how many rupees a day. Both rise as the account earns trust. A test key has 50 requests a second and no spend, because it spends nothing.
#Trust tiers
Every account sits in a tier. It moves up on its own as you pay and as the account ages.
| Tier | Who | Requests a second | Spend a day |
|---|---|---|---|
| T0 | Free, never paid, no KYC | 2 | ₹100 |
| T1 | Paid once (a top-up or a plan), or KYC saved | 5 | ₹10,000 |
| T2 | At least ₹10,000 paid in all, and the account is 7 days old | 20 | ₹50,000 |
| T3 | At least ₹1,00,000 paid, and the account is 30 days old | 100 | ₹5,00,000 |
- A paid plan sets a floor: Build is never below 25 requests a second, Growth never below 100.
- Enterprise and partner accounts have limits set by the platform admin.
- KYC means saving the firm's PAN or GSTIN in the firm settings.
- Your tier is in
GET /accountastier, with your limits underlimits. See authentication.
A call that would take the day's spend over the limit is refused before it runs, with 402 and the code daily_spend_cap. It is free. The limit resets at midnight IST.
#The headers
Every response carries three rate-limit headers, success or error:
| Header | Meaning |
|---|---|
RateLimit-Limit |
Requests a second your key may make. |
RateLimit-Remaining |
Requests left in the current second. |
RateLimit-Reset |
Seconds until the window resets. |
RateLimit-Limit: 5
RateLimit-Remaining: 0
RateLimit-Reset: 1
#When you get a 429
Over the limit, the API returns 429 with the error type rate_limited and the code rate_limited, plus a Retry-After header in seconds. A 429 is never billed and the request did not run. (A different 429, with the code too_many_reports, means 100 reports are already being made; see reports.)
- Stop sending on that key.
- Wait
RateLimit-Resetseconds, plus a little random jitter. - Send again with the same
Idempotency-Keyif it was a POST, so a retry that did run cannot run twice. See idempotency. - If you hit it often, spread calls out (a queue with a fixed rate does this) or move up a tier.
async function call(url: string, init: RequestInit, tries = 5): Promise<Response> {
for (let i = 0; i < tries; i++) {
const res = await fetch(url, init);
if (res.status !== 429 && res.status !== 503) return res;
const wait = Number(res.headers.get("RateLimit-Reset") ?? 1) * 1000;
await new Promise((r) => setTimeout(r, wait * 2 ** i + Math.random() * 250));
}
throw new Error("still limited after retries");
}
import random, time, requests
def call(method, url, tries=5, **kw):
for i in range(tries):
res = requests.request(method, url, timeout=30, **kw)
if res.status_code not in (429, 503):
return res
wait = float(res.headers.get("RateLimit-Reset", 1))
time.sleep(wait * 2**i + random.random() * 0.25)
raise RuntimeError("still limited after retries")
Retry 429 and 503 only. A 400, 401, 402, 403 or 404 will fail again the same way; read its doc_url instead.
#Staying under the limit
- Ask for a list once and keep it, instead of one call per row.
- Use
POST /cause-lists/matchto check many CNRs against a cause list in one call. - Use a watch and a webhook instead of polling a case. A poll that finds nothing still counts against your limit.
- Cache case details yourself.
GET /cases/{cnr}is already cached for up to 24 hours on our side.