REST API v1 · Developers
Errors and error codes
Errors always come back as JSON with a stable machine-readable error code and a human-readable message. Branch on error and the HTTP status — messages are for people and may change.
Developer docs
On this page
Error format
Every error response has the same shape: { "error": CODE, "message": "…" }. Some codes add fields that help you react — for example issues (what was invalid), required (the missing scope), balance, feature or retryAfter.
HTTP/1.1 400 Bad Request
{
"error": "INVALID_REQUEST",
"message": "Invalid body",
"issues": [{ "path": "body.amount", "message": "Number must be greater than or equal to 1" }]
}HTTP/1.1 409 Conflict
{ "error": "INSUFFICIENT_FUNDS", "message": "The member does not have enough to remove this amount", "balance": 40 }In issues, each path starts with the part of the request it refers to: params., query., body. or header..
All error codes
Each endpoint in the API reference lists the codes it can return. The full list:
| HTTP | Code | Description |
|---|---|---|
| 400 | INVALID_REQUEST | Invalid IDs, query parameters, headers or body; issues lists what’s wrong |
| 400 | INVALID_JSON | The body isn’t valid JSON |
| 401 | UNAUTHORIZED | The Authorization header is missing or malformed |
| 401 | INVALID_API_KEY | The key doesn’t exist |
| 401 | API_KEY_REVOKED | The key was revoked in the console |
| 401 | API_KEY_EXPIRED | The key has expired |
| 402 | PLAN_LIMIT | The server’s plan doesn’t cover this: a limit is reached or a flag is missing |
| 403 | BROWSER_REQUESTS_NOT_ALLOWED | The request came from a browser (it has an Origin header) |
| 403 | GUILD_MISMATCH | The key belongs to another server |
| 403 | INSUFFICIENT_SCOPE | The key lacks the scope; required names it |
| 403 | GUILD_BLOCKED | The server is blocked from BotGG |
| 403 | SELF_DECISION | The API key’s creator can’t decide their own application or appeal |
| 403 | MODERATOR_NOT_MEMBER | The API key’s creator is no longer on the server (moderation acts in their name) |
| 403 | MODERATOR_MISSING_PERMISSION | The API key’s creator lacks the Discord permission for this action; required names it |
| 403 | HIERARCHY | The target can’t be moderated; reason says why (the key’s creator, a bot, the owner, or a role not below the creator’s or the bot’s) |
| 404 | NOT_FOUND | Unknown endpoint |
| 404 | TICKET_NOT_FOUND | No ticket with this number on the server |
| 404 | TRANSCRIPT_NOT_FOUND | The ticket has no saved transcript (still open, transcripts off, or deleted) |
| 404 | APPLICATION_NOT_FOUND | No application (or form) with this number or ID on the server |
| 404 | APPEAL_NOT_FOUND | No ban appeal with this number on the server |
| 404 | CASE_NOT_FOUND | No moderation case with this number on the server |
| 404 | MODERATION_ACTION_NOT_FOUND | No moderation action with this ID was made with this API key |
| 404 | BOT_NOT_IN_GUILD | BotGG isn’t on this server |
| 404 | MEMBER_NOT_FOUND | The user isn’t a member of the server |
| 404 | XP_CHANGE_NOT_FOUND | No XP change with this ID was made with this API key |
| 404 | SHOP_STORE_NOT_FOUND | No store with this ID on the server |
| 404 | SHOP_ITEM_NOT_FOUND | No Shop item with this ID on the server |
| 404 | SHOP_PURCHASE_NOT_FOUND | No purchase with this ID on the server |
| 404 | GIVEAWAY_NOT_FOUND | No giveaway with this ID on the server |
| 404 | FLOW_NOT_FOUND | No flow with this ID on the server |
| 404 | FLOW_EXECUTION_NOT_FOUND | No flow run with this ID on the server |
| 409 | FEATURE_DISABLED | The feature this endpoint uses is turned off on the server; feature names it |
| 409 | BANK_DISABLED | The Bank is turned off on this server |
| 409 | INSUFFICIENT_FUNDS | Not enough balance to remove; balance is the current one |
| 409 | SHOP_ITEM_UNAVAILABLE | The item can’t be bought or granted right now; reason says why (disabled, store closed, out of stock, limit, price changed, roles, level, role already owned) |
| 409 | SHOP_ALREADY_REFUNDED | The purchase was already refunded |
| 409 | SHOP_ITEM_USED | The item was already used; refund it with force: true |
| 409 | ROLE_NOT_GRANTABLE | The bot can’t give the item’s role (missing, above the bot, too powerful, or Discord refused); nothing was charged |
| 409 | GIVEAWAY_BAD_STATE | The giveaway’s status doesn’t allow this; status is the current one |
| 409 | FLOW_NOT_RUNNABLE | Only flows that start with a Manual or Webhook trigger can be run; triggerType is the flow’s trigger |
| 409 | FLOW_DISABLED | The flow is turned off |
| 409 | TICKET_CLOSED | The ticket is already closed |
| 409 | ALREADY_DECIDED | The application or appeal was already decided; status is the decision |
| 409 | NOT_BANNED | The user isn’t banned from the server |
| 409 | BOT_MISSING_PERMISSIONS | The bot lacks the Discord permission for this action; missing lists it |
| 413 | PAYLOAD_TOO_LARGE | The request body is too large |
| 415 | UNSUPPORTED_MEDIA_TYPE | The body isn’t sent as application/json |
| 422 | IDEMPOTENCY_KEY_REUSED | The idempotency key was already used for a different request |
| 422 | BOT_ACCOUNT | Bots can’t hold balances |
| 422 | BALANCE_LIMIT | The change would break the server’s balance limits |
| 429 | RATE_LIMITED | Too many requests; wait Retry-After seconds (also in the retryAfter field) |
| 500 | INTERNAL | Something went wrong on our side |
| 503 | SERVICE_UNAVAILABLE | Discord couldn’t be reached, or BotGG has temporarily paused the feature; retry later |
Which errors to retry
429 RATE_LIMITEDand503 SERVICE_UNAVAILABLE: wait the number of seconds inRetry-After(also inretryAfter), then retry.500 INTERNALand network errors: retry with backoff. For writes, send an Idempotency-Key so a retry never applies a change twice.- Other
4xxerrors won’t succeed by retrying the same request: fix the request, the key or the server settings first.