Errors, pagination and limits
Error bodies
Every error is JSON, in one of these shapes.
- Most errors
- Validation (400)
- Refused launch (400)
- LLM gateway
Authentication, permission, not-found, conflict and rate-limit errors carry a human-readable detail, and sometimes a machine-readable code:
{"detail": "Authentication credentials were not provided."}
A request that fails validation maps each invalid field to its messages. Errors inside a JSON field, such as a campaign spec or an agent config, are keyed by their dotted path, and errors not about one field are under non_field_errors:
{"name": ["This field is required."]}
{"spec": {"attacks.1.parameters.bogus": "Extra inputs are not permitted"}}
A launch that fails its checks lists them in problems, the same objects the preflight returns:
{
"problems": [
{
"code": "INSUFFICIENT_CREDITS",
"message": "Your organization does not have enough credits for a cloud run.",
"details": {"balance": "0.42"}
}
]
}
The gateway answers its own errors in the shape OpenAI clients expect:
{
"error": "Insufficient credits for this operation.",
"code": "INSUFFICIENT_CREDITS",
"details": "Estimated cost: 0.012000 USD, Available: 0.004000 USD. Please add credits to continue."
}
Match on the status code and on code when there is one; the messages are for people and can change.
Status codes
| Status | Means | What to do |
|---|---|---|
200, 201 | Done; 201 when something was created | |
204 | Done, no body (a delete) | |
304 | Not modified: the ETag you sent in If-None-Match still holds | Keep the copy you have |
400 | The request is invalid; nothing was changed | Fix the fields named in the body |
401 | Missing, invalid, expired or revoked credential, or an API key on an endpoint that needs the dashboard's login | See Authentication |
402 | Not enough credits for a gateway call | See Credits |
403 | The credential may not do this, for example a user without an organization | Check which user the key belongs to with GET /key/context |
404 | No such object in your organization, or a page past the end of a list | Check the id and the path |
409 | The run is not in a state that allows the call: already finished, still active, or already claimed | Read the run's status, then cancel it or wait |
429 | Rate limited | Wait the seconds in the Retry-After header, then retry |
500, 502, 503, 504 | A server or upstream failure | Retry later, with backoff |
The reference lists, for each operation, the statuses it can answer and why.
Pagination
List endpoints return one page at a time:
{
"count": 42,
"next": "https://api.hackagent.dev/agent?page=3",
"previous": "https://api.hackagent.dev/agent?page=1",
"results": []
}
count is the total across all pages, and next and previous are ready-to-use URLs, or null at either end. Ask for a page with page, starting at 1. Pages hold 10 items, except where an endpoint accepts page_size:
| Endpoint | Largest page_size |
|---|---|
GET /result, GET /run/{id}/goals | 1000 |
GET /run, GET /run/summary, GET /run/{id}/attempts | 100 |
A larger page_size is lowered to the largest. A page past the end gets 404 on most lists; the run report lists (/run/summary, /run/{id}/goals, /run/{id}/attempts) treat an invalid page as page 1 instead. A few endpoints return everything at once, such as GET /campaign/techniques and GET /run/{id}/matrix. To read every page, follow next until it is null; First request has a Python loop.
Rate limits
Each user can make 1,000 requests an hour, shared by all of the user's API keys and dashboard sessions. Past that, requests get 429 with a Retry-After header saying how many seconds to wait. The run report reads (GET /run/summary and the /run/{id}/... reports) count against a separate, larger budget, so a live view that polls a run does not use up the main one.
Large campaigns that record their results from your machine make a request for every result and trace step, so they can reach the limit. To stay under it:
- Poll
GET /run/{id}/summarywithIf-None-Match, and read the heavier reports only when itsrevisionmoves. - Ask for larger pages where
page_sizeis accepted, and leave out what you do not need (include_traces=falseonGET /result,include_results=falseonGET /run/{id}). - Launch on HackAgent cloud: a cloud run's requests count against its own budget, not yours.
Credits
Only the LLM gateway is paid, from your organization's credit balance, and a cloud launch needs a minimum balance. Everything else in the API is free.