Rate Limits
Requests are limited by two independent layers, and the tighter one wins:
- Per API key — the budget for the key you are using. Plan against this number.
- Per IP address — a coarser ceiling shared by every key behind the same network.
The IP layer exists to contain abuse. Unless you route a lot of traffic through one egress IP, the per-key figure is what will actually bind.
| Endpoints | Limit |
|---|---|
GET /v1/products · GET /v1/balance · GET /v1/api-key · GET /v1/tasks/{token} · GET /v1/tasks/{token}/result · GET /v1/tasks/{token}/download | 60 requests / minute per API key (120 / minute per IP) |
POST /v1/detect · POST /v1/detect/file | 10 requests / minute per API key (30 / minute per IP) |
POST /v1/realtime/check/{series} · POST /v1/realtime/detect | Token bucket — per API key: burst 60, refill 1/s (≈60 / minute sustained) per account: burst 120, refill 2/s · IP layer: 180 / minute |
Realtime is not a flat per-minute counter. It is a token bucket: you may spend a full burst of 60 tokens at once and then refill one token per second. Steady throughput is therefore about 60 checks / minute per key, while short spikes are absorbed by the bucket capacity.
Response headers
Section titled “Response headers”Which headers you get depends on which layer answered, and the three groups do not behave identically:
| Header | Meaning | When it is present |
|---|---|---|
X-RateLimit-Remaining | Requests (or tokens) left in the current window | Every response on all three groups |
X-RateLimit-Limit | The ceiling that was applied | Every general and detection response, and every 429 from the IP layer. Never on a realtime response — realtime spends a bucket, so there is no fixed ceiling to report |
Retry-After | Seconds to wait before retrying | Only on a 429 |
X-RateLimit-Reset | Unix timestamp when the window resets | Only on a 429 from the general or detection group; never from realtime or the IP layer |
X-RateLimit-Remaining is the one header you can rely on unconditionally, and the only one realtime reports on success.
When you hit a 429
Section titled “When you hit a 429”Stop sending requests for the number of seconds given by Retry-After, then resume with exponential backoff. The response body tells you which layer you hit:
{ "message": "Too many requests. Limit: 60/min." }| Layer | Status | Body |
|---|---|---|
| Per key — general | 429 | {"message":"Too many requests. Limit: 60/min."} |
| Per key — detection | 429 | {"message":"Too many detection requests. Limit: 10/min."} |
| Per key — realtime | 429 | {"code":"realtime_rate_limited","message":"Realtime detection rate limit exceeded.","retry_after":12} |
| Per IP | 429 | {"message":"Too many requests from this network."} |
Only the realtime rejection carries a machine-readable code; the others are plain messages whose text states the limit that applied. Branch on the HTTP status and Retry-After, and read the message for diagnosis.