Handle Rate Limits
The Jobven API enforces two kinds of limits. Both return a 429 status when exceeded, but you handle them differently:
- Burst limit — a per-second cap on how fast you can send requests. Retry after a short pause.
- Monthly quota — the total calls included in your plan each billing period. Retrying won't help until the period resets.
The exact numbers for each plan are listed under Authentication → Rate Limiting.
Response Headers
Every response includes headers describing your monthly quota:
X-RateLimit-Limit: 30000
X-RateLimit-Remaining: 28750
X-RateLimit-Reset: 1735257660
| Header | Description |
|---|---|
X-RateLimit-Limit | Calls included this billing period. This is the limit actually enforced on your account, which is your plan's allowance unless a different one was agreed with us. |
X-RateLimit-Remaining | Calls left this billing period |
X-RateLimit-Reset | Unix timestamp (seconds) when your quota resets |
X-Request-Time-Ms | How long the request took to process |
Retry-After header. When you hit the burst limit, the 429 response body includes a retryAfter value (in seconds) instead.Read the header, not your plan's advertised allowance
X-RateLimit-Limit is the ceiling actually enforced on your account. If we agreed a
different limit with you, your plan's published number is not the one you are held to,
and the header is where that difference shows up. Every API response carries it, so there is
no separate call to make.
If you track usage programmatically, X-RateLimit-Limit and X-RateLimit-Remaining are the
only two numbers you need. Credits are no longer part of how a request is accepted or
rejected.
Telling the Two Limits Apart
Both limits return 429, so check the error field in the response body to decide how to react.
Burst limit (slow down and retry):
{
"statusCode": 429,
"message": "Rate limit exceeded",
"error": "Too Many Requests",
"retryAfter": 1
}
Monthly quota (stop and upgrade or wait for reset):
{
"statusCode": 429,
"message": "Monthly API call limit exceeded",
"error": "Quota Exceeded",
"resetAt": 1735257660
}
Handling 429 Responses
Retry on the burst limit, but stop on a quota error — retrying won't return data until your billing period resets.
JavaScript
async function fetchWithRetry(url, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, {
headers: { 'X-API-Key': process.env.JOBVEN_API_KEY }
});
if (response.status === 429) {
const body = await response.json();
// Monthly quota - retrying won't help until reset
if (body.error === 'Quota Exceeded') {
throw new Error('Monthly quota exhausted. Upgrade your plan or wait for reset.');
}
// Burst limit - wait and retry
const wait = body.retryAfter ?? 1;
console.log(`Rate limited. Waiting ${wait}s...`);
await new Promise(r => setTimeout(r, wait * 1000));
continue;
}
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return response;
}
throw new Error('Max retries exceeded');
}
Python
import os
import time
import requests
def fetch_with_retry(url, params=None, max_retries=3):
headers = {'X-API-Key': os.environ['JOBVEN_API_KEY']}
for attempt in range(max_retries):
response = requests.get(url, headers=headers, params=params)
if response.status_code == 429:
body = response.json()
# Monthly quota - retrying won't help until reset
if body.get('error') == 'Quota Exceeded':
raise Exception('Monthly quota exhausted. Upgrade your plan or wait for reset.')
# Burst limit - wait and retry
wait = body.get('retryAfter', 1)
print(f"Rate limited. Waiting {wait}s...")
time.sleep(wait)
continue
response.raise_for_status()
return response
raise Exception('Max retries exceeded')
Avoiding Rate Limits
- Use the maximum
limit- The default is 10 jobs per request, but your plan allows more: 25 on Free, 50 on Starter, 100 on Growth and above. One request counts as one call regardless of how many jobs come back, so requesting the maximum stretches your monthly quota and reduces the number of round trips. - Use incremental sync - Fetch only new jobs with
postedAfterinstead of everything. See Incremental Sync. - Cache responses - Don't fetch the same data repeatedly. Cache results for as long as makes sense for your use case.
- Watch your remaining quota - Monitor
X-RateLimit-Remainingand slow down before it reaches zero.