Error Handling

Error response format and common error codes.

All Unmarkdown™ API errors return a consistent JSON envelope. This makes it straightforward to handle errors programmatically regardless of the endpoint.

Error Response Format

Every error response follows this structure:

{
  "error": {
    "code": "error_code",
    "message": "Human-readable description of the problem",
    "status": 400
  }
}
FieldTypeDescription
codestringMachine-readable error code for programmatic handling
messagestringHuman-readable description. Safe to display to users.
statusnumberHTTP status code (matches the response status)

Error Codes

StatusCodeDescriptionSuggested Action
400invalid_bodyMalformed JSON in request bodyCheck your JSON syntax
400validation_errorInvalid parameter value or missing required fieldCheck the field requirements for this endpoint
400content_rejectedContent flagged by abuse scanner (demo publish only)Review content for phishing or spam patterns
401unauthorizedMissing, invalid, or revoked API keyCheck your API key is correct and active
403forbiddenFeature requires Pro tierUpgrade to Pro or remove the Pro-only parameter
403quota_exceededFree tier document limit (3) reachedUpgrade to Pro for unlimited documents
404not_foundResource does not exist or does not belong to youVerify the resource ID
409conflictSlug already taken by another documentChoose a different slug
410expiredDemo page has expired (past 72 hours)Demo pages cannot be claimed after expiration
429rate_limitedToo many requests per secondWait and retry with backoff
429quota_exceededMonthly API quota exhaustedWait for monthly reset or upgrade to Pro
500internal_errorUnexpected server errorRetry the request. If persistent, contact support.
500render_errorFailed to render markdown to HTMLCheck your markdown for syntax issues

Handling Errors in Code

const res = await fetch('https://api.unmarkdown.com/v1/documents/publish', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer um_your_key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ content: '# Hello' })
});

if (!res.ok) {
  const { error } = await res.json();

  switch (error.code) {
    case 'unauthorized':
      console.error('Check your API key');
      break;
    case 'rate_limited':
      console.error('Slow down, retrying...');
      // implement backoff
      break;
    case 'quota_exceeded':
      console.error(`Quota exceeded: ${error.message}`);
      break;
    case 'validation_error':
      console.error(`Invalid request: ${error.message}`);
      break;
    default:
      console.error(`API error: ${error.message}`);
  }
}
import requests

res = requests.post(
    'https://api.unmarkdown.com/v1/documents/publish',
    headers={'Authorization': 'Bearer um_your_key'},
    json={'content': '# Hello'}
)

if not res.ok:
    error = res.json()['error']

    if error['code'] == 'rate_limited':
        print('Rate limited, retrying...')
    elif error['code'] == 'quota_exceeded':
        print(f"Quota exceeded: {error['message']}")
    else:
        print(f"Error {error['status']}: {error['message']}")

Best Practices

Always check the code field. Do not rely on HTTP status codes alone, since multiple error types can share the same status code (for example, both rate_limited and quota_exceeded return 429).

Display the message to users. Error messages are written to be human-readable and safe to show in a UI.

Retry on 429 and 500. Rate limit errors and server errors are transient. Implement retry logic with exponential backoff. Do not retry 400, 401, 403, or 404 errors, as those require a change to the request.

TIP

The status field inside the error envelope always matches the HTTP response status code. This is useful when your HTTP library does not expose the status code directly.