Skip to content

Reference

Markdown API errors

Every failure from the Markdown Viewer API returns the same predictable JSON envelope with a stable error.code, an appropriate HTTP status, and an optional details field for structured information. Build resilient integrations by switching on the code, not the message.

Error response shape

All errors — whether validation, authentication, rate limiting, or server-side — serialise identically:

{
  "error": {
    "code": "bad_request",
    "message": "Invalid request body.",
    "details": {
      "fieldErrors": {
        "markdown": [
          "Required"
        ]
      }
    }
  }
}
  • code — stable identifier; switch on this.
  • message — human-readable; don't pattern-match against it.
  • details — optional structured payload (eg. field-level validation errors).

Defensive error handling

A robust client checks the HTTP status, parses the JSON envelope, and decides whether to retry, surface a UI error, or escalate:

async function call(path: string, body: unknown, apiKey: string) {
  const res = await fetch(`https://trymarkdownviewer.com${path}`, {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${apiKey}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  const payload = await res.json();
  if (!res.ok) {
    const code = payload?.error?.code ?? "unknown_error";
    // Switch on the stable code, not res.status alone.
    switch (code) {
      case "rate_limited":
      case "quota_exceeded":
        // Honour Retry-After or upgrade plan.
        throw new Error(`Rate limited: ${code}`);
      case "unauthorized":
        // Force a key refresh / logout.
        throw new Error("Auth failed");
      default:
        throw new Error(`${code}: ${payload?.error?.message}`);
    }
  }
  return payload.data;
}

Error code reference

Every error you can see, in one table. The codes are sorted client → rate → server.

bad_request
HTTP 400
Client

Invalid request body

The request body is invalid, missing a required field, or not valid JSON.

How to fix

  • Set `Content-Type: application/json` on every request.
  • Make sure your payload is valid JSON (no trailing commas, properly quoted strings).
  • Check the `details.fieldErrors` object for the exact field that failed validation.
unauthorized
HTTP 401
Client

Missing or invalid API key

Your API key is missing, malformed, revoked, or doesn't exist.

How to fix

  • Add `Authorization: Bearer <YOUR_API_KEY>` to the request headers.
  • Confirm the key isn't revoked — check the API Keys page in your dashboard.
  • Make sure you copied the entire key (they're 36+ characters after the prefix).
forbidden
HTTP 403
Client

Not allowed on this plan

The action isn't permitted for your current plan or account.

How to fix

  • Check whether the endpoint requires a paid plan.
  • Upgrade your plan from the billing page if you've hit a plan-only limit.
not_found
HTTP 404
Client

Resource not found

The endpoint or resource you requested doesn't exist.

How to fix

  • Double-check the path — every API route is under `/api/v1/*`.
  • Make sure you're not hitting a typo'd path like `/api/v1/renderer`.
method_not_allowed
HTTP 405
Client

Wrong HTTP method

The endpoint exists but doesn't accept the HTTP method you used.

How to fix

  • All processing endpoints use `POST`. Send a `POST` request, not `GET`.
  • Check the docs page for the endpoint to confirm the expected verb.
payload_too_large
HTTP 413
Client

Markdown body is too big

Your request body exceeds the 256 KiB per-request limit.

How to fix

  • Split very long documents into smaller chunks and render them separately.
  • Trim oversized inline base64 images out of the markdown before sending.
unsupported_media_type
HTTP 415
Client

Wrong content type

`Content-Type` must be `application/json`.

How to fix

  • Set `Content-Type: application/json` on every API request.
  • Don't send `multipart/form-data` — wrap the markdown in JSON instead.
rate_limited
HTTP 429
Rate limit

Burst rate limit hit

You sent more than 20 requests in 10 seconds with this key.

How to fix

  • Slow down and add a small delay between rapid loops.
  • Honour the `Retry-After` response header before retrying.
  • Batch requests where possible, or use exponential backoff with jitter.
quota_exceeded
HTTP 429
Rate limit

Plan quota exhausted

Your plan's daily (or monthly) request quota has been used up.

How to fix

  • Wait for the quota window to reset — see `X-RateLimit-Reset`.
  • Upgrade your plan from the billing page for a higher quota.
timeout
HTTP 504
Server

Processing timed out

The request took longer than the per-request CPU budget (1 second).

How to fix

  • Try a smaller markdown document.
  • Avoid pathological inputs (eg. thousands of nested lists).
  • If you hit this consistently, please open a GitHub issue with a repro.
internal_error
HTTP 500
Server

Something broke on our side

An unexpected error occurred while processing the request.

How to fix

  • Retry the request after a short delay.
  • If it keeps happening, please report the issue with the timestamp.
network_error
HTTP n/a
Client

Network error

The browser couldn't reach the API at all — DNS, CORS, or offline.

How to fix

  • Check that you're online and not blocked by an ad/script blocker.
  • If you're calling from a different origin, ensure CORS isn't being blocked.
  • Inspect the browser DevTools network tab for the underlying cause.
parse_error
HTTP n/a
Client

Response wasn't valid JSON

The server returned a response that couldn't be parsed as JSON.

How to fix

  • This usually indicates a network-level failure (eg. proxy, captive portal).
  • Retry the request — if it keeps happening, please report it.

FAQ

What does the error.code field mean?
It's a stable, machine-readable identifier for the failure (eg. unauthorized, rate_limited, payload_too_large). Build your conditionals against the code — the human-readable error.message can change between versions without warning.
How do I tell a burst rate limit from a quota exhaustion?
Both return HTTP 429, but error.code differs. rate_limited means you tripped the per-key 20 req / 10s burst limit. quota_exceeded means your plan's daily or monthly quota has been used up — wait for X-RateLimit-Reset or upgrade.
When should I retry a failed request?
Retry idempotent 5xx errors (internal_error, timeout) with exponential backoff. Always honour the Retry-After header on 429 responses. Don't retry 4xx errors — they're client-side and won't fix themselves.
Are error responses still rate-limited?
Failed requests count toward burst limits but do not consume your plan quota — bad_request, unauthorized, and validation errors won't drain your daily allowance.
How do I get more context on a validation failure?
When the error code is bad_request, look at the details.fieldErrors object. It's a map of field name → array of validation messages so you can show the user a precise inline error.

Next steps