Skip to content

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.

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
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

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"
}

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"
}
{
"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"
}
{
"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"
}

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"
}
{
"error": "unauthorized",
"message": "No API key provided. Set the Authorization: Bearer <key> header.",
"docs": "https://og-engine.com/quick-start#step-1"
}
{
"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"
}
{
"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"
}
{
"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.

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.

  1. Check X-RateLimit-Reset to know when the quota resets
  2. Cache the reset time and surface a user-friendly message in your UI
  3. Consider upgrading your plan for higher limits
  4. 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.

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.