Rate limits
Two independent limits protect the platform and keep delivery latency predictable.
Per-IP request limits
Section titled “Per-IP request limits”These limit how fast a single caller can send requests. They apply to the caller’s IP prefix (or API key prefix for /v1/*), reset on a rolling window, and protect the service from floods. They are not tied to your plan.
| Operation | Limit | Window |
|---|---|---|
Ingest events (/in/*) |
1,000 requests | 60 seconds |
Read and write API (/v1/*) |
600 requests | 60 seconds |
Manual retries (/v1/deliveries/*/retry) |
60 requests | 60 seconds |
Every API response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. When a per-IP limit is exceeded, the API returns 429 with a Retry-After header.
Workspace request quota
Section titled “Workspace request quota”The quota is shared by every endpoint in a workspace and counts inbound POST /in/:publicId requests that are accepted and stored.
| Plan | Requests | Resets |
|---|---|---|
| Free | 1,000 | Never (lifetime grant) |
| Pro | 10,000 per prepaid block | Never; buy more blocks to top up |
What counts:
| Outcome | Billed |
|---|---|
| Accepted and stored | 1 request |
| Filtered by a source filter or destination rule | 0 |
| Duplicate delivery (same source delivery ID) | 0 |
| Invalid signature | 0 |
| Rejected by a rate limit or the quota itself | 0 |
| Delivery retries after acceptance | 0 |
| Each destination after the first three | +0.25 |
| Each 64 KiB of payload after the first 64 KiB | +1 |
/t/* (anonymous try playground) and /inspect/* use the per-IP limits only and never consume workspace quota.
When the quota is exhausted
Section titled “When the quota is exhausted”Inbound requests return 429 and are not stored:
{ "error": "quota_exceeded", "plan": "free", "upgrade_url": "https://hooks.events/dashboard/billing", "message": "Free plan request limit reached. Upgrade to Pro to keep receiving events."}The response carries X-Quota-Limit and X-Quota-Remaining. Neither the Free grant nor prepaid blocks reset, so the response has no Retry-After: buy more blocks to lift the limit immediately.
Current usage is available from GET /v1/billing/usage; the console reads the same values on the billing page.
Respect Retry-After instead of retrying immediately. Sources that retry on 429 keep events flowing once the window resets; events rejected with 429 are not stored.