Rate limits & quotas
We enforce two limits independently:
- Per-minute rate limit — sliding window, per API key.
- Monthly quota — total result units charged in your billing period.
When either trips, calls return 429.
Plan tiers
| Plan | Rate limit (req/min) | Monthly result quota |
|---|---|---|
| Free | 20 | 100 |
| Starter | 60 | 5,000 |
| Pro | 300 | 50,000 |
| Scale | 1,500 | 500,000 |
A “result” = one row returned. Cache hits on /v1/properties/{zpid} and its
sub-resources count as 0 units — they don’t burn quota.
What counts as a unit
| Endpoint | Units charged |
|---|---|
/v1/properties/by-url (sync, cache miss) | 1 per result returned (≈ 1) |
/v1/properties/by-address | 1 |
/v1/properties/{zpid} (cache hit, < 24h) | 0 |
/v1/properties/{zpid} (cache miss) | 1 |
/v1/properties/{zpid}/... sub-resources | 0 if cached, 1 on miss |
/v1/buildings/by-url (extract_units != disabled) | 1 per unit returned |
/v1/properties/batch (async) | 1 per result, charged on completion |
/v1/search, /v1/listings/* | 1 per listing returned |
/v1/search/with-details | 1 per search listing + 1 per detail row |
429 responses
{ "error": { "code": "rate_limited", "message": "Rate limit exceeded for plan 'starter' (60/min)", "request_id": "..." }}{ "error": { "code": "quota_exceeded", "message": "Plan 'starter' quota of 5000 reached. Used 5012 this period.", "request_id": "..." }}Best practices
- Cache aggressively on your side for static fields (zpid, address, year built).
- Use sub-resources instead of the full detail when you only need photos or schools.
- Use async + webhooks for any batch over ~50 — sync calls have a 5-min ceiling.
- Back off on 429 with exponential jitter. Don’t retry faster than once every 2 seconds.