xYago APIv1

Rate limits

Each API key has a per-minute request limit. Exceed it and you get 429 rate_limited; wait the number of seconds in Retry-After.

Limits

Headers

Authenticated requests that reach the rate-limit check include:

HeaderMeaning
X-RateLimit-LimitRequests allowed per minute.
X-RateLimit-RemainingRequests left in the current window.
X-RateLimit-ResetWhen the next window starts (Unix epoch, seconds).
Retry-AfterOnly on 429: seconds to wait.
HTTP/1.1 429 Too Many Requests
Retry-After: 17
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1772463660
X-Request-Id: req_0c1d2e3f4a5b6c7d

{"error": {"code": "rate_limited", "message": "Rate limit exceeded. Retry after the indicated number of seconds."}}

Retrying correctly

Python

import random
import time


def request_with_retries(session, method, url, max_attempts=5, **kwargs):
    for attempt in range(1, max_attempts + 1):
        response = session.request(method, url, timeout=30, **kwargs)
        if response.status_code == 429:
            delay = int(response.headers.get("Retry-After", "1"))
        elif response.status_code in (500, 502, 503):
            delay = min(2 ** attempt, 30) + random.random()
        else:
            return response
        if attempt == max_attempts:
            return response
        time.sleep(delay)

JavaScript

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function fetchWithRetries(url, options = {}, maxAttempts = 5) {
  for (let attempt = 1; ; attempt++) {
    const response = await fetch(url, options);
    let delay;
    if (response.status === 429) delay = Number(response.headers.get("Retry-After") || 1) * 1000;
    else if ([500, 502, 503].includes(response.status)) delay = Math.min(2 ** attempt, 30) * 1000 + Math.random() * 1000;
    else return response;
    if (attempt === maxAttempts) return response;
    await sleep(delay);
  }
}

C#

static async Task<HttpResponseMessage> SendWithRetriesAsync(
    HttpClient http, Func<HttpRequestMessage> build, int maxAttempts = 5)
{
    for (var attempt = 1; ; attempt++)
    {
        var response = await http.SendAsync(build());
        TimeSpan delay;
        if ((int)response.StatusCode == 429)
            delay = response.Headers.RetryAfter?.Delta ?? TimeSpan.FromSeconds(1);
        else if ((int)response.StatusCode is 500 or 502 or 503)
            delay = TimeSpan.FromSeconds(Math.Min(Math.Pow(2, attempt), 30) + Random.Shared.NextDouble());
        else
            return response;
        if (attempt == maxAttempts) return response;
        await Task.Delay(delay);
    }
}

With Polly (.NET) you can express the same with a retry policy that reads Retry-After. Don't retry 4xx errors other than 429.

Besides the per-key limit, POST /messages and Verify have their own caps per sender and phone (see Messages and Verify); they also return 429 rate_limited with Retry-After. If those caps cannot be checked, sends (POST /messages, POST /verify/start) return 503 limits_unavailable; POST /verify/check is not blocked.