Errors and rate limiting
Errors
Section titled “Errors”All error types except ValidationError have this structure:
{ "error": { "type": "NotFound", "message": "Resource not found.", "request_id": "req_6b1c9d2e4f3a5078b6c4d2e0" }}The error type ValidationError adds field_errors and non_field_errors:
{ "error": { "type": "ValidationError", "message": "One or more fields are missing or have invalid values. See `field_errors` and `non_field_errors` for details.", "field_errors": { "due_date": ["This field is required."] }, "non_field_errors": [], "request_id": "req_6b1c9d2e4f3a5078b6c4d2e0" }}The error type MissingScope adds required_scope (the missing access scope, also
in the X-Required-Scope response header) and doc_url; see
access scopes.
Your integration should treat the list of error types as open: new types may be added over time.
These error types exist:
| Type | Status code | Description |
|---|---|---|
ValidationError |
400 | One or more fields are missing or have invalid values. See field_errors and non_field_errors for details. |
ParseError |
400 | The request contains malformed data. |
IdempotencyKeyInvalidError |
400 | The Idempotency-Key header is invalid, see Idempotent requests. |
AuthenticationFailed |
401 | Authentication failed: invalid authorization header or invalid API key. |
NotAuthenticated |
401 | Authentication failed: no API key was provided. |
MissingScope |
403 | The API key lacks the access scope this endpoint requires; required_scope names it. See access scopes. |
PermissionDenied |
403 | No permission to perform this action. |
NotFound |
404 | Resource not found. |
MethodNotAllowed |
405 | The HTTP method is not allowed. |
NotAcceptable |
406 | The Accept header of the request could not be satisfied. |
IdempotencyKeyInUseError |
409 | A request with the same Idempotency-Key is still in progress, see Idempotent requests. |
UnsupportedMediaType |
415 | Unsupported media type in the request. |
IdempotencyKeyReuseError |
422 | The Idempotency-Key was already used for a different request, see Idempotent requests. |
Throttled |
429 | A rate limit was reached. |
InternalServerError |
500 | An internal server error occurred. |
Request IDs
Section titled “Request IDs”Every API response carries the HTTP header X-Request-ID with a unique ID such as
req_6b1c9d2e4f3a5078b6c4d2e0; error responses repeat it as request_id in the body.
Include this ID in support inquiries: it lets us look up the exact request in our logs.
Rate limiting
Section titled “Rate limiting”Schulungstool applies rate limiting to its API to ensure consistent API performance for all users. By default, every Schulungstool account can make up to 1000 API requests per minute.
Every response reports the current state of the limit in three HTTP headers, so you can pace your requests before a limit is reached:
| Header | Meaning |
|---|---|
X-RateLimit-Limit |
Maximum number of requests allowed in the current window. |
X-RateLimit-Remaining |
Requests still available in the current window, after this request. |
X-RateLimit-Reset |
Time at which at least one request becomes available again, as a Unix timestamp in seconds. |
Exception: responses with status code 401 do not carry these headers, because without
a valid API key no account can be associated with the request. Responses with status
code 403 do not carry them either: the access scope check runs before the rate limit
check.
When a limit is reached, you receive an HTTP response with status code 429:
{ "error": { "type": "Throttled", "message": "Request was throttled. Expected available in 7 seconds." }}The response includes the HTTP header field Retry-After with the number of seconds to
wait, after which a new request can be expected to receive a normal response.
Retry recommendations
Section titled “Retry recommendations”- Watch
X-RateLimit-Remaining: as the value approaches 0, spread further requests out until the time given inX-RateLimit-Resetinstead of running into the429. - Retry a request only on status
429(wait the number of seconds given inRetry-After), on409with the error typeIdempotencyKeyInUseError(wait the seconds given inRetry-After, then retry with the same idempotency key and the same body) and on5xxerrors (with exponential backoff and a limited number of attempts). Add a small random delay to each wait, so that many clients do not retry at the same moment. - Retry
POSTrequests with an idempotency key, then retries are safe even after a timeout or connection error: Idempotent requests. - Do not retry other
4xxerrors: the request itself is the problem, and repeating it unchanged fails again. Fix the request based on the error type and message instead, see the table above.