Rate limits
API quotas apply per organization: every key of an organization draws from the same budget. There are three request limits and two stream limits, set by your plan.
| Plan | Requests / minute | Requests / day | Stream connections | Subscriptions / connection | Firehose topics | Webhooks |
|---|---|---|---|---|---|---|
| Core | No API access | |||||
| Pro | 60 | 50,000 | 1 | 50 | No | No |
| Elite | 300 | 500,000 | 5 | 500 | Yes | Yes |
| Enterprise | 1,200 | 5,000,000 | 20 | 2,000 | Yes | Yes |
Concurrent requests are limited to 10 on Pro, 25 on Elite and 100 on Enterprise. Enterprise contracts can set custom quotas; your current
values are always in the headers below and in GET /v1/usage.
Headers
Section titled “Headers”Every response to a key-authenticated request carries two headers, following the IETF RateLimit header fields draft:
RateLimit-Policy: "minute";q=60;w=60, "day";q=50000;w=86400RateLimit: "minute";r=59;t=1RateLimit-Policylists your quotas:qrequests per window ofwseconds.RateLimitdescribes the policy closest to running out:rrequests remain, and the window is fully replenished intseconds.
The minute and day limits refill continuously (a generic cell rate algorithm), not at fixed clock boundaries: you may use the whole quota in a burst, and capacity returns at the quota’s rate afterwards.
#!/usr/bin/env bash# The quota headers of a request: RateLimit-Policy (your quotas) and RateLimit# (requests left in the most constrained window and seconds until it resets).set -euo pipefailAPI="${STATUS302_API_URL:-https://api.status302.com}"
curl --fail-with-body -sS -o /dev/null -D - "$API/v1/events?limit=1" \ -H "Authorization: Bearer $STATUS302_API_KEY" \ | grep -i -E '^(ratelimit|ratelimit-policy|retry-after):'429 Too Many Requests
Section titled “429 Too Many Requests”When a limit is exhausted, the API answers 429 with a Retry-After header (whole seconds) and a problem naming the limit:
HTTP/1.1 429 Too Many RequestsRetry-After: 17RateLimit-Policy: "minute";q=60;w=60, "day";q=50000;w=86400RateLimit: "minute";r=0;t=60Content-Type: application/problem+json
{ "type": "https://status302.com/problems/rate-limited", "title": "Rate limited", "status": 429, "detail": "requests per minute quota of your plan exhausted; retry after the Retry-After seconds", "limit": "minute"}limit is minute, day or concurrent for your key’s quotas, or stream_tickets (below). (stream_connections is reserved in the
schema; REST responses don’t use it.) A concurrent refusal carries Retry-After: 1 and no RateLimit headers, because it isn’t a
windowed quota. Signed-in app sessions don’t have these quotas; they are throttled per user and per IP instead (limit user or ip).
Wait for Retry-After seconds before retrying, and retry a bounded number of times. Refused requests don’t count against your quota, but
they are counted as throttled in your usage.
// Reading the quota headers, and handling 429 without the SDK.import { setTimeout as sleep } from "node:timers/promises";import { Status302 } from "@status302/sdk";
const api = process.env.STATUS302_API_URL ?? "https://api.status302.com";const headers = { Authorization: `Bearer ${process.env.STATUS302_API_KEY}` };
// Plain fetch: retry a 429 after Retry-After seconds, a bounded number of times.async function getJSON(url: string, attempts = 5): Promise<unknown> { for (let attempt = 1; ; attempt++) { const res = await fetch(url, { headers }); console.log(`RateLimit-Policy: ${res.headers.get("ratelimit-policy")}`); console.log(`RateLimit: ${res.headers.get("ratelimit")}`); if (res.status === 429 && attempt < attempts) { const wait = Number(res.headers.get("retry-after") ?? "1"); console.log(`rate limited, retrying in ${wait}s`); await sleep(wait * 1000); continue; } if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`); return res.json(); }}await getJSON(`${api}/v1/events?limit=1`);
// The SDK retries 429s itself and parses the headers on every response.const status302 = new Status302({ apiKey: process.env.STATUS302_API_KEY, baseUrl: api });const res = await status302.request("get", "/v1/events", { query: { limit: 1 } });if (res.rateLimit) { const { policy, remaining, reset, policies } = res.rateLimit; console.log(`${remaining} requests left in the ${policy} window, resets in ${reset}s`); for (const p of policies) console.log(`quota ${p.name}: ${p.quota} per ${p.window}s`);}# Reading the quota headers, and handling 429 without the SDK.import osimport timefrom typing import Any
import httpx
from status302 import Status302
api = os.environ.get("STATUS302_API_URL", "https://api.status302.com")headers = {"Authorization": f"Bearer {os.environ['STATUS302_API_KEY']}"}
def get_json(url: str, attempts: int = 5) -> Any: """Plain httpx: retry a 429 after Retry-After seconds, a bounded number of times.""" for attempt in range(1, attempts + 1): res = httpx.get(url, headers=headers) print("RateLimit-Policy:", res.headers.get("ratelimit-policy")) print("RateLimit:", res.headers.get("ratelimit")) if res.status_code == 429 and attempt < attempts: wait = int(res.headers.get("retry-after", "1")) print(f"rate limited, retrying in {wait}s") time.sleep(wait) continue res.raise_for_status() return res.json()
get_json(f"{api}/v1/events?limit=1")
# The SDK retries 429s itself and parses the headers on every response.with Status302(os.environ["STATUS302_API_KEY"], base_url=api) as status302: res = status302.request("GET", "/v1/events", query={"limit": 1}) if res.rate_limit: rl = res.rate_limit print(f"{rl.remaining} requests left in the {rl.policy} window, resets in {rl.reset}s") for p in rl.policies: print(f"quota {p.name}: {p.quota} per {p.window}s")The SDKs retry 429 responses automatically for every method, waiting out Retry-After up to 60 seconds (configurable), and raise a
RateLimitError when the wait would be longer or retries are exhausted.
Staying under the limits
Section titled “Staying under the limits”- Use the stream for live data. Polling an event’s metrics every second costs 60 requests a minute; subscribing to
event.{id}.kpiscosts none. See the stream guide. - Use exports for bulk reads. An export of the whole catalog is one request; paging it is hundreds.
- Use webhooks for events you react to. Alert firings, on-sales and status changes arrive without polling. See webhooks.
- Watch
RateLimit. Slow down whenrgets low instead of running into429. - Check your usage.
GET /v1/usagereturns your quotas, current consumption and history by endpoint group and key; the same data is in Settings → Developers → Usage.
Stream limits
Section titled “Stream limits”Each open stream connection holds one of your organization’s connection slots for as long as it is open. Opening one more closes it with
code 4429; close idle connections or multiplex topics over one connection. Each connection can hold up to your plan’s subscriptions
per connection; one more answers an in-band subscription_limit error and the connection stays open. Clients may send at most 20
messages per second (bursts of 50). See stream limits.

