Skip to Content
ReferenceErrors

Errors

All errors return JSON with a consistent shape:

{ "statusCode": 404, "code": "NOT_FOUND", "message": "Project not found" }

Common status codes

StatusCodeTypical cause
400BAD_REQUESTMalformed body or missing required field
400VALIDATION_ERRORBody passes parse but fails schema (e.g., url not a valid URL)
400MALFORMED_BODYJSON parse error
401UNAUTHORIZEDMissing or invalid JWT / API key
402TIER_LOCKEDImage was generated on a higher plan than your current subscription
403FORBIDDENNot the resource owner, or scoped API key targeting a different project
403PLAN_LIMIT_EXCEEDEDMonthly AI quota or project/domain limit reached
404NOT_FOUNDResource doesn’t exist (bad ID, unknown template slug)
409CONFLICTDuplicate resource (e.g., email already registered)
409IMAGE_EXISTSAn image already exists for the same (projectId, url, ...) — use force: true to regenerate
429Rate limit exceeded — honor Retry-After

Handling errors

const res = await fetch("https://api.ogstack.dev/images/generate", { method: "POST", headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" }, body: JSON.stringify({ url, projectId }), }); if (!res.ok) { const error = await res.json(); switch (error.code) { case "IMAGE_EXISTS": // Image already cached — fetch the existing one or pass force: true break; case "PLAN_LIMIT_EXCEEDED": // Monthly quota hit — upgrade or wait for reset break; case "TIER_LOCKED": // The image was generated on a higher plan; downgrade handling break; default: throw new Error(`${error.code}: ${error.message}`); } }

Retrying

Retry 429 (with Retry-After) and transient 5xx errors. Do not retry 4xx client errors — the request is malformed and will fail again.

For rate-limit-aware retry logic, exponential backoff capped by Retry-After is the right default:

async function fetchWithBackoff(req: () => Promise<Response>, maxAttempts = 5): Promise<Response> { for (let attempt = 1; attempt <= maxAttempts; attempt++) { const res = await req(); if (res.ok || (res.status < 500 && res.status !== 429)) { return res; } const retryAfter = Number(res.headers.get("retry-after") ?? 0); const backoffMs = Math.max(retryAfter * 1000, 2 ** attempt * 100); await new Promise((r) => setTimeout(r, backoffMs)); } throw new Error("Max retries exceeded"); }