Limits and errors
Request and record limits, rate-limit headers and error types of the portal API.
Limits
Limits protect each school's portal from floods (OWASP API Security, API4: unrestricted resource consumption). The numbers below are per-connection defaults; ask the school if you need others.
| Limit | Default |
|---|---|
| Read calls | 600 a minute |
| Write calls | 60 a minute |
| Concurrent calls | 4 |
| Records sent | 2,000 a minute, 50,000 a day |
| Records a school receives (all connections together) | 5,000 a minute |
| Students per batch | 500 |
| JSON body | 5 MB |
| OneRoster CSV package | 20 MB, 50,000 rows per file |
| Jobs waiting or running per school | 4 |
Jobs are paced by the record limits, so a large package can take a few minutes.
Rate-limit headers
Every answer carries headers from the IETF draft RateLimit header fields for HTTP:
RateLimit-Policy: "read";q=600;w=60
RateLimit: "read";r=596;t=42q: calls allowed perwseconds.r: calls left.t: seconds until the window resets.- Writes add a
"records"policy for records per minute.
Going over returns 429 Too Many Requests with Retry-After (seconds). Wait that long, then retry; send writes with an Idempotency-Key so retries never create duplicates.
Errors
Errors are Problem Details (RFC 9457), application/problem+json:
{
"type": "urn:ttdn:problem:invalid-request",
"title": "The request is not valid",
"status": 422,
"detail": "Some students are not valid; nothing was queued.",
"instance": "/integration/v1/students/batch",
"errors": [{ "index": 3, "ref": "SV2026004", "message": "studentCode and name are required." }]
}Act on type; title and detail are for people.
| HTTP | type (urn:ttdn:problem:...) | When |
|---|---|---|
| 401 | unauthorized | Missing, wrong, expired or revoked token, or a token of another school |
| 403 | insufficient-scope | The token lacks the scope for this endpoint |
| 403 | ip-not-allowed | Called from an address outside the school's allowlist |
| 404 | not-found | No such season, student or job |
| 409 | conflict, code-taken, login-name-taken, declared-code | The data points at another account (email, student code or login name already belongs to someone else) |
| 409 | season-closed | Admitting students to a closed season |
| 413 | too-large | Body too large, or more than 500 students |
| 422 | invalid-request | Invalid data; errors lists each row |
| 422 | invalid-package | The OneRoster CSV package cannot be read |
| 422 | idempotency-key-reused, invalid-idempotency-key | The Idempotency-Key was used for a different body, or is longer than 255 characters |
| 429 | rate-limited | Over a limit; wait for Retry-After |
| 500 | internal-error | Portal error; retry later and tell the school if it persists |
| 503 | busy | The school already has 4 jobs waiting; wait for Retry-After |
What the school sees
Under Kết nối hệ thống, each connection's Theo dõi view shows the school calls per day for 30 days (success, request errors, server errors), records sent and recent errors. The portal counts only; it does not store request contents. Daily numbers are kept 13 months, recent errors 30 days.