Errors and limits
The error shape, what each code means, rate limits and retrying safely.
Errors
Every error has the same shape, so a pipeline can branch on code rather than on the message:
{
"code": "API_KEY_CAP_EXCEEDED",
"message": "This run reserves 1000 credits and this API key's budget of 500 this cycle has 300 left. Raise the key's budget in the dashboard.",
"statusCode": 403
}Some validation errors add a field naming the part of the request that was wrong. Messages are written for people and may change; codes will not.
| Status | Code | Meaning |
|---|---|---|
| 400 | VALIDATION_ERROR | The request is missing something or has an invalid value |
| 401 | UNAUTHORIZED | No key, or the key is invalid, expired, revoked or for a different environment |
| 402 | INSUFFICIENT_CREDITS | Your balance does not cover the run |
| 403 | FORBIDDEN | The key, or its creator's role, lacks the permission for this call, or a pentest target is not assigned to you in the organization |
| 403 | ACCOUNT_SUSPENDED | The account is suspended |
| 403 | API_KEY_CAP_EXCEEDED | The run would take the key past its credit budget |
| 403 | ORG_CREDIT_CAP_EXCEEDED | The run would take you past your spending allowance in the organization |
| 403 | TARGET_API_RUNS_DISABLED | API runs are turned off for this target |
| 403 | TARGET_NOT_ASSIGNED | In an organization, the domain you asked to scan is not assigned to you |
| 403 | PLAN_LIMIT_EXCEEDED | Your plan does not allow this, for example its scan limit is reached |
| 404 | NOT_FOUND | No such scan, pentest or target verification in this account |
| 409 | IDEMPOTENCY_IN_PROGRESS | The first request with this Idempotency-Key is still running |
| 422 | IDEMPOTENCY_KEY_REUSED | This Idempotency-Key was already used with a different request |
| 429 | RATE_LIMITED | Too many requests. Wait for Retry-After |
| 429 | PENTEST_CONCURRENCY_EXCEEDED | You already have as many pentests running as your account allows. Wait for one to finish |
| 500 | INTERNAL_ERROR | Something went wrong on our side. Retry with the same Idempotency-Key |
RATE_LIMITED and PENTEST_CONCURRENCY_EXCEEDED are both 429 but need different handling: back off and retry the first, wait for one of your running pentests to finish before the second.
Rate limits
| Limit | Per | Allowance |
|---|---|---|
| Reads | key | 120 a minute |
| Reads | account | 600 a minute, across all your keys |
| Starts and cancels | key | 10 a minute |
Every answer carries the limit you are closest to:
RateLimit-Limit: 120
RateLimit-Remaining: 117
RateLimit-Reset: 42RateLimit-Reset is the number of seconds until the window starts over. A refused request answers 429 with Retry-After, in seconds. A refused request still counts, so retrying before Retry-After only keeps you refused.
A very high request rate from one IP address can also be refused by Barrion's network edge, before it reaches the API. That 429 has no JSON body and no RateLimit-* headers. Treat it like any other 429: wait, then retry. Polling a scan or a pentest every 15 to 30 seconds stays well inside the limits.
Idempotency
POST /v1/pentests and POST /v1/scans need an Idempotency-Key header: any string of up to 255 visible characters, with no spaces. It is what lets you retry a start safely.
- The same key and the same body within 24 hours gets the first answer back, with the header
Idempotent-Replayed: true. Nothing is started or charged a second time. - The same key with a different body is refused with
422 IDEMPOTENCY_KEY_REUSED. - A retry while the first request is still running is refused with
409 IDEMPOTENCY_IN_PROGRESS. Wait a few seconds and send the same request again. - Only a success is remembered. If a start is refused, say for lack of credits, fix the cause and send the same request with the same key: it runs again, rather than repeating the refusal.
- If a start fails on our side with a
500, retry with the same key. If the run had already been created, you get that run back rather than a second one. The key is held for two minutes after the failure, so a retry inside that window is refused with409: wait and try again. Retry within a few minutes: a run whose start failed and was not retried is markedFAILEDafter about five minutes, and its credits are returned. A retry after that answers thatFAILEDrun. Nothing was charged for it, so start again with a new key.
Body fields are compared regardless of their order, so a client that rebuilds the same request is still recognised.
In CI, build the key from something that stays the same when a run is retried:
- Pentests: use the pipeline's run id alone, for example
pentest-$GITHUB_RUN_ID. Re-running a failed workflow run then gets the same pentest back instead of starting, and paying for, a second one. A new scheduled or manual run has a new run id and starts a fresh pentest. Do not add$GITHUB_RUN_ATTEMPT: GitHub increases it on every re-run, so each re-run would send a new key. - Scans: the attempt number is fine to include, since scans are free.