Errors and limits
Status codes, error bodies, monthly quotas, per-minute limits and how to retry the API safely.
Errors return a JSON body with a human-readable message, and the request id when there is one.
{ "error": "No chessboard was detected in the image.", "requestId": "4b2c5e1a-8d0f-4f1e-9a53-0b6c2f7d1a90" }
Status codes
| Status | Meaning | What to do |
|---|---|---|
200 |
Recognized. | |
400 |
No image in the request, or the file cannot be decoded. |
Fix the request. |
401 |
Missing, invalid or revoked API key. | Send X-API-Key; create a new key if needed. |
413 |
The file is over 12 MB, or the image is over 40 megapixels. | Downscale and resend. A board only needs about 20 px per square. |
415 |
The upload is not an image. | Send PNG, JPEG or WebP. |
422 |
No chessboard was found in the image. | Crop closer, or pass bbox if you know where the board is. |
429 |
Monthly quota used up, or too many requests per minute. | Wait for the reset in X-RateLimit-Reset, or upgrade. |
503 |
Workers are starting or the queue is full. | Retry with backoff. |
504 |
Recognition timed out (30 s). | Retry once; send a smaller image. |
Quotas
Quota is per account, counted per calendar month in UTC, and shared by all your keys. Every authenticated request that carries an image counts as one, including a request that then fails with 422 or a 5xx. Requests rejected for a bad key, a missing file or a file that is too large or not an image do not count.
| Plan | Requests a month |
|---|---|
| API Free | 500 |
| API Starter | 10,000 |
| API Growth | 100,000 |
| API Scale | 1,000,000 |
At the limit the API answers 429 with the quota in the body and in the X-RateLimit-* headers. Nothing is billed beyond your plan.
On top of the monthly quota there is a burst limit per client IP address, currently 60 requests a minute. It returns 429 with Too many requests; slow down. If you need more throughput, write to us.
Retrying
Retry 503 and 504 with exponential backoff. Do not retry 400, 401, 413, 415 or 422: the same request fails the same way.
async function recognize(form, tries = 4) {
for (let attempt = 0; attempt < tries; attempt++) {
const response = await fetch('https://api.chessglance.com/v1/recognize', { method: 'POST', headers: { 'X-API-Key': process.env.API_KEY }, body: form });
if (response.ok) return response.json();
// 503 and 504 are safe to retry; everything else needs a fix on your side.
if (![503, 504].includes(response.status)) throw new Error((await response.json()).error);
await new Promise((resolve) => setTimeout(resolve, 500 * 2 ** attempt));
}
throw new Error('Recognizer unavailable');
}import time
def recognize(path, tries=4):
for attempt in range(tries):
with open(path, "rb") as image:
response = requests.post(URL, headers=HEADERS, files={"image": image}, timeout=60)
if response.ok:
return response.json()
if response.status_code not in (503, 504):
raise RuntimeError(response.json()["error"])
time.sleep(0.5 * 2 ** attempt)
raise RuntimeError("Recognizer unavailable")Data handling
Uploaded images are deleted when the request finishes. We log the request id, status, number of boards, time taken and size, not the image. See the privacy policy.
Fair play
Recognition works on any page, but the API is for study, publishing and analysis. API customers agree not to use it to assist in a live game, and to pass the same rule on to their users. See the terms.