Rate Limiting
PrairieCloud enforces two types of limits:
- Monthly quota — total requests per billing period
- Per-minute burst limit — requests per 60-second rolling window
Both limits are enforced per API key.
Tier Limits
See prairiecloud.io/pricing for a full comparison of what's included at each tier.
| Plan | Price | Monthly Quota | Per-Minute Burst | Concurrent Requests |
|---|---|---|---|---|
| Free | $0 | 1,000 | 10 | 2 |
| Pioneer | $19/mo | 50,000 | 300 | 10 |
| Pro | $49/mo | 500,000 | 1,500 | 25 |
| Business | $299/mo | 2,000,000 | 3,000 | 50 |
| Enterprise | Custom | 10,000,000 | 10,000 | 100 |
When any limit is reached, the API returns 429 Too Many Requests. Rate limiting is applied at three levels: monthly quota, per-minute burst, and concurrent connections. Need higher limits? Upgrade your plan.
Rate Limit Response Headers
Every API response includes rate limit headers:
| Header | Description |
|---|---|
X-RateLimit-Limit | Your monthly quota |
X-RateLimit-Remaining | Requests remaining this billing period |
X-RateLimit-Reset | Unix timestamp when the monthly quota resets |
Example response headers:
HTTP/2 200
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1743379200
Convert the X-RateLimit-Reset timestamp to a human-readable date:
date -d @1743379200
# → Sat Mar 1 00:00:00 UTC 2026
Request Cost
Not all requests cost the same against your quota. Most requests cost 1 unit, but requests involving many variables, geographies, or wildcard expansions cost more.
Requests to /v1/variables, /v1/geographies, /v1/topics, /v1/boundaries, and /v1/centroids endpoints are free and never count against your quota.
Base Cost
| Scenario | Cost |
|---|---|
| Standard request (≤10 variables, ≤10 geos) | 1 unit |
| >10 variables or >10 geographies | 1 + ceil(overrun / 10) units |
Wildcard geography (e.g. county:48*) | ceil(expanded_geos / 10) units |
Examples
| Request | Expanded Size | Cost |
|---|---|---|
?variables=pop&geo=state:48 | 1 variable, 1 geo | 1 unit |
?variables=pop,income,... (15 vars) | 15 variables | 2 units |
?geo=county:48* (254 TX counties) | 254 geos | 26 units |
?geo=county:06* (58 CA counties) | 58 geos | 6 units |
Wildcard queries are powerful but consume quota quickly. Check your remaining quota via the Dashboard or the /v1/account/usage endpoint.
429 Response Format
When you exceed a rate limit, you receive a 429 Too Many Requests response with an RFC 7807 body and a Retry-After header:
HTTP/2 429
Content-Type: application/problem+json
Retry-After: 12
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1743379200
{
"type": "https://prairiecloud.io/errors/rate-limit-exceeded",
"title": "Rate Limit Exceeded",
"status": 429,
"detail": "Per-minute burst limit of 10 exceeded for tier 'free'.",
"instance": "/v1/data",
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"limit_type": "burst",
"plan": "free",
"upgrade_url": "https://dashboard.prairiecloud.io/dashboard/billing/"
}
The response now includes three additional fields:
| Field | Description |
|---|---|
plan | Your current plan name (e.g., free, pioneer, pro) |
quota | Your monthly request quota |
upgrade_url | Direct link to upgrade your plan for higher limits |
The Retry-After header is always present on 429 responses and indicates the number of seconds to wait before retrying.
Checking Your Current Usage
Monitor your current usage in the Dashboard — daily usage charts and quota progress are visible on the Usage page.
You can also check your remaining quota programmatically via the API:
curl -H "X-API-Key: YOUR_KEY" \
"https://api.prairiecloud.io/v1/account/usage"
{
"data": {
"tier": "free",
"quota_limit": 1000,
"quota_used": 153,
"quota_remaining": 847,
"period_start": "2026-02-01",
"period_end": "2026-02-28",
"burst_limit": 10
}
}
Retry Strategy with Exponential Backoff
When you receive a 429, always respect the Retry-After header before retrying. For robustness, combine this with exponential backoff:
import time
import random
import requests
def request_with_retry(url, headers, max_retries=5):
for attempt in range(max_retries):
response = requests.get(url, headers=headers)
if response.status_code == 429:
# Respect Retry-After header
retry_after = int(response.headers.get("Retry-After", 1))
# Add jitter to avoid thundering herd
wait = retry_after + random.uniform(0, 1)
print(f"Rate limited. Waiting {wait:.1f}s before retry {attempt + 1}/{max_retries}")
time.sleep(wait)
continue
if response.status_code >= 500:
# Exponential backoff for server errors
wait = (2 ** attempt) + random.uniform(0, 1)
print(f"Server error {response.status_code}. Waiting {wait:.1f}s")
time.sleep(wait)
continue
return response
raise Exception(f"Request failed after {max_retries} retries")
Key Principles
- Always check
Retry-After— this is the minimum wait time. Never retry before it elapses. - Add jitter — a small random offset (0–1 seconds) prevents multiple clients from retrying simultaneously.
- Use exponential backoff for 5xx errors — double the wait time on each retry attempt.
- Set a maximum retry count — don't retry forever; surface the error to the caller after N attempts.
- Don't retry 4xx errors (except 429) — a
400 Bad Requestor404 Not Foundwon't succeed on retry.
Optimizing Your Usage
To stay within your quota:
- Batch variables: Request multiple variables in one call instead of one per request. The
variablesparameter accepts a comma-separated list. - Batch geographies: Similarly, request multiple
geovalues in a single call. - Cache responses: ACS data doesn't change mid-month. Cache responses locally for frequently requested variable/geography combinations.
- Use the largest geographic unit you need: County-level data is available in one request; iterating over all tracts in a county could use hundreds of requests.
Tier-Gated Features
Some API features are only available on higher-tier plans. If you attempt to use a Pro-tier feature on a Free or Pioneer plan, the API returns 403 Forbidden with a feature-requires-upgrade error type:
{
"type": "https://prairiecloud.io/errors/feature-requires-upgrade",
"title": "Feature Requires Upgrade",
"status": 403,
"detail": "CSV export requires a Pro plan or higher.",
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"upgrade_url": "https://dashboard.prairiecloud.io/dashboard/billing/"
}
Features by Tier
| Feature | Free | Pioneer | Pro | Business | Enterprise |
|---|---|---|---|---|---|
CSV export (format=csv) | ❌ | ❌ | ✅ | ✅ | ✅ |
Wildcard geography (geo=county:48*) | ❌ | ❌ | ✅ | ✅ | ✅ |
| Pagination limit >200 | ❌ | ❌ | ✅ (5,000) | ✅ (10,000) | ✅ (10,000) |
Free and Pioneer users can access all other endpoints and features. See Error Handling for the full error response format.
Next Steps
- Error Handling — Full RFC 7807 error response format and error code reference
- API Keys — Key management, rotation, and security best practices
- Quickstart — See the full quickstart for your first API call