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 number of jobs your plan delivers each billing period. Every job in a response counts one; empty responses are free. 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 job quota, in jobs. Remaining
already accounts for the jobs in the response it arrives with:
X-RateLimit-Limit: 50000
X-RateLimit-Remaining: 48750
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 |
429 also carries a Retry-After header, in seconds. On the burst limit it matches the body's retryAfter. On the monthly quota it is a suggested wait of one minute to one hour before checking again; resetAt in the body and X-RateLimit-Reset give the exact reset time.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 job limit exceeded",
"error": "Quota Exceeded",
"resetAt": 1735257660,
"upgradeUrl": "https://jobven.com/dashboard/billing"
}
upgradeUrl is the billing page, where you can move to a plan with a larger allowance. An
account on agreed terms has no upgradeUrl; contact us to change its limit.
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- Omittinglimitreturns your plan's maximum per request: 25 on Free, 50 on Starter, 100 on Growth and above. You pay per job delivered, not per request, so a larger page costs the same as the same jobs fetched in smaller pages; it just takes fewer round trips against the per-second limit. - Use incremental sync - Fetch only the jobs listed since your last run with
listedAfter. A poll that returns nothing costs nothing, so you can check as often as the per-second limit allows. 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.