LiyaSupport
API Reference

Rate limits & errors

Understand API rate limits, HTTP error codes, and how to implement graceful retry logic.

5 min read Updated July 2026

Rate limits by plan

PlanRequests / minRequests / dayBurst (10s window)Concurrent requests
Starter6010,00012010
Growth300100,00060050
Scale600500,0001,200100
Enterprise1,000Unlimited2,000200

Rate limits are applied per API key. If you have multiple API keys, each has its own independent limit. Rate limits cannot be shared across keys.

Rate limit headers

Every API response includes the following headers:

HeaderValueDescription
X-RateLimit-Limit300Your rate limit ceiling per minute
X-RateLimit-Remaining247Requests remaining in the current window
X-RateLimit-Reset1720000260Unix timestamp when the window resets
X-RateLimit-Burst-Remaining598Remaining requests in the 10-second burst window

Handling 429 responses

When rate limited, you receive a 429 Too Many Requests response with a Retry-After header indicating how many seconds to wait before retrying.

Implement exponential backoff with jitter to handle bursts gracefully:

javascript
async function fetchWithRetry(url, options, maxRetries = 5) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.status !== 429) return response;

    const retryAfter = parseInt(response.headers.get("Retry-After") || "1", 10);
    const jitter = Math.random() * 1000; // add up to 1s of jitter
    const delay = retryAfter * 1000 * Math.pow(2, attempt) + jitter;

    if (attempt < maxRetries) {
      console.log(`Rate limited. Retrying in ${Math.round(delay)}ms...`);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
  throw new Error("Max retries exceeded");
}
Add random jitter (0–1000ms) to your backoff delay. Without jitter, multiple processes retrying at the same time will all hit the API simultaneously after the window resets.

HTTP error codes

StatusError typeCommon causes
400invalid_requestMissing required field, malformed JSON, invalid parameter value
401authentication_errorNo Authorization header, expired token, invalid API key format
403permission_errorAPI key scope doesn&apos;t include the requested operation
404not_foundResource ID doesn&apos;t exist or was deleted
405method_not_allowedHTTP method not supported for this endpoint
409conflictDuplicate resource (e.g. contact with same email already exists)
422validation_errorRequest body passed schema validation but failed business logic (e.g. undefined custom attribute key)
429rate_limit_errorToo many requests — see rate limits section above
500api_errorInternal server error — safe to retry with exponential backoff
502bad_gatewayUpstream service unavailable — retry after a few seconds
503service_unavailableLiya is temporarily down — check status.liyasupport.com

Error response format

json
{
  "error": {
    "type": "validation_error",
    "message": "Custom attribute key 'contract_value' is not defined in your workspace.",
    "code": "undefined_attribute_key",
    "param": "custom_attributes.contract_value",
    "request_id": "req_01J9XABC123"
  }
}

Include the request_id when contacting support — it allows our team to look up the exact request in our logs.


Idempotency keys

For POST requests (create operations), pass an Idempotency-Key header with a unique value (e.g. a UUID) to ensure safe retries. If we receive two requests with the same idempotency key within 24 hours, the second request returns the same response as the first without creating a duplicate resource.

bash
curl -X POST https://api.liyasupport.com/v1/tickets \
  -H "Authorization: Bearer lsk_live_..." \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json" \
  -d '{ "subject": "...", "body": "...", "contact": { "email": "..." } }'