Error Codes & HTTP Status Reference
Every error response is JSON with this structure:
{
"error": {
"code": "error_code",
"message": "Human-readable message"
}
}
Billing: Errors are never billed (failures and cache hits have zero cost). Only successful responses (status=200, cache miss) are billable.
400 — Bad Request (invalid_params)
The request parameters are invalid.
Billed: No
Causes:
titleis missing or exceeds 200 characterstemplateis not one of:basic,gradient,blog,productthemeis notlightordarkworhare outside 100–2400 or 100–1260 rangeformatis notpng,webp,avif, orsvg(for/og) orpng,webp,jpeg(for/screenshot)expiresis not a valid unix timestamp- Required parameter is missing (e.g.,
urlfor/screenshot)
Example response:
{
"error": {
"code": "invalid_params",
"message": "title must be between 1 and 200 characters"
}
}
How to fix: Validate all parameters on the client before sending. Check the API reference for allowed values and limits.
401 — Unauthorized (invalid_key)
The API key is missing, malformed, or revoked.
Billed: No
Causes:
Authorization: Bearer ...header is missing or malformed (must beBearer sk_live_...)- The key does not exist (typo, never issued)
- The key has been revoked with
POST /v1/keys
Example response:
{
"error": {
"code": "invalid_key",
"message": "API key not found or revoked"
}
}
How to fix: Check that your Authorization header is exactly Bearer sk_live_<40 hex chars>. If the key was lost, generate a new one.
402 — Payment Required (quota_exceeded)
Free tier render quota exceeded.
Billed: No
Causes:
- Free tier account has exceeded 500 renders in the current calendar month
- Upgrade to PAYG to lift the limit
Example response:
{
"error": {
"code": "quota_exceeded",
"message": "Free tier quota of 500 renders/month exceeded"
}
}
How to fix: Upgrade to pay-as-you-go in your dashboard, or wait until the next month.
403 — Forbidden (invalid_signature or expired)
The request signature is invalid or the signature has expired.
Billed: No
Causes (invalid_signature):
sigparameter does not match the expected HMAC-SHA256- Parameters were modified after signing
- Using an old or wrong
SIGNING_SECRET - URL encoding mismatch during signing vs. transmission
Causes (expired):
expirestimestamp is in the past- Current server time >
expiresunix seconds
Example response:
{
"error": {
"code": "invalid_signature",
"message": "Signature verification failed"
}
}
How to fix:
- Double-check
SIGNING_SECRET— it must match the key shown in your dashboard - Verify the signing algorithm: sort params alphabetically, URL-encode, join with
&, then HMAC-SHA256 - If
expiresis set, ensure it's a future unix timestamp (e.g.,Math.floor(Date.now() / 1000) + 3600for 1 hour from now) - Check that no URL parameters were modified after signing
Use the Quickstart signing example as a reference.
422 — Unprocessable Entity (render_failed)
The render engine encountered an error.
Billed: No (failures are never billed)
Causes:
- Invalid CSS in the template (Satori only supports flexbox)
- Invalid template HTML structure
- Font loading failed (rare)
- Out-of-memory (very large image dimensions with complex CSS)
Example response:
{
"error": {
"code": "render_failed",
"message": "Satori render failed: unsupported CSS property 'grid'"
}
}
How to fix:
- Use the Playground to test your template live
- Ensure CSS uses flexbox only (no grid, no calc(), no custom properties)
- Simplify the template — remove complex CSS, animations, or remote images
- Check that all fonts are bundled or passed as base64 data URIs
See OG Reference — Satori Constraints.
429 — Too Many Requests (rate_limited)
Rate limit exceeded.
Billed: No
Limits:
- Free tier: 60 requests/minute per IP or API key
- Paid tier (PAYG): 600 requests/minute per API key
Response includes Retry-After header with seconds to wait.
Example response:
{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded: 60 requests per minute"
}
}
Headers:
Retry-After: 30
How to fix:
- Wait the number of seconds in
Retry-After - Implement exponential backoff: wait, retry, and double the wait on next limit
- Batch requests efficiently — use caching; don't re-render the same image twice
- Upgrade to PAYG for 10x higher limits (600/min instead of 60/min)
500 — Internal Server Error (internal)
Unexpected server error.
Billed: No (failures are never billed)
Causes:
- Satori or resvg wasm module crashed (very rare)
- Database connection failed (very rare)
- Cloudflare infrastructure issue (transient)
Example response:
{
"error": {
"code": "internal",
"message": "Internal server error"
}
}
Stack traces are never sent to clients.
How to fix:
- Retry with exponential backoff
- Check status.renderog.app for incidents
- Email support@renderog.app if the error persists
501 — Not Implemented (not_available)
Feature not yet available.
Billed: No
Causes:
- Calling
/screenshotbefore Phase 5 (Browser Rendering) is enabled - Requesting an unsupported format like WebP on an older version
Example response:
{
"error": {
"code": "not_available",
"message": "Screenshot API is coming soon (Phase 5)"
}
}
How to fix: Wait for the feature to be enabled, or use /og (currently live).
Summary Table
| HTTP | Code | Billed? | Retry? | Cause |
|---|---|---|---|---|
| 400 | invalid_params |
No | No | Bad input |
| 401 | invalid_key |
No | No | Missing/revoked API key |
| 402 | quota_exceeded |
No | No | Upgrade or wait |
| 403 | invalid_signature |
No | No | Wrong signature or expired |
| 403 | expired |
No | No | Signature timestamp past |
| 422 | render_failed |
No | Yes (maybe) | CSS/HTML error in template |
| 429 | rate_limited |
No | Yes (with backoff) | Too many requests |
| 500 | internal |
No | Yes (backoff) | Server error |
| 501 | not_available |
No | No | Feature not live yet |
Best Practices
- Always check the HTTP status — your HTTP client library will bubble up 4xx/5xx; don't silently ignore them
- Parse
error.codefor programmatic handling (e.g., show users a message, retry, or escalate) - Use
Retry-Afterwhen retrying 429 errors - Never retry 400, 401, 402, 403 — these need manual intervention
- Log errors with context — save the request URL, parameters (excluding secrets), and error response for debugging
See Quickstart and OG Reference for working examples.