Error Handling
OG Engine returns structured JSON error responses with consistent shapes across all endpoints. This guide covers the error format, all error codes, rate limiting behavior, and recommended retry strategies.
Error Response Structure
Section titled “Error Response Structure”All errors return a JSON body with the following shape:
{ "error": "invalid_font", "message": "Font 'Comic Sans' is not available. See /health for the list of supported fonts.", "details": { "field": "style.font", "provided": "Comic Sans", "available": ["Outfit", "Inter", "Playfair Display", "Sora", "Space Grotesk", "JetBrains Mono", "Noto Sans JP", "Noto Sans AR"] }, "docs": "https://og-engine.com/api-reference/errors#invalid_font"}| Field | Type | Description |
|---|---|---|
error | string | Machine-readable error code |
message | string | Human-readable description |
details | object | Additional context (field name, allowed values, etc.) — may be absent |
docs | string | Direct link to the relevant docs section |
Error Code Reference
Section titled “Error Code Reference”| HTTP Status | Code | When it occurs |
|---|---|---|
| 400 | invalid_request | The request body is malformed JSON or has an unexpected structure |
| 400 | missing_field | A required field (format, title) is absent |
| 400 | invalid_font | The style.font value is not one of the supported fonts |
| 400 | invalid_format | The format value is not one of og, twitter, square, linkedin, story |
| 400 | invalid_file | The uploaded background image is corrupt, unsupported format, or exceeds 5MB |
| 401 | unauthorized | The Authorization header is missing |
| 401 | unauthorized | The API key is invalid or does not exist |
| 401 | unauthorized | The API key has been revoked |
| 402 | plan_required | The requested feature (WebP output, batch rendering) requires a paid plan |
| 429 | rate_limited | Your plan’s monthly render quota has been exceeded |
| 500 | server_error | An unexpected internal error occurred on the server |
invalid_request (400)
Section titled “invalid_request (400)”Returned when the request body cannot be parsed as valid JSON, or when the top-level structure is wrong (e.g., sending an array instead of an object).
{ "error": "invalid_request", "message": "Request body must be a JSON object.", "docs": "https://og-engine.com/api-reference/render"}missing_field (400)
Section titled “missing_field (400)”Returned when a required field is absent. The details.field property tells you exactly which field is missing.
{ "error": "missing_field", "message": "The 'format' field is required.", "details": { "field": "format", "allowed": ["og", "twitter", "square", "linkedin", "story"] }, "docs": "https://og-engine.com/api-reference/errors#missing_field"}invalid_font (400)
Section titled “invalid_font (400)”{ "error": "invalid_font", "message": "Font 'Roboto' is not available.", "details": { "field": "style.font", "provided": "Roboto", "available": ["Outfit", "Inter", "Playfair Display", "Sora", "Space Grotesk", "JetBrains Mono", "Noto Sans JP", "Noto Sans AR"] }, "docs": "https://og-engine.com/api-reference/errors#invalid_font"}invalid_format (400)
Section titled “invalid_format (400)”{ "error": "invalid_format", "message": "Format 'facebook' is not supported.", "details": { "field": "format", "provided": "facebook", "allowed": ["og", "twitter", "square", "linkedin", "story"] }, "docs": "https://og-engine.com/api-reference/errors#invalid_format"}invalid_file (400)
Section titled “invalid_file (400)”Returned when a background image upload is rejected.
{ "error": "invalid_file", "message": "Image file exceeds the 5MB size limit.", "details": { "field": "image", "maxSizeBytes": 5242880 }, "docs": "https://og-engine.com/api-reference/errors#invalid_file"}unauthorized (401)
Section titled “unauthorized (401)”{ "error": "unauthorized", "message": "No API key provided. Set the Authorization: Bearer <key> header.", "docs": "https://og-engine.com/quick-start#step-1"}plan_required (402)
Section titled “plan_required (402)”{ "error": "plan_required", "message": "WebP output requires a Starter plan or above.", "details": { "feature": "webp_output", "requiredPlan": "starter", "upgradeUrl": "https://og-engine.com/pricing" }, "docs": "https://og-engine.com/api-reference/errors#plan_required"}rate_limited (429)
Section titled “rate_limited (429)”{ "error": "rate_limited", "message": "Monthly render quota exceeded. Resets at next billing cycle.", "details": { "limit": 500, "used": 500, "resetAt": "2026-05-15T00:00:00Z", "upgradeUrl": "https://og-engine.com/pricing" }, "docs": "https://og-engine.com/api-reference/errors#rate_limited"}server_error (500)
Section titled “server_error (500)”{ "error": "server_error", "message": "An unexpected error occurred. Please try again.", "requestId": "req_abc123"}Include the requestId when contacting support — it lets us find your request in server logs.
Rate Limiting Headers
Section titled “Rate Limiting Headers”Every response from authenticated endpoints includes rate limiting headers, even when the request succeeds:
| Header | Example | Description |
|---|---|---|
X-RateLimit-Limit | 500 | Total monthly render quota for your plan |
X-RateLimit-Remaining | 347 | Renders left this month |
X-RateLimit-Reset | 1735689600 | Unix timestamp (seconds) when quota resets |
Read these on every successful response so you can warn your users or pause generation before hitting the limit.
What to Do When Rate Limited
Section titled “What to Do When Rate Limited”- Check
X-RateLimit-Resetto know when the quota resets - Cache the reset time and surface a user-friendly message in your UI
- Consider upgrading your plan for higher limits
- If you need temporary burst capacity, contact support at support@og-engine.com
The /validate endpoint is not metered and does not consume your render quota. Use it freely even when you are near your limit.
Retry Strategy for 5xx Errors
Section titled “Retry Strategy for 5xx Errors”Server errors (500) are rare but can occur during deployments or transient infrastructure issues. Recommended retry strategy:
async function renderWithRetry(payload: object, maxRetries = 3) { for (let attempt = 0; attempt < maxRetries; attempt++) { const res = await fetch('https://og-engine.com/render', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.OG_ENGINE_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify(payload), })
if (res.ok) return await res.arrayBuffer()
const error = await res.json()
// Do not retry client errors (4xx) if (res.status >= 400 && res.status < 500) { throw new Error(`${error.error}: ${error.message}`) }
// Exponential backoff for 5xx: wait 200ms, 400ms, 800ms if (attempt < maxRetries - 1) { await new Promise((resolve) => setTimeout(resolve, 200 * 2 ** attempt)) } }
throw new Error('Max retries exceeded')}Do not retry 4xx errors — they represent problems with your request that will not resolve by retrying.
Next Steps
Section titled “Next Steps”- Read the API Errors Reference for the complete errors table
- Understand Rate Limiting and Plans for quota details
- See Text Validation as a free alternative to trial-and-error rendering