Rate limits
Three independent limits govern every API key:
- Rate, requests per minute (sliding window). Trips
429 rate_limited. - Concurrency, one in-flight request per key (see below). Extra parallel calls queue, they are not rejected.
- Credit balance, drawn down per successful call;
402 out_of_creditsat zero. See Pricing.
For credit costs and plan tiers, see Pricing.
Per-minute limits
| Plan | Rate limit (req/min) |
|---|---|
| Free | 20 |
| Monthly | 200 |
| Annual | 300 |
| Enterprise | custom |
The window is sliding, based on the last 60 seconds, not a calendar minute.
Concurrency = 1
Each API key runs one in-flight request at a time. This is a first-class limit, on par with your per-minute rate. Firing requests in parallel on a single key does not go faster, the extra calls serialize behind the one in flight. Scale throughput by raising your plan, not by adding parallelism. For large result sets, use async jobs + webhooks (below) instead of many concurrent syncs.
Result caps
One caps table for the whole API. maxItems on a search is the single knob that
decides sync vs async: maxItems ≤ 50 runs synchronously; maxItems ≥ 51
flips the request to an async job (as do extractionMethod: PAGINATION_WITH_ZOOM_IN
and async: true).
| Endpoint | Field | Bounds | Sync ↔ async |
|---|---|---|---|
POST /v1/search, POST /v1/listings/{for-sale,for-rent,sold} | maxItems | 1 to ~820 (PAGINATION); ~500 (MAP_MARKERS) | ≤ 50 sync · ≥ 51 async · PAGINATION_WITH_ZOOM_IN always async |
POST /v1/search/with-details | maxItems | 1 to ~820 | always async (two chained stages) |
GET /v1/listings (REST wrapper) | max_items | 1-50 | sync only, for >50 use POST /v1/search |
POST /v1/properties/batch | entries (urls + addresses) | up to 500 per job | always async |
GET /v1/buildings/by-url | - | - | sync by default; set sync=false for large buildings |
GET /v1/jobs | limit | 1-500 (default 50) | n/a |
GET /v1/jobs/{id}/results | limit | 1-1000 (default 100) | n/a |
GET /v1/usage | limit | 1-1000 (default 100) | n/a |
The earlier “up to 50”, “≤50”, and “1-500” figures refer to different knobs: 50 is the sync search cap, 500 is the batch entry cap, and 1000 is the job-results page cap. They are not in conflict.
429 response
{ "error": { "code": "rate_limited", "message": "Rate limit exceeded for plan 'monthly' (200/min)", "request_id": "..." } }Handling 429
- Back off with exponential jitter. Don’t retry faster than once every 2 seconds.
- Use async + webhooks for any batch over ~50, sync calls have a 5-min ceiling.
- Cache aggressively on your side for static fields (zpid, address, year built) so you don’t burn your rate limit re-fetching the same property.