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"
}
FieldTypeDescription
errorstringMachine-readable error code
messagestringHuman-readable description
detailsobjectAdditional context (field name, allowed values, etc.) — may be absent
docsstringDirect link to the relevant docs section
HTTP StatusCodeWhen it occurs
400invalid_requestThe request body is malformed JSON or has an unexpected structure
400missing_fieldA required field (format, title) is absent
400invalid_fontThe style.font value is not one of the supported fonts
400invalid_formatThe format value is not one of og, twitter, square, linkedin, story
400invalid_fileThe uploaded background image is corrupt, unsupported format, or exceeds 5MB
401unauthorizedThe Authorization header is missing
401unauthorizedThe API key is invalid or does not exist
401unauthorizedThe API key has been revoked
402plan_requiredThe requested feature (WebP output, batch rendering) requires a paid plan
429rate_limitedYour plan’s monthly render quota has been exceeded
500server_errorAn 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:

HeaderExampleDescription
X-RateLimit-Limit500Total monthly render quota for your plan
X-RateLimit-Remaining347Renders left this month
X-RateLimit-Reset1735689600Unix 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.