Skip to content

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

TierQuotaWindowBulk accessMarkets
Taster250per dayNo (depth-capped ~1,000)1
Sous Chef250,000per monthYesAll
Head Chef2,000,000per monthYesAll
Executive ChefCustomNegotiatedYesAll

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.

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 with GET /v1/{resource}/{id}. A bare or filter-only list returns 403 /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, foods requests return 403 /problems/market-not-selected. Once set, foods is 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:

HeaderMeaning
RateLimit-LimitYour account's quota for the window
RateLimit-RemainingRequests left in the current window, across all your keys
RateLimit-ResetSeconds until allowance next frees up
X-RateLimit-ResetSame 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.

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.

TierSustained rateBurst
Taster1 request/secup to 10 at once
Sous Chef10 requests/secup to 50 at once
Head Chefunthrottled
Executive Chefnegotiatednegotiated

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:

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:

GET
curl https://api.noms.sh/v1/usage \
  -H "Authorization: Bearer $NOMS_KEY"
Response
200 OK
{
"data": {
"tier": "sous_chef",
"quota_window": "monthly",
"request_quota": 250000,
"request_count": 18432,requests used this window, across every key you own
"remaining": 231568,requests left this window
"period_end": "2026-07-14T00:00:00Z"when allowance next frees up
}
}

Your dashboard shows the same total, plus a per-key breakdown of where it went.

Next steps

  • Support: what to send us when something still does not add up.