Limits by Tier
Rate limits are enforced per API key on a rolling 24-hour window. Limits reset at midnight UTC.
Explorer
3 / day
3 requests per 24 hours
Free
Starter
25 / day
25 requests per 24 hours
$99/mo
Professional
100 / day
100 requests per 24 hours
$249/mo
Business
∞
Unlimited requests
$499/mo
Note
The /health endpoint is not rate-limited. Marketplace management endpoints (/marketplace/*) have separate limits and do not count against your daily quota.
Rate Limit Headers
Every API response includes rate limit information in the response headers.
| Header | Description | Example |
X-RateLimit-Limit | Maximum requests allowed per day for your tier | 100 |
X-RateLimit-Remaining | Requests remaining in the current window | 73 |
X-RateLimit-Reset | Unix timestamp when the rate limit window resets (midnight UTC) | 1740528000 |
Retry-After | Seconds until you can retry (only on 429 responses) | 3600 |
Handling 429 Too Many Requests
When you exceed your rate limit, the API returns a 429 status code with a JSON error body and a Retry-After header.
429 Response Example
Response
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
X-RateLimit-Limit: 25
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1740528000
Retry-After: 3600
{
"detail": "Rate limit exceeded. 25/25 requests used today. Resets at midnight UTC.",
"upgrade_url": "https://thinkkits.com/pricing"
}
Important
Do not retry immediately on a 429. Always respect the Retry-After header. Clients that repeatedly ignore rate limits may be temporarily blocked.
Code Examples
curl
bash
# Check remaining quota in response headers
curl -si "https://api.thinkkits.com/schools/search?q=Springfield" \
-H "X-API-Key: tk_live_abc123" \
| grep -i "x-ratelimit"
# Output:
# X-RateLimit-Limit: 100
# X-RateLimit-Remaining: 99
# X-RateLimit-Reset: 1740528000
JavaScript (fetch with retry)
JavaScript
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const res = await fetch(url, options);
// Log remaining quota
const remaining = res.headers.get('X-RateLimit-Remaining');
console.log(`Requests remaining: ${remaining}`);
if (res.status !== 429) return res;
// Respect Retry-After header
const retryAfter = parseInt(res.headers.get('Retry-After') || '60');
console.warn(`Rate limited. Retrying in ${retryAfter}s...`);
await new Promise(r => setTimeout(r, retryAfter * 1000));
}
throw new Error('Max retries exceeded');
}
// Usage
const res = await fetchWithRetry(
'https://api.thinkkits.com/schools/search?q=Springfield',
{ headers: { 'X-API-Key': 'tk_live_abc123' } }
);
const data = await res.json();
Python (requests with retry)
Python
import time
import requests
API_KEY = "tk_live_abc123"
BASE_URL = "https://api.thinkkits.com"
def api_request(endpoint, params=None, max_retries=3):
headers = {"X-API-Key": API_KEY}
for attempt in range(max_retries):
resp = requests.get(f"{BASE_URL}{endpoint}", headers=headers, params=params)
# Log remaining quota
remaining = resp.headers.get("X-RateLimit-Remaining", "?")
print(f"Requests remaining: {remaining}")
if resp.status_code != 429:
resp.raise_for_status()
return resp.json()
# Respect Retry-After header
retry_after = int(resp.headers.get("Retry-After", "60"))
print(f"Rate limited. Waiting {retry_after}s...")
time.sleep(retry_after)
raise Exception("Max retries exceeded")
# Usage
schools = api_request("/schools/search", {"q": "Springfield"})
Best Practices
- Monitor your usage. Check
X-RateLimit-Remaining headers proactively. Don't wait for a 429 to learn you're at your limit.
- Cache aggressively. School profiles and funding data change infrequently. Cache responses for 24 hours to reduce API calls.
- Use bulk endpoints. The
/schools/bulk-import endpoint lets you look up many schools in one request (Business tier), saving rate limit budget.
- Implement exponential backoff. If you get a 429, wait the
Retry-After duration. If you get a second 429, double the wait time.
- Upgrade when you need more. If you consistently hit limits, upgrading from Starter ($99/mo) to Professional ($249/mo) gives you 4x the daily quota. Business tier has no limits.
- Use webhooks instead of polling. Instead of polling for changes, subscribe to customer webhooks to receive events in real-time.
Upgrading Your Tier
Need more requests? Upgrade your plan through the billing portal or API.
curl
# Check current usage
curl "https://api.thinkkits.com/marketplace/usage" \
-H "X-API-Key: tk_live_abc123"
# Upgrade tier
curl -X POST "https://api.thinkkits.com/marketplace/keys/abc123/upgrade" \
-H "X-API-Key: tk_live_abc123" \
-H "Content-Type: application/json" \
-d '{"new_tier": "PREMIUM"}'