Error Reference
Every error code the API returns, what caused it, and exactly how to fix it. Every JSON body below is verbatim from the production handler.
All error responses include an X-Request-Id header. Include it when contacting hello@chart-output.com.
400 Bad Request
The request body is not valid JSON, or the chart spec fails schema validation. The details array always identifies the exact failing field — no guessing required.
Invalid chart specification (Zod validation failure)
{
"error": "Invalid chart specification",
"details": [
{ "path": "data.labels", "message": "At least 1 label is required" },
{ "path": "width", "message": "Number must be less than or equal to 4000" }
]
}Malformed JSON in request body
{ "error": "Invalid JSON in request body" }URL too long (GET endpoint only)
{
"error": "URL too long",
"message": "URL exceeds 2000 characters. Use POST for complex charts."
}Reading details[].path
Each path uses dot notation and maps directly to a key in your JSON spec. Array indices appear as numbers.
| path value | Meaning |
|---|---|
| type | Root-level chart type; must be line, bar, pie, doughnut, radar, or polarArea |
| data.labels | The labels array — must have 1–1000 entries |
| data.datasets.0.data | data array of the first dataset (index 0) |
| width / height | Root-level dimensions; must be integers in the range 100–4000 |
| (empty string) | Error is at the root of the spec — usually a missing required field |
Common causes
- Missing
typeordata— both are always required - Invalid chart type or format string — values are case-sensitive (
"Line"fails,"line"works) - Dimensions outside 100–4000 px, or
scaleoutside 1–4 - Empty
data.labelsordata.datasets, or more than 1000 labels / 20 datasets / 1000 data points per dataset - Trailing commas or single-quoted strings — standard JSON only
- Non-HTTPS
header.logoUrl— only HTTPS URLs are accepted - Invalid base64 encoding in the
chartorcGET parameter
Fix
Read details[].path and details[].message. Every field name is a direct reference to your spec JSON. The Chart Studio playground highlights the offending key inline.
401 Unauthorized
An Authorization header was present but the key was invalid, malformed, or revoked. Requests with no auth at all return 200 at anonymous/starter limits — not 401.
Invalid or revoked API key
{ "error": "Invalid API key" }Malformed Authorization header
The header was present but did not start with Bearer (with the trailing space).
{ "error": "Invalid Authorization format. Use: Bearer <api_key>" }Fix
| Cause | Fix |
|---|---|
| Typo in key | Copy the key exactly from API Keys in your dashboard |
| Wrong prefix format | Header must be Authorization: Bearer pk_live_… (note the space) |
| Key was deleted | Create a new key in the dashboard |
Using a pk_test_ key against production data | Sandbox keys work for renders; they are not restricted by environment |
403 Forbidden
The API key is valid but the operation is not permitted for the current plan. Check plan and upgradeUrl in the response body.
Subscription required (trial expired or account lapsed)
{
"error": "Subscription required. Please upgrade to continue.",
"plan": "expired",
"upgradeUrl": "/app/usage-billing"
}Trial plan — non-PNG format requested
Trial accounts are restricted to PNG output only.
{
"error": "Trial includes PNG output only. Upgrade for WebP, SVG, JPEG, and PDF.",
"plan": "trial",
"upgradeUrl": "/pricing"
}PDF output — Business plan required
PDF output requires the Business plan (pro or business internally).
{
"error": "PDF output requires the Business plan",
"plan": "starter",
"upgradeUrl": "/pricing"
}Fix
| Cause | Fix |
|---|---|
| Trial + non-PNG format | Use "format": "png", or upgrade |
| Expired subscription | Renew or upgrade at /app/usage-billing |
| PDF on Starter/Pro tier | Upgrade to Business, or use PNG + embed in a PDF library (see the PDF guide) |
429 Too Many Requests
Either per-window rate limiting or render quota exhaustion. The two are distinguished by the shape of the body.
Rate limit — 100 requests per 15 minutes
{
"error": "Rate limit exceeded",
"retryAfter": 120
}Headers also present on 429 rate-limit responses:
Retry-After— Seconds until the window resetsX-RateLimit-Limit— Maximum requests per window (100)X-RateLimit-Remaining— Requests remaining (0 on 429)X-RateLimit-Reset— ISO 8601 timestamp of when the window resets
Billing-period quota exceeded
{
"error": "Render quota exceeded for your billing period. Upgrade or wait for renewal.",
"currentUsage": 100000,
"limit": 100000,
"plan": "starter"
}Trial render cap reached (500 renders)
{
"error": "Trial render limit reached (500 renders). Upgrade to continue.",
"plan": "trial",
"upgradeUrl": "/app/usage-billing"
}Fix
| Cause | Fix |
|---|---|
| Rate limit (100/15 min) | Wait Retry-After seconds, or distribute requests over time |
| Billing quota | Upgrade plan or wait for next billing cycle |
| Trial cap (500) | Upgrade to Starter or higher at /app/usage-billing |
500 Internal Server Error
Server error during rendering. This is never caused by your spec — it means something went wrong on our side.
{ "error": "Internal server error" }Email hello@chart-output.com with the X-Request-Id from the response header and we will investigate.