Handling k12panel API errors
Every error response uses the same shape, so your program can handle them consistently.
Every error response uses the same shape, so your program can handle them consistently:
{ "error": "not_found", "message": "No person matches email 'nobody@school.org'." }
Check the HTTP status code and the error field.
Common responses
| Status | error |
Meaning | What to do |
|---|---|---|---|
| 401 | authentication_failed |
Missing, invalid, revoked, or expired key | Check the Authorization header; rotate the key if needed |
| 403 | insufficient_scope |
The key lacks the required permission | Grant the permission (or use a key that has it) |
| 404 | — | The record doesn't exist in your organization | Check the id; you can only access your own org's data |
| 409 | already_checked_out |
The asset is already checked out to someone | Check it in first, or leave it as-is |
| 409 | ambiguous_reference |
A lookup matched more than one record | Retry with an exact id (see below) |
| 422 | not_found |
A referenced record (person/site) wasn't found | Check the email/name you sent |
| 422 | invalid |
The request body failed validation | See the details field |
| 429 | — | Too many requests | Wait a moment and retry |
The "more than one match" case
Because emails, serials, and names aren't always unique, a lookup used in an action (like checkout) can match more than one record. Instead of guessing, the API returns the list of candidates so you can pick the right one and retry with its id:
{
"error": "ambiguous_reference",
"message": "3 people match email 'jsmith@school.org'. Retry with an id.",
"candidates": [
{ "id": 3087, "first_name": "John", "last_name": "Smith", "org_unit": "HS / Staff", "groups": ["Teachers"] },
{ "id": 4192, "first_name": "Jane", "last_name": "Smith", "org_unit": "MS / Staff", "groups": ["Teachers"] },
{ "id": 5310, "first_name": "Jamie", "last_name": "Smith", "org_unit": "HS / Students", "groups": ["Grade 11"] }
]
}
The easy case (a unique email) is a single call. The ambiguous case gives you everything you need to resolve it — including enough detail to show a technician a "which one?" prompt. Retry the action with the exact id:
{ "person": { "id": 3087 } }
Rate limits
Each key can make a reasonable number of requests per minute. If you exceed it you'll get a 429 response — pace your requests and retry after a moment. Well-behaved tools won't hit this in normal use.