Skip to main content
Every failure is JSON with an error field holding one sentence. Nothing in it is a stack trace, and nothing in it is safe to parse for meaning — read the status code, log the sentence.

Status codes

The JSON did not parse, a required field is missing, or a value is outside its bounds. error names the first problem found rather than listing them all.Common causes: posting {"server_count": "1482"} as a string, a command name over 32 characters, or /check without a userId.
No Authorization header, a header we cannot read, or a token that is not in our database. Rotating a token invalidates the old one immediately, so a sudden 401 on a bot that worked yesterday usually means somebody generated a new one.
Also what you get for a bot id that is not a Discord snowflake. It is deliberately the same answer in all three cases; see Authentication.
The body carries retryAfter, in seconds. Wait that long; retrying immediately only spends the same budget again, because the limit is keyed on your token rather than on your IP.
The request was fine and we could not write it. Retry with backoff. GET https://discordbotlist.lol/api/health answers ok or not without a credential, which is the quickest way to tell a fault on our side from one in your integration.

Backing off

A working pattern, and the one we would write ourselves:
Do not retry a 400 or a 401. Neither will succeed on a second attempt, and a bot retrying a malformed body on a loop is how a token ends up rate limited for a reason its owner cannot see.

Limits, and why each is what it is

If our rate-limit store is unreachable the limiter fails open: your requests go through. A cache outage should not stop a running bot from checking in, and the token is still verified either way.