Errors
HTTP status codes, JSON error shapes, response headers, and when it's safe to retry.
Chuger returns standard HTTP status codes. Errors are always JSON, with a stable shape you can pattern-match in your client.
Common status codes
| Status | When it happens |
|---|---|
200 OK | Successful request |
202 Accepted | Async job accepted (only /v1/content/bulk) |
401 Unauthenticated | Missing or invalid API token |
402 Payment Required | No active plan or insufficient credits |
404 Not Found | Path doesn't exist, or an account-scoped resource was not found |
422 Unprocessable Entity | Validation failed (bad URL, missing fields, etc.) |
429 Too Many Requests | Rate limit or monthly quota exceeded |
503 Service Unavailable | Request couldn't be fulfilled |
Error shapes
Validation errors — 422
The standard validation shape:
{
"message": "The url field is required.",
"errors": {
"url": ["The url field is required."]
}
}
Authentication errors — 401
{
"message": "Unauthenticated."
}
Plan / payment errors — 402
{
"error": "Plan Required",
"message": "Access to 'advanced_api' requires a higher subscription plan."
}
{
"error": "Insufficient Credits",
"message": "Insufficient credits. Required: 10, Available: 4",
"code": "INSUFFICIENT_CREDITS"
}
For bulk endpoints, the body also includes a details object:
{
"error": "Insufficient Credits",
"message": "You do not have enough credits for this bulk operation.",
"code": "INSUFFICIENT_CREDITS",
"details": {
"required": 200,
"available": 50,
"url_count": 100
}
}
The stable code: "INSUFFICIENT_CREDITS" is the most reliable signal to detect — use it to trigger a top-up flow.
Rate limit errors — 429
{
"error": "Rate limit exceeded",
"message": "Too many requests for content endpoint. Rate limit will reset at 2026-05-13 14:01:00 UTC.",
"details": {
"endpoint": "content",
"limit": 15,
"remaining": 0,
"reset_time": 1731600060,
"reset_date": "2026-05-13 14:01:00 UTC"
},
"retry_after": 24
}
{
"error": "Quota exceeded",
"message": "Monthly quota exceeded. Quota will reset at 2026-06-01 00:00:00 UTC. Consider upgrading your plan for higher quotas.",
"details": {
"quota_type": "monthly",
"quota": 1800,
"remaining": 0,
"current_usage": 1800,
"percentage_used": 100,
"reset_time": 1733011200,
"reset_date": "2026-06-01 00:00:00 UTC"
}
}
Upstream / fulfilment errors — 503
When Chuger genuinely couldn't fulfill a request — for example, the target site refused us across every fallback path — you get a plain 503:
{
"message": "Failed to scrape the URL using available services."
}
No credits are deducted on 503 responses. You can safely retry after a short delay.
Useful headers on errors
429 responses include the same X-RateLimit-* and X-Monthly-* headers documented in Rate limits & quotas, plus Retry-After in seconds when applicable.
Handling errors in code
const res = await fetch(url, { headers });
if (res.status === 429) {
const retryAfter = Number(res.headers.get('Retry-After') ?? 30);
await sleep(retryAfter * 1000);
return retry();
}
if (res.status === 402) {
const body = await res.json();
if (body.code === 'INSUFFICIENT_CREDITS') {
notifyOps('Out of credits — top up at chuger.com');
}
throw new Error(body.message);
}
if (!res.ok) {
throw new Error(`Chuger ${res.status}: ${await res.text()}`);
}
When to retry
| Status | Safe to retry? |
|---|---|
429 | Yes, after Retry-After seconds |
503 | Yes, with backoff (failed services may recover) |
5xx | Yes, with backoff |
401, 402, 403, 404, 422 | No — fix the request first |