Skip to content
Rate Limits

Rate Limits & Quotas

Every account gets 3,000 API requests per minute per API key, and your proxy traffic runs on bandwidth that never expires. Here's how the limits work and how to stay well inside them.

Limits at a Glance

The API limit is the same on every account: 3,000 requests per minute per API key (requests without a key count per IP address). Proxy traffic itself is metered by bandwidth, not by request.

LimitLightBasicAdvancedMobile
API requests/min3,000 per key3,000 per key3,000 per key3,000 per key
BandwidthPay-as-you-goPay-as-you-goPay-as-you-goPay-as-you-go
Sticky session length (dashboard)No length setting1–60 min1–60 minUp to 240 min

Via the API, Basic and Advanced take the session length as sessionDuration (seconds). The mobile sticky interval is set in the dashboard. ISP IPs are static, and datacenter lines are rotating or sticky.

Rate Limit Response Headers

Every API response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (seconds until the window resets) headers. When you exceed the rate limit, the API returns a 429 Too Many Requests response with a Retry-After header:

python
import requests
import time
response = requests.get(
"https://api.zentislabs.com/api/v1/account",
headers={"X-API-Key": "YOUR_API_KEY"}
)
print("Requests left this minute:", response.headers.get("RateLimit-Remaining"))
# If rate limited
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 5))
print(f"Rate limited. Retry after {retry_after} seconds.")
time.sleep(retry_after)

Bandwidth Tracking

bash
# Check your bandwidth balance for each product
curl -H "X-API-Key: YOUR_API_KEY" \
"https://api.zentislabs.com/api/v1/usage"
# Response (abridged)
{
"success": true,
"products": {
"light": { "totalGb": 0, "usedGb": 0, "remainingGb": 0 },
"basic": { "totalGb": 10, "usedGb": 3.22, "remainingGb": 6.78 },
"advanced": { "totalGb": 0, "usedGb": 0, "remainingGb": 0 },
"mobile": { "totalGb": 1, "usedGb": 0.18, "remainingGb": 0.82 },
"datacenter": { "totalGb": 0, "usedGb": 0, "remainingGb": 0 }
},
"updatedAt": "2026-09-24T12:00:00.000Z"
}

Handling 429 Rate Limits

python
import requests
import time
API = "https://api.zentislabs.com/api/v1"
HEADERS = {"X-API-Key": "YOUR_API_KEY"}
def api_get(path: str) -> requests.Response:
"""Call the API, waiting out 429 responses automatically."""
while True:
response = requests.get(f"{API}{path}", headers=HEADERS, timeout=30)
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 5))
print(f"Rate limited. Waiting {retry_after}s...")
time.sleep(retry_after)
continue
return response
usage = api_get("/usage").json()
print(usage["products"]["basic"]["remainingGb"])
typescript
async function apiGet(path: string): Promise<Response> {
while (true) {
const response = await fetch(`https://api.zentislabs.com/api/v1${path}`, {
headers: { "X-API-Key": process.env.ZENTISLABS_API_KEY! },
});
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get("Retry-After") || "5", 10);
console.log(`Rate limited. Retrying in ${retryAfter}s...`);
await new Promise((r) => setTimeout(r, retryAfter * 1000));
continue;
}
return response;
}
}
const { products } = await (await apiGet("/usage")).json();
console.log(products.basic.remainingGb);

Concurrency Management

python
import asyncio
import aiohttp
# Cap concurrency in your client so traffic ramps up smoothly
CONCURRENCY_LIMIT = 100 # tune for your target site
async def scrape_with_concurrency(urls: list[str]):
semaphore = asyncio.Semaphore(CONCURRENCY_LIMIT)
async def fetch(session, url):
async with semaphore:
proxy = "http://USERNAME:PASSWORD_country-us@basic.resi.zentislabs.com:12321"
async with session.get(url, proxy=proxy) as resp:
return await resp.text()
async with aiohttp.ClientSession() as session:
tasks = [fetch(session, url) for url in urls]
results = await asyncio.gather(*tasks, return_exceptions=True)
success = sum(1 for r in results if not isinstance(r, Exception))
print(f"Completed: {success}/{len(urls)}")
return results

Best Practices

Monitor rate limit headers

Check RateLimit-Remaining to proactively slow down before hitting limits.

Use connection pooling

Reuse TCP connections with HTTP keep-alive to reduce overhead.

Implement backoff

Use exponential backoff with jitter when you receive 429 responses.

Distribute load

Spread requests evenly over time instead of bursting.