Limits (rate limits)
The per-minute limits use a sliding window: the requests of the last 60 seconds are counted, not those since the full minute.
| Limit | Value | Window |
|---|---|---|
| Read requests per token | 120 | sliding, 1 minute |
| Write requests per token | 20 | sliding, 1 minute |
| All requests per organization | 600 | sliding, 1 minute |
| All requests per IP address | 300 | sliding, 1 minute |
| Failed sign-ins per IP address | 20 | sliding, 1 minute |
| New imports per token (POST /v1/tenant-imports) | 20 | sliding, 1 hour |
| New imports per account (creator of the token, all of its imports together; fixed) | 20 | sliding, 10 minutes |
| Open imports per organization (created via the API) | 50 | no window: until taken over or deleted |
| Imports in total per organization (file and API, including completed ones; fixed) | 500 | no window: completed imports are deleted after 30 days, open ones after 90 days |
| New DATEV exports per organization (POST /v1/datev/exports) | 30 | 1 hour |
| New prospect enquiries per token (POST /v1/prospects) | 30 | 1 hour |
| New prospect enquiries per token and email address | 5 | 24 hours |
| Webhooks per organization | 10 | — |
| Active tokens per organization | 25 | — |
| Entries per list page | at most 200 | — |
Limits for imports
So that a misused token cannot block your organization's imports, POST /v1/tenant-imports has limits of its own:
| Limit | Default | Response when exceeded |
|---|---|---|
| Open imports per organization (created via the API) | 50 | 422 import_limit_reached |
| New imports per token and hour | 20 | 429 import_rate_limited |
| New imports per account (creator of the token) in 10 minutes; fixed, cannot be raised | 20 | 429 rate_limited |
| Imports in total per organization (file and API, including completed ones; fixed) | 500 | 422 import_total_limit_reached |
| Records per import | 500 | 400 validation_error |
| Request size (import · takeover) | 1 MB · 64 KB | 413 payload_too_large |
An import counts as open until all of its records have been taken over or deleted in the management app. The values that apply to your organization and the number of open imports are shown under Administration → API access; when a limit is exceeded, error.details states the limit and the current count (e.g. { "limit": 50, "open": 50 }). If you need more, contact support — the limits can be raised per organization. A retry with an Idempotency-Key that has already been used does not count as a new import. You release API imports you no longer need yourself: DELETE /v1/tenant-imports/{id} (see Cancel an import).
Quota headers
As soon as the token is known, all responses — including errors such as 400, 403, 404 or 409 — carry X-RateLimit-Limit (the token's limit per minute: read or write), X-RateLimit-Remaining (calls still available) and X-RateLimit-Reset (seconds until all calls counted so far have left the sliding window; at most 60). Without a known token they are absent: with 401, with an unknown address (route_not_found) and with 503 before the check. With 429 rate_limited, the Retry-After header states the waiting time in seconds until the next call is possible (also for the per-organization limit); X-RateLimit-Reset then has the same value. Wait at least that long and double the waiting time on repeated 429 or 503 responses.
429 in detail: special cases
Every 429 carries Retry-After (whole seconds, at least 1) — that is the binding wait. The number depends on the limit that was hit; for the special cases below X-RateLimit-Remaining says nothing about the cause, because it keeps counting the minute limit of the token.
| Limit hit | Answer | Retry-After | error.details |
|---|---|---|---|
| Minute limit per token or organization | 429 rate_limited | seconds until the next call is possible (at most 60) | — |
| Overall limit or failed attempts per IP address | 429 rate_limited | as above; without quota headers (X-RateLimit-*) if the token had not been checked yet | — |
| Webhook replay: 1000 deliveries per webhook and hour | 429 rate_limited | 1 to 3600: until there is room in the hourly quota again | { "limit": 1000, "retry_after": 1800 } |
| Test events: at most 3 waiting per webhook | 429 rate_limited | 60 | { "limit": 3 } |
| New imports per token and hour | 429 import_rate_limited | 3600 | { "limit": 20, "used": 20 } |
| Imports: 20 in 10 minutes per account | 429 rate_limited | 600 | — |
| Prospect submissions per token (hour, day per email address) | 429 rate_limited | wait until there is room again | { "limit": 30 } |
Minute limit (most common case)
HTTP/1.1 429 Too Many Requests
Retry-After: 23
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 23
X-Request-Id: req_8c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f
{
"success": false,
"error": {
"code": "rate_limited",
"message": "<explanatory text in German>"
},
"request_id": "req_8c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f"
}Replay limit of a webhook
HTTP/1.1 429 Too Many Requests
Retry-After: 1800
X-RateLimit-Limit: 20
X-RateLimit-Remaining: 17
X-RateLimit-Reset: 1800
X-Request-Id: req_8c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f
{
"success": false,
"error": {
"code": "rate_limited",
"message": "<explanatory text in German>",
"details": {
"limit": 1000,
"retry_after": 1800
}
},
"request_id": "req_8c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f"
}What X-RateLimit-Reset means: on normal responses the seconds until all calls counted so far have left the sliding minute window (at most 60). On a 429 with Retry-After it carries the same value as Retry-After — for the hourly limits that is up to 3600, not only up to 60. Schedule your retry by Retry-After; repeat the request unchanged for replay and imports, writing calls with the same Idempotency-Key. A replay that fails on the hourly limit has queued nothing; do not let several processes restore at the same time.