Skip to main content

Overview

The Ordinal API uses conventional HTTP response codes to indicate the success or failure of an API request. Codes in the 2xx range indicate success, codes in the 4xx range indicate an error with the provided information, and codes in the 5xx range indicate an error with Ordinal’s servers.

Error Response Format

All error responses follow a consistent JSON format:
{
  "code": "ERROR_CODE",
  "message": "A human-readable description of the error"
}
For validation errors, additional field-level details are included:
{
  "code": "BAD_REQUEST",
  "message": "Bad Request",
  "data": {
    "errors": {
      "field_name": ["Error message for this field"]
    }
  }
}

Error Code Reference

CodeHTTP StatusDescription
BAD_REQUEST400Invalid input or validation failure
UNAUTHORIZED401Authentication required or failed
FORBIDDEN403Access denied
NOT_FOUND404Resource not found
CONFLICT409Resource conflict
TOO_MANY_REQUESTS429Rate limit exceeded
INTERNAL_SERVER_ERROR500Server error (contact support if persistent)

Common Error Codes

400 Bad Request

Returned when the request contains invalid parameters or fails validation.
{
  "code": "BAD_REQUEST",
  "message": "Bad Request",
  "data": {
    "errors": {
      "id": ["Invalid UUID"],
      "email": ["Invalid email address"]
    }
  }
}
Common causes:
  • Missing required fields
  • Invalid field formats (e.g., invalid UUID, email, date)
  • Business rule violations
  • References to resources that don’t exist or belong to another workspace

401 Unauthorized

Returned when authentication fails.
{
  "code": "UNAUTHORIZED",
  "message": "Invalid or unauthorized API key"
}
Common causes:
  • Missing Authorization header
  • Invalid API key format
  • API key doesn’t exist
  • API key was revoked

403 Forbidden

Returned when authentication succeeds but the request is not allowed.
{
  "code": "FORBIDDEN",
  "message": "API key was revoked 2 days ago"
}
Common causes:
  • API key has been revoked or expired
  • Insufficient permissions for the operation
  • Attempting to use engagement-only profiles for analytics

404 Not Found

Returned when the requested resource doesn’t exist.
{
  "code": "NOT_FOUND",
  "message": "Post not found or does not belong to this workspace"
}
Common causes:
  • Resource ID doesn’t exist
  • Resource belongs to a different workspace
  • Resource was deleted

409 Conflict

Returned when the request conflicts with existing data.
{
  "code": "CONFLICT",
  "message": "Label with name \"Marketing\" already exists"
}
Common causes:
  • Attempting to create a duplicate resource
  • Unique constraint violation

429 Too Many Requests

Returned when you’ve exceeded the rate limit.
{
  "code": "TOO_MANY_REQUESTS",
  "message": "Rate limit of 100 requests per 60s exceeded. Quota resets in 45 seconds"
}
How to handle:
  • Wait for the rate limit to reset
  • Implement exponential backoff
  • Cache responses where appropriate
  • Contact support if you need to increase the rate limit

Handling Errors

Best Practices

Always check the HTTP status code before parsing the response body.
const response = await fetch(url, options);

if (!response.ok) {
  const error = await response.json();
  throw new Error(`${error.code}: ${error.message}`);
}
For 400 errors, check the data.errors field for field-specific messages.
if (error.code === 'BAD_REQUEST' && error.data?.errors) {
  for (const [field, messages] of Object.entries(error.data.errors)) {
    console.error(`${field}: ${messages.join(', ')}`);
  }
}
For rate limits and server errors, implement retry with exponential backoff.
async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const response = await fetch(url, options);
    
    if (response.status === 429 || response.status >= 500) {
      const delay = Math.pow(2, i) * 1000;
      await new Promise(resolve => setTimeout(resolve, delay));
      continue;
    }
    
    return response;
  }
  throw new Error('Max retries exceeded');
}
Log error responses with context for easier debugging.
if (!response.ok) {
  const error = await response.json();
  console.error('API Error:', {
    status: response.status,
    code: error.code,
    message: error.message,
    endpoint: url,
    timestamp: new Date().toISOString()
  });
}

Example Error Handler

async function handleApiResponse(response) {
  if (response.ok) {
    return response.json();
  }

  const error = await response.json();

  switch (response.status) {
    case 400:
      if (error.data?.errors) {
        throw new ValidationError(error.message, error.data.errors);
      }
      throw new BadRequestError(error.message);

    case 401:
      throw new UnauthorizedError(error.message);

    case 403:
      throw new ForbiddenError(error.message);

    case 404:
      throw new NotFoundError(error.message);

    case 429:
      throw new RateLimitError(error.message);

    default:
      throw new ApiError(error.code, error.message);
  }
}