Rate limits & quotas
Your account has two independent limits, set by your tier: a request quota
(how much you call over a window) and a burst limit (how fast you call in the
moment). This page covers both, the headers we send, what each 429 looks like, and
how to read your own usage.
Quotas by tier
| Tier | Quota | Window | Bulk access | Markets |
|---|---|---|---|---|
| Taster | 250 | per day | No (depth-capped ~1,000) | 1 |
| Sous Chef | 250,000 | per month | Yes | All |
| Head Chef | 2,000,000 | per month | Yes | All |
| Executive Chef | Custom | Negotiated | Yes | All |
Limits are launch values and may change before general availability. Prices and plan comparison live in the pricing section.
One allowance per account
The quota belongs to you, not to any one key. Every key you hold draws on the same allowance, and a request through any of them counts against the same total. Creating a second key gives you a second credential, never a second quota.
You can hold up to 5 keys at once. Keys exist so you can separate environments and rotate a secret without downtime; use one per app or per environment, revoke what you no longer need.
Reading your standing
Because the numbers are per account, GET /v1/usage reports the same figures no
matter which of your keys you ask with.
When the window resets
The two windows behave differently, and the difference matters if you are pacing a job against them.
- Free (Taster): a rolling 24 hours. Your usage is what you spent in the last 24 hours, measured in hourly steps. Nothing resets at midnight: allowance frees up gradually as each hour ages out. Spend everything at 3pm and you are clear again around 3pm the next day, not at 00:00.
- Paid: your billing period. Usage counts from the start of the current period and resets when it renews. Upgrading between paid tiers raises the ceiling immediately without moving the renewal date or clearing what you have spent.
RateLimit-Reset always tells you when allowance next frees up: the top of the next
hour on the free tier, the next renewal on a paid one.
Free-tier access
Beyond the volume quota, the free Taster tier has three structural limits. It is built for looking things up, not bulk-downloading the catalog. Paid tiers lift all three.
- Search-first listing. A list endpoint requires a
?q=search (filters and sorting compose on top), or you fetch a single record withGET /v1/{resource}/{id}. A bare or filter-only list returns403 /problems/enumeration-forbidden. - Result depth. Search results are capped at roughly the first 1,000 matches;
paging past that returns
403 /problems/result-depth-exceeded. - Single market. You serve one market (country) of your choosing. Set it in
your dashboard or with
PUT /v1/market; until you do,foodsrequests return403 /problems/market-not-selected. Once set,foodsis scoped to that market. The market belongs to your account, so every key you hold serves the same one. See Choosing your market.
RateLimit headers
Every metered response carries your current standing, so you never have to guess.
We send both the IETF RateLimit-* headers and the widely-parsed X-RateLimit-*
aliases:
| Header | Meaning |
|---|---|
RateLimit-Limit | Your account's quota for the window |
RateLimit-Remaining | Requests left in the current window, across all your keys |
RateLimit-Reset | Seconds until allowance next frees up |
X-RateLimit-Reset | Same moment, as a Unix epoch timestamp |
What counts as a request
A 304 Not Modified counts. When you revalidate a cached page with
If-None-Match (see Querying), the request still reached the API,
so it is metered and rate-limited exactly like a 200. Revalidation saves you
bandwidth and time, not quota.
A response your own client serves from its own cache never reaches us at all, so there is nothing to count. That is the only way a read is free.
When you hit the limit
At or over quota, requests return 429 with a Retry-After header (seconds until
reset). The blocked request is not counted against you. Both 429s carry the
same three extension members (limit, window and retry_after), so you can
back off from the body alone if you are not reading headers.
429 Too Many Requests
{ "type": "/problems/quota-exceeded", "title": "Quota exceeded", "status": 429, "limit": 250, "window": "daily", "retry_after": 1623, "detail": "Request quota of 250 per day exceeded. Retry once the current window resets.", "doc_url": "https://api.noms.sh/docs#tag/quota-exceeded" }
Handle it by backing off until Retry-After:
const res = await fetch(url, { headers: { "X-API-Key": process.env.NOMS_KEY } }); if (res.status === 429) { const wait = Number(res.headers.get("Retry-After")) * 1000; await new Promise((r) => setTimeout(r, wait)); // ...then retry }
Burst rate limits
Separate from the quota, each tier has a burst limit that caps how fast you call, not how much in total. You can be well under your quota and still be asked to slow down if you fire requests too quickly in a short window.
| Tier | Sustained rate | Burst |
|---|---|---|
| Taster | 1 request/sec | up to 10 at once |
| Sous Chef | 10 requests/sec | up to 50 at once |
| Head Chef | unthrottled | — |
| Executive Chef | negotiated | negotiated |
Think of it as a bucket of tokens: each request spends one, and tokens refill at the sustained rate. A burst of calls after a quiet moment is fine; that is what the burst allowance is for. A relentless loop gets clamped to the sustained rate once the burst is spent. Like the quota, the bucket is per account: calling through several keys at once spends from the one bucket, it does not multiply your rate.
Go too fast and requests return 429 with a Retry-After header (usually a second
or two) and a different problem type from the quota 429:
429 Too Many Requests
{ "type": "/problems/rate-limited", "title": "Rate limit exceeded", "status": 429, "limit": 1.0, "window": "second", "retry_after": 1, "detail": "Burst rate limit of 1 requests per second exceeded. Slow down and retry after the Retry-After interval.", "doc_url": "https://api.noms.sh/docs#tag/rate-limited" }
Handle it exactly like a quota 429: back off until Retry-After, using the same
snippet as above. Branch on the type field if you want different behavior. Unlike the quota 429, a burst 429 carries only Retry-After, not the
RateLimit-* headers.
Check your usage
Read your account's current-window count any time with GET /v1/usage. It is
unmetered, so it stays reachable even when you're over quota:
curl https://api.noms.sh/v1/usage \ -H "Authorization: Bearer $NOMS_KEY"
Your dashboard shows the same total, plus a per-key breakdown of where it went.
Need more headroom?
If you're bumping the ceiling, upgrade in the pricing section or, for enterprise volume, get in touch.
Next steps
- Support: what to send us when something still does not add up.