Errors & Rate Limits

Error response format, HTTP status codes, and rate limiting in the Tabular Pro API.

Errors & Rate Limits

Response Format

All API responses follow a consistent format:

Success:

{
  "success": 1,
  "data": { ... }
}

Error:

{
  "error": 1,
  "message": "Human-readable error description"
}

Some errors include a code field for programmatic handling:

{
  "error": 1,
  "code": "NO_ORGANIZATION",
  "message": "User has no organization attached"
}

HTTP Status Codes

Code Meaning
200 Success
201 Created (new resource)
204 No Content (successful deletion)
400 Bad Request — invalid parameters or missing required fields
401 Unauthorized — missing or invalid authentication
403 Forbidden — insufficient permissions
404 Not Found — resource doesn't exist
429 Too Many Requests — rate limit exceeded
500 Internal Server Error

Common Errors

Authentication Errors

// Missing auth
{ "error": 1, "message": "Authentication required" }

// Invalid/expired token or API key
{ "error": 1, "message": "Invalid or expired token" }
{ "error": 1, "message": "Invalid API key" }

// Wrong user type
{ "error": 1, "message": "Admin access required" }

Validation Errors

// Missing required field
{ "error": 1, "message": "Survey name is required" }

// Invalid ID parameter
{ "error": 1, "message": "Invalid survey ID" }

Resource Errors

// Not found
{ "error": 1, "message": "Survey not found" }

// Organization database not configured
{ "error": 1, "message": "Organization database not configured" }

Rate Limits

Endpoint Group Window Max Requests
General API (/api/*) 15 minutes 1,000
Authentication (/api/auth/*) 15 minutes 20
Registration (/api/register/*) 1 hour 10

Rate Limit Headers

Every response includes:

RateLimit-Limit: 1000
RateLimit-Remaining: 994
RateLimit-Reset: 1714060800

Handling Rate Limits

When you exceed the limit:

// HTTP 429
{ "error": 1, "message": "Too many attempts. Please try again later." }

Best practices:

  • Cache responses when possible
  • Use batch endpoints (e.g., chart-data-batch) instead of individual requests
  • Implement exponential backoff on 429 responses
  • Monitor RateLimit-Remaining to stay within limits

Error Handling Best Practice

async function apiCall(path) {
  const response = await fetch(`${BASE_URL}${path}`, {
    headers: { 'X-API-Key': API_KEY },
  });

  if (response.status === 429) {
    // Rate limited — wait and retry
    const retryAfter = response.headers.get('Retry-After') || 60;
    await new Promise(r => setTimeout(r, retryAfter * 1000));
    return apiCall(path);
  }

  const data = await response.json();

  if (!response.ok) {
    throw new Error(data.message || `API error: ${response.status}`);
  }

  return data;
}