Skip to main content
The ENS Ads API enforces rate limits to ensure fair access and platform stability. If your integration sends too many requests in a short period, the API returns a 429 Too Many Requests response. You should design your integration to handle this response gracefully by reducing request frequency and retrying with a delay, rather than failing immediately or retrying in a tight loop.

429 — Too Many Requests

When you exceed the rate limit, the API responds with:
{
  "status": "error",
  "message": "Rate limit exceeded"
}
Do not retry immediately after receiving this response. Instead, wait and retry with increasing delays using an exponential backoff strategy.

Best practices

Cache fetched campaigns. The most effective way to stay within rate limits is to avoid fetching campaigns more often than necessary. Fetch a batch of campaigns once per page load (or once per session) and reuse the results rather than calling /campaigns/fetch on every render or component mount. Use the limit parameter. The limit query parameter accepts values up to 100, allowing you to retrieve up to 100 campaigns in a single request. Batching fetches this way reduces the total number of API calls your integration makes. Implement exponential backoff for retries. When you receive a 429 response, wait before retrying. Double the wait time on each successive failure to avoid hammering the API while it recovers.
Cache campaign data at the server or CDN layer whenever possible. Serving cached campaign results to users eliminates redundant /campaigns/fetch calls entirely and keeps your integration well within rate limits even at high traffic volumes.

Exponential backoff example

The following example shows a JavaScript function that retries a failed fetch with exponential backoff. It waits 500 ms after the first failure, 1000 ms after the second, 2000 ms after the third, and so on, up to a configurable maximum number of attempts.
const BASE_URL = 'https://ads.enslive.live/api/v1';
const API_KEY = 'YOUR_API_KEY';

async function fetchCampaignsWithRetry(placement, location, device, maxRetries = 4) {
  let attempt = 0;

  while (attempt < maxRetries) {
    const response = await fetch(
      `${BASE_URL}/campaigns/fetch?placement=${placement}&location=${location}&device=${device}`,
      {
        headers: {
          'Authorization': `Bearer ${API_KEY}`
        }
      }
    );

    if (response.ok) {
      const data = await response.json();
      return data.data;
    }

    if (response.status === 429) {
      const delayMs = Math.pow(2, attempt) * 500;
      console.warn(`Rate limited. Retrying in ${delayMs}ms (attempt ${attempt + 1}/${maxRetries})`);
      await new Promise(resolve => setTimeout(resolve, delayMs));
      attempt++;
      continue;
    }

    // For non-429 errors, throw immediately rather than retrying
    throw new Error(`Request failed with status ${response.status}`);
  }

  throw new Error('Max retries reached. Could not fetch campaigns.');
}
This pattern keeps your integration resilient without flooding the API with repeated requests during a rate-limit window.