> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.hellodata.ai/documentation/concepts/respecting-rate-limits/llms.txt. # Respecting Rate Limits When you call the HelloData API, every response includes rate limit headers. To avoid hitting the limiter (or to recover quickly when you do), read the `RateLimit` header and wait the number of seconds specified by its `t` parameter before retrying. ## Which header to use We use the IETF `RateLimit` header format. * **`RateLimit`**: includes remaining requests (`r`) and seconds until reset (`t`) * **`RateLimit-Policy`**: describes the policy (quota `q` and window `w`) Example `RateLimit` header: ```text "default";r=0;t=12 ``` In that example, you should wait **12 seconds** before retrying. ## Python example This example retries a request when it receives a `429 Too Many Requests`, waiting `t` seconds from the `RateLimit` header before retrying. ```python import time import re import requests def parse_ratelimit_t_seconds(rate_limit_header: str | None) -> int | None: """ Parses the IETF RateLimit header and returns the `t` value (seconds until reset). Example header: '"default";r=0;t=12' """ if not rate_limit_header: return None # Match ;t= anywhere in the header value m = re.search(r"(?:^|;)t=(\d+)(?:;|$)", rate_limit_header) if not m: return None return int(m.group(1)) def get_with_rate_limit_handling(url: str, headers: dict[str, str], *, max_retries: int = 5): for attempt in range(max_retries + 1): resp = requests.get(url, headers=headers, timeout=30) if resp.status_code != 429: return resp wait_s = parse_ratelimit_t_seconds(resp.headers.get("RateLimit")) if wait_s is None: # If the header is missing/unparseable, fall back to a small backoff. wait_s = min(2 ** attempt, 30) time.sleep(wait_s) return resp # last response (429) # Example usage: # API_KEY = "..." # r = get_with_rate_limit_handling( # "https://api.hellodata.ai/v1/property/search", # headers={"Authorization": f"Bearer {API_KEY}"}, # ) # r.raise_for_status() # data = r.json() ``` ## TypeScript example This example does the same thing using `fetch` (Node 18+). ```typescript function parseRateLimitTSeconds(rateLimitHeader: string | null): number | null { if (!rateLimitHeader) return null; // Example header: `"default";r=0;t=12` const match = rateLimitHeader.match(/(?:^|;)t=(\d+)(?:;|$)/); if (!match) return null; return Number(match[1]); } function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } export async function fetchWithRateLimitHandling( url: string, init: RequestInit, { maxRetries = 5 }: { maxRetries?: number } = {} ): Promise { let lastResponse: Response | undefined; for (let attempt = 0; attempt <= maxRetries; attempt++) { const res = await fetch(url, init); lastResponse = res; if (res.status !== 429) { return res; } const tSeconds = parseRateLimitTSeconds(res.headers.get("RateLimit")); const waitSeconds = tSeconds ?? Math.min(2 ** attempt, 30); await sleep(waitSeconds * 1000); } return lastResponse!; } // Example usage: // const API_KEY = process.env.HELLODATA_API_KEY!; // const res = await fetchWithRateLimitHandling("https://api.hellodata.ai/v1/property/search", { // method: "GET", // headers: { Authorization: `Bearer ${API_KEY}` }, // }); // if (!res.ok) throw new Error(await res.text()); // const json = await res.json(); ``` ## Notes and best practices * **Proactively slow down when `r` is low**: if you’re near `r=0`, add a small delay between requests. * **Prefer concurrency limits over retries**: cap parallel requests (e.g. with a semaphore / queue) so you don’t burst over the minute window. * **Always handle missing headers**: use a small exponential backoff fallback if `RateLimit` isn’t present for some reason. > Wait and retry using response headers