Rate limits

Per-plan request limits, the 429 response shape, and how to back off and retry.

The Listings API meters requests per account against your plan's rate limit. Staying inside the limit keeps your integration responsive; crossing it returns 429 Too Many Requests with everything you need to retry gracefully.

Per-plan limits

Limits are plan-based and enforced per account — every API key on an account draws from the same budget. Requests are metered on two windows: a per-minute burst limit and a daily cap.

PlanPer-minute limitDaily limit
Launch10 req/min~7,200 req/day
Growth50 req/minScales with plan
EnterpriseNegotiatedNegotiated

The 429 response

When you exceed a limit the API responds with HTTP 429 and the standard error envelope. Three fields matter for retries:

  • retry_after_seconds — how long to wait before retrying. The Retry-After HTTP header carries the same value when present.
  • correlation_id — a request identifier to quote if you contact support.
  • doc_url — a link back to the rate_limited entry in the error-codes reference.
{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Too many requests. Slow down.",
    "retry_after_seconds": 30,
    "correlation_id": "9f2c1e64-2b1a-4b0e-9f1a-2c7d5e8a1b3c",
    "doc_url": "https://docs.listingsapi.com/docs/error-codes#rate_limited"
  }
}

Handling 429s with backoff

Honor retry_after_seconds when it is present, and otherwise back off exponentially — doubling the delay on each consecutive 429 up to a ceiling, with a little random jitter so a fleet of clients doesn't retry in lockstep.

import random
import time

import requests

MAX_RETRIES = 5


def get_with_backoff(url: str, headers: dict, max_retries: int = MAX_RETRIES):
    for attempt in range(max_retries):
        resp = requests.get(url, headers=headers)
        if resp.status_code != 429:
            resp.raise_for_status()
            return resp.json()

        # Prefer the server's hint; fall back to exponential backoff + jitter.
        body = resp.json().get("error", {})
        retry_after = body.get("retry_after_seconds")
        if retry_after is None:
            retry_after = min(2 ** attempt, 30) + random.uniform(0, 1)
        time.sleep(retry_after)

    raise RuntimeError("Still rate limited after retrying")

Staying under the limit

  • Request one page at a time — tight parallel pagination loops are the most common source of 429s. Read pageInfo.hasNextPage and only fetch the next page once the current one returns.
  • Filter server-side with the filter query parameter instead of pulling every page and discarding rows client-side.
  • Cache responses that don't change often rather than re-fetching them on every request.