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.
| Plan | Per-minute limit | Daily limit |
|---|---|---|
| Launch | 10 req/min | ~7,200 req/day |
| Growth | 50 req/min | Scales with plan |
| Enterprise | Negotiated | Negotiated |
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. TheRetry-AfterHTTP header carries the same value when present.correlation_id— a request identifier to quote if you contact support.doc_url— a link back to therate_limitedentry 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. ReadpageInfo.hasNextPageand only fetch the next page once the current one returns. - Filter server-side with the
filterquery 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.