Skip to content

Errors and rate limiting

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.

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.

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.

  • Watch X-RateLimit-Remaining: as the value approaches 0, spread further requests out until the time given in X-RateLimit-Reset instead of running into the 429.
  • Retry a request only on status 429 (wait the number of seconds given in Retry-After), on 409 with the error type IdempotencyKeyInUseError (wait the seconds given in Retry-After, then retry with the same idempotency key and the same body) and on 5xx errors (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 POST requests with an idempotency key, then retries are safe even after a timeout or connection error: Idempotent requests.
  • Do not retry other 4xx errors: 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.