Skip to main content
The AgriBackup API enforces rate limits on every endpoint to protect platform stability for all enterprise customers. Every response you receive includes headers that tell you exactly where you stand in the current window — use them to pace your requests proactively rather than waiting for a rejection.

Standard Limits

Live keys (sk_live_) are limited to 100 requests per second. Sandbox keys (sk_test_) share the same limit so your load-testing environment reflects production behaviour accurately.
Rate limits are applied per API key, not per IP address or organisation. If you manage multiple integrations, issue a separate key for each to isolate their quotas from one another.

Rate-Limit Response Headers

Every API response — including successful ones — carries three headers you can inspect to track your remaining quota:
HeaderTypeDescription
X-RateLimit-LimitintegerThe maximum number of requests permitted per second for your key
X-RateLimit-RemainingintegerThe number of requests you have left in the current window
X-RateLimit-ResetintegerUTC epoch timestamp (seconds) at which the window resets
Parse these headers on every response so your client can throttle itself before hitting the hard limit.

HTTP 429 — Too Many Requests

When you exceed the rate limit, the API returns 429 Too Many Requests. The response body follows the standard ApiError schema and includes a Retry-After header indicating how many seconds to wait before retrying.
{
  "code": "RATE_LIMIT_EXCEEDED",
  "message": "You have exceeded the allowed request rate. Please slow down.",
  "details": []
}
Do not immediately retry a 429 response. The server will continue to reject your requests until the window resets. Respect the Retry-After header value before issuing your next request.

Handling Rate Limits in Your Integration

Recommendations

  • Read X-RateLimit-Remaining proactively. If the value drops below 10, introduce an artificial delay before the next request rather than waiting for a 429.
  • Use batch endpoints where available. Submitting multiple polygons or batches in a single call consumes one request unit instead of many. Prefer POST /polygons with an array payload over looping individual calls.
  • Apply exponential backoff with jitter. If you do receive a 429, wait for the Retry-After interval, then retry with an exponentially increasing delay to avoid a thundering-herd pattern.
  • Queue writes during bursts. For high-volume ingestion pipelines, use an internal queue and drain it at a controlled rate (e.g. 80 requests/second) to give yourself headroom.

Python — Exponential Backoff

import time
import random
import requests

API_KEY = "sk_live_YOUR_API_KEY"
BASE_URL = "https://live.agribackup.com/api/v1"

def request_with_backoff(method: str, path: str, **kwargs) -> requests.Response:
    """Make an API request, retrying with exponential backoff on 429."""
    url = f"{BASE_URL}{path}"
    headers = {"X-API-Key": API_KEY, **kwargs.pop("headers", {})}
    max_retries = 5
    base_delay = 1.0  # seconds

    for attempt in range(max_retries):
        response = requests.request(method, url, headers=headers, **kwargs)

        if response.status_code != 429:
            return response

        retry_after = int(response.headers.get("Retry-After", base_delay * (2 ** attempt)))
        jitter = random.uniform(0, 0.5)
        sleep_time = retry_after + jitter

        print(f"Rate limited. Retrying in {sleep_time:.2f}s (attempt {attempt + 1}/{max_retries})")
        time.sleep(sleep_time)

    raise Exception("Max retries exceeded after repeated 429 responses.")


# Example usage
response = request_with_backoff("GET", "/enterprise/eudr/polygons")
print(response.json())

Node.js — Exponential Backoff

import axios from "axios";

const API_KEY = "sk_live_YOUR_API_KEY";
const BASE_URL = "https://live.agribackup.com/api/v1";

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

async function requestWithBackoff(method, path, options = {}) {
  const url = `${BASE_URL}${path}`;
  const headers = { "X-API-Key": API_KEY, ...options.headers };
  const maxRetries = 5;

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await axios({ method, url, headers, ...options });
      return response.data;
    } catch (err) {
      if (err.response?.status !== 429) throw err;

      const retryAfter = parseInt(err.response.headers["retry-after"] ?? "1", 10);
      const jitter = Math.random() * 500; // up to 500 ms of jitter
      const delay = retryAfter * 1000 * Math.pow(2, attempt) + jitter;

      console.warn(`Rate limited. Retrying in ${(delay / 1000).toFixed(2)}s (attempt ${attempt + 1}/${maxRetries})`);
      await sleep(delay);
    }
  }

  throw new Error("Max retries exceeded after repeated 429 responses.");
}

// Example usage
const data = await requestWithBackoff("GET", "/enterprise/eudr/polygons");
console.log(data);
If you are running bulk data migrations or end-of-month compliance archiving, schedule those jobs during off-peak hours and target no more than 70 requests/second to give yourself a comfortable buffer below the 100 rps ceiling.