Errors

The consumer API uses a small set of HTTP status codes and a uniform error body. Build your client around the status code first; the message is for humans reading logs.

Error body

Errors return a single-field JSON object:

{
  "message": "Invalid or missing API key."
}

The message is intentionally short and stable. Do not parse it; switch on the HTTP status instead.

Status codes

StatusWhen you’ll see it
200 OKSuccess.
401 UnauthorizedAPI key missing, malformed, or revoked. Re-check headers and key status.
404 Not FoundThe requested released blog does not exist in your workspace, or has not been released.
500 Internal Server ErrorUnexpected failure on our side. Retry with backoff.

Handling 401

A 401 always means one of:

  • No x-api-key and no Authorization: Bearer header was sent.
  • The key was sent but has been revoked from the dashboard.
  • The key never existed (typo, swapped prefix, copied from a different environment).

Re-check the key in Workspace settings → API keys. The dashboard shows the prefix of every active key for visual confirmation.

Handling 404

A 404 from GET /api/consumer/blogs/{blogId} means the blog ID does not correspond to a released blog in the workspace bound to your key. The blog may exist in a different workspace, may still be in draft, or may have been removed. Cross-check with GET /api/consumer/blogs to discover currently released IDs.

Retrying

The list and get endpoints are pure reads, so retries are safe. A simple exponential backoff is plenty:

async function getWithRetry(url: string, headers: Record<string, string>) {
  for (let attempt = 0; attempt < 4; attempt += 1) {
    const response = await fetch(url, { headers });
    if (response.status < 500) return response;
    await new Promise((resolve) => setTimeout(resolve, 250 * 2 ** attempt));
  }
  throw new Error("Essel API unavailable after 4 attempts.");
}

Do not retry 401 or 404 — they will not resolve themselves.

Rate limits

There is no fixed rate limit today, but the API is sized for periodic syncs (every few minutes), not per-request fan-out from end users. If you need to serve high-traffic reader pages, cache responses on your side rather than passing every page view through to this API.