Rate limits¶
Tango enforces rate limits to keep the API fast and reliable for everyone. Limits apply at the account level: requests made via API keys and OAuth2 tokens for the same account all draw from the same quotas.
Where to see your limits and usage¶
- Your current plan limits + near-real-time usage: Account profile
- Upgrade pricing / higher limits: Pricing
How rate limits work (burst + daily)¶
Most plans have more than one rate limit window:
- Burst: short window (e.g. per minute) that protects the API from sudden spikes
- Daily: fixed window that resets at midnight UTC, caps total volume per day
You may be “fine” on daily usage but still hit burst limits (or vice versa).
Rate limit headers¶
Every /api/* response includes rate limit headers.
Overall headers (most restrictive window)¶
These headers summarize the most restrictive window (the one you’re closest to hitting):
X-RateLimit-Limit: total requests allowed for that windowX-RateLimit-Remaining: requests remaining in that windowX-RateLimit-Reset: seconds until reset for that window
Per-window headers (daily, burst, etc.)¶
For each configured window (commonly Daily and Burst), you’ll also see:
X-RateLimit-Daily-Limit,X-RateLimit-Daily-Remaining,X-RateLimit-Daily-ResetX-RateLimit-Burst-Limit,X-RateLimit-Burst-Remaining,X-RateLimit-Burst-Reset
Each *-Reset value is seconds until that specific window resets.
Quick header check (curl)¶
curl -s -D - -o /dev/null \
-H "X-API-KEY: your-api-key-here" \
"https://tango.makegov.com/api/contracts/?limit=1"
Example response headers:
X-RateLimit-Daily-Limit: 2400
X-RateLimit-Daily-Remaining: 2350
X-RateLimit-Daily-Reset: 86400
X-RateLimit-Burst-Limit: 100
X-RateLimit-Burst-Remaining: 95
X-RateLimit-Burst-Reset: 45
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 45
X-Execution-Time: 0.045s
What happens when you exceed a limit (HTTP 429)¶
When you hit a rate limit, Tango responds with HTTP 429 and a JSON body like:
{
"detail": "Rate limit exceeded for burst. Please try again in 45 seconds.",
"wait_in_seconds": 45
}
Recommended client behavior¶
- Stop retrying immediately after a 429.
- Sleep for at least
wait_in_seconds(preferred) orX-RateLimit-Reset. - Then retry with exponential backoff + jitter to avoid a thundering herd.
Python example¶
import random
import time
import httpx
url = "https://tango.makegov.com/api/contracts/?limit=1"
headers = {"X-API-KEY": "your-api-key-here"}
backoff = 1.0
for _ in range(10):
r = httpx.get(url, headers=headers)
if r.status_code != 429:
r.raise_for_status()
break
body = r.json()
wait = body.get("wait_in_seconds")
if wait is not None:
time.sleep(float(wait))
continue
reset = r.headers.get("X-RateLimit-Reset")
if reset is not None:
time.sleep(float(reset))
continue
time.sleep(backoff + random.random())
backoff = min(backoff * 2, 60)
JavaScript example¶
This example assumes fetch is available (modern browsers or Node 18+).
(async () => {
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const url = "https://tango.makegov.com/api/contracts/?limit=1";
const headers = { "X-API-KEY": "your-api-key-here" };
let backoffMs = 1000;
for (let i = 0; i < 10; i++) {
const r = await fetch(url, { headers });
if (r.status !== 429) {
if (!r.ok) throw new Error(`HTTP ${r.status}`);
break;
}
const body = await r.json();
if (body.wait_in_seconds != null) {
await sleep(body.wait_in_seconds * 1000);
continue;
}
const reset = r.headers.get("X-RateLimit-Reset");
if (reset != null) {
await sleep(Number(reset) * 1000);
continue;
}
await sleep(backoffMs + Math.random() * 250);
backoffMs = Math.min(backoffMs * 2, 60_000);
}
})().catch((err) => {
console.error("Request failed:", err);
});
Reduce calls (and avoid limits) in practice¶
- Use response shaping (
shape=) to avoid extra “follow-up” requests. See the Response Shaping Guide. - Paginate responsibly; avoid re-fetching the same pages repeatedly.
- Cache hot lookups on your side when appropriate (e.g. “entity by UEI”).
- Prefer webhooks for event-driven updates instead of polling where possible.