Notion API rate limits: shared per-integration budget and intermittent 429s under concurrent load
26.5K reputation · 27 Feb 2025, 13:50 UTC
We run a single Notion integration token across several workspaces, and under concurrent sync jobs we see intermittent failures that look like throttling. Notion documents an average rate limit of roughly three requests per second per integration, with some burst tolerance, and HTTP 429 responses when the limit is exceeded.
The uncertainty is how that budget is actually enforced in practice: whether the ~3 req/s figure is a rolling average, a fixed window, or a token bucket, and how much burst headroom exists before 429s start. This matters because our retry-with-backoff logic needs a sensible initial delay, and we also need to decide whether splitting work across multiple integrations is a legitimate scaling path or against the intended usage model.
A second concern is distinguishing API-side throttling from client-side HTTP connection pool saturation in our Node.js client, since both surface as intermittent timeouts and retries.
Specific questions:
- Is the documented ~3 requests/second enforced as an average over a window, and is the burst tolerance quantified anywhere?
- Does Notion return a
Retry-Afterheader on 429 responses that clients should honor? - Are there per-endpoint exceptions to the standard limit (e.g., search or block children endpoints)?
1 answer
1 question comment
Use comments to ask for clarification. Post a solution as an answer.
26,525 reputation · 27 Feb 2025, 18:24 UTC
Practical nuance on Notion’s rate‑limit enforcement
While the public docs state a rough 3 requests per second budget, the actual enforcement follows a token‑bucket model that allows a short burst. In practice you’ll often see the bucket hold up to about 8–10 tokens, so a burst of 8–10 rapid requests can succeed before the bucket is drained and 429s start to appear.
When a 429 is returned, Notion always includes a Retry-After header whose value is expressed in seconds (e.g., Retry-After: 4). A value of 0 means “retry immediately”. Your retry logic should therefore:
- Read the
Retry-Afterheader and pause for that many seconds. - Apply exponential back‑off for subsequent failures.
Distinguishing server‑side throttling from client‑side connection pool saturation can be done by inspecting the error type: 429 with a Retry-After header indicates server‑side limits; ECONNRESET, ETIMEDOUT, or ECONNREFUSED in Node.js point to client‑side pool exhaustion.
Quick checklist
- All workspaces sharing the same integration token draw from a single bucket.
- No per‑endpoint exceptions are documented—every request counts toward the same 3 rps budget.
- Log timestamps, status codes, and
Retry-Aftervalues to verify that bursts are the cause of intermittent 429s.