Skip to main content

Overview

The DialNexa API enforces rate limits to ensure fair usage and platform stability. Rate limits are applied per API key. When you exceed a rate limit, the API returns 429 Too Many Requests:
{
  "statusCode": 429,
  "message": "Too many requests. Please slow down and retry after a moment.",
  "error": "Too Many Requests"
}

Rate limit headers

Every API response includes headers that tell you your current usage:
HeaderDescription
X-RateLimit-LimitThe maximum number of requests allowed in the current window.
X-RateLimit-RemainingHow many requests you have left in the current window.
X-RateLimit-ResetUnix timestamp when the current window resets.
Retry-AfterSeconds to wait before retrying (present on 429 responses).

Handling 429 responses

When you receive a 429, wait for the number of seconds indicated in the Retry-After header before retrying. Implement exponential backoff for production systems to avoid hammering the API immediately after the window resets.

Retry with exponential backoff

async function requestWithRetry(
  url: string,
  options: RequestInit,
  maxRetries = 5
): Promise<Response> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const response = await fetch(url, options);

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

    if (attempt === maxRetries) {
      throw new Error("Rate limit exceeded after maximum retries.");
    }

    const retryAfter = parseInt(response.headers.get("Retry-After") || "1", 10);
    const delay = retryAfter * 1000 * Math.pow(2, attempt);
    await new Promise((resolve) => setTimeout(resolve, delay));
  }

  throw new Error("Unexpected error in retry loop.");
}
import time
import requests as req

def request_with_retry(method: str, url: str, max_retries: int = 5, **kwargs) -> req.Response:
    for attempt in range(max_retries + 1):
        response = req.request(method, url, **kwargs)

        if response.status_code != 429:
            return response

        if attempt == max_retries:
            raise Exception("Rate limit exceeded after maximum retries.")

        retry_after = int(response.headers.get("Retry-After", 1))
        delay = retry_after * (2 ** attempt)
        time.sleep(delay)

    raise Exception("Unexpected error in retry loop.")

Best practices

Batch operations over single-item calls: If you need to add 500 leads to a campaign, use the file upload endpoint rather than making 500 individual POST requests to /campaigns-leads. Cache read-only data: Voices, languages, LLMs, and transcribers change infrequently. Fetch them once and cache locally rather than re-fetching on every request. Use ETags for call logs: The GET /call-logs endpoint supports If-None-Match / ETag caching. Pass the ETag from a previous response so the server can return 304 Not Modified instead of a full payload when nothing has changed. Use webhooks instead of polling: Rather than polling GET /campaigns/{id}/status in a loop, register a webhook for campaign.completed and handle the event when it arrives.

Increasing your limits

If your use case requires higher rate limits, contact support@dialnexa.com to discuss your requirements.