Лимиты запросов
Crawlbase применяет лимиты параллелизма на токен, а не запросов в минуту. Отправляйте так быстро, как хотите — главное, держите количество одновременных запросов в пределах вашего потолка.
Лимиты по умолчанию
Каждый аккаунт начинается с одинаковых щедрых лимитов по умолчанию. Их не нужно запрашивать - они уже действуют на каждом токене с момента регистрации.
| Лимит | По умолчанию | Область |
|---|---|---|
| Одновременные запросы | 20 | на токен |
| Запросов в секунду | ~ 20 (производное) | на токен |
| Всего запросов в месяц | до 51,000,000 | на токен |
| Тайм-аут одного запроса | 90 секунд | на запрос |
| Размер очереди Crawler | 100,000 URL | на crawler |
«Одновременные» означает выполняющиеся в один и тот же момент. Если каждый запрос занимает 2 секунды и вы держите 20 параллельно, то получите примерно 10 запросов в секунду в устойчивом режиме - это даёт около 864 тысяч запросов в день на токен.
Не пытайтесь ограничивать скорость до «X запросов в секунду» - просто ограничьте пул воркеров значением 20 и позвольте задержке запросов определять пропускную способность. Так проще, и это соответствует тому, как лимит на самом деле работает на стороне сервера.
Что происходит при превышении лимита
Если вы превысите потолок параллелизма, Crawlbase ответит HTTP 429 Too Many Requests. Запрос отклоняется - он не ставится в очередь - поэтому вам следует повторить его с задержкой (backoff).
// HTTP/1.1 429 Too Many Requests
// Retry-After: 1
// X-Crawlbase-Concurrency: 20
{ "error": "Concurrency limit reached", "limit": 20 }Заголовок Retry-After сообщает минимальное число секунд до повторной попытки. Всегда соблюдайте его.
Корректная обработка лимитов
Правильный паттерн — экспоненциальный backoff с jitter. В большинстве HTTP-клиентов он встроен; ниже минимальный пример, если вам нужно реализовать его самостоятельно.
import time, random
from crawlbase import CrawlingAPI
api = CrawlingAPI({'token': 'YOUR_TOKEN'})
def crawl_with_retry(url, attempts=5):
for i in range(attempts):
res = api.get(url)
if res['status_code'] != 429:
return res
wait = (2 ** i) + random.random()
time.sleep(wait)
raise RuntimeError('Rate limit exhausted')const { CrawlingAPI } = require('crawlbase');
const api = new CrawlingAPI({ token: 'YOUR_TOKEN' });
async function crawlWithRetry(url, attempts = 5) {
for (let i = 0; i < attempts; i++) {
const res = await api.get(url);
if (res.statusCode !== 429) return res;
const wait = (2 ** i) * 1000 + Math.random() * 1000;
await new Promise(r => setTimeout(r, wait));
}
throw new Error('Rate limit exhausted');
}Для нагрузок, которые постоянно упираются в потолок, используйте пул воркеров вместо fire-and-forget. Ограничьте пул вашим лимитом параллелизма — и вы никогда не увидите 429.
Масштабирование за пределы значений по умолчанию
Три варианта, когда 20 одновременных недостаточно:
Лучшие практики
- Ограничивайте пулы воркеров лимитом вашего токена. Не превышайте подписку в расчёте на 429-е ответы - отклонённый запрос всё равно тратит время на сетевой обмен.
- Используйте
async=trueдля медленных целей. Длительные crawl-запросы с JS-рендерингом занимают слот параллелизма на всё время выполнения. Режим async освобождает слот мгновенно и доставляет результат через webhook. - Всегда соблюдайте
Retry-After. Игнорирование этого заголовка лишь создаёт ещё больше 429 и сжигает сетевые round-trip. - Добавляйте jitter к повторам. Если 50 воркеров одновременно повторят запрос ровно через 1 секунду после кратковременного всплеска, вы получите такой же всплеск снова.
- Настройте оповещения на устойчивый уровень 429. Случайный 429 — это нормально. Устойчивый уровень 5%+ означает, что вам нужен более высокий параллелизм или более умное планирование.