К какому сервису принадлежат лимиты
Названия легко перепутать. Groq — компания, размещающая модели на собственном оборудовании. Grok — семейство моделей от xAI. Этот сайт не относится ни к тому, ни к другому: это API, совместимое с OpenAI, для одной модели с открытыми весами (идентификатор модели uncensored), запущенной независимым провайдером. Поэтому для самого лимита запросов API Groq, например, количество запросов или токенов в минуту по определённому тарифу, откройте консоль Groq или документацию и прочитайте цифры для вашего аккаунта. Они меняются, и я не буду их угадывать.
Далее приведены точные данные для нашего API, и эти привычки переносятся на любого провайдера: уважайте заголовки лимитов, повторяйте запросы только при правильных ошибках и ограничивайте собственный параллелизм до того, как это сделает сервер. Если вы пришли сюда для настройки Grok NSFW, наше руководство по интеграции также охватывает сопутствующие ошибки.
Лимиты вкратце
| Лимит | Полный ключ | Пробный ключ |
|---|---|---|
| Параллельные запросы | 8 | 2 |
| Запросов в минуту | 300 | 60 |
| Тело запроса | 8 МБ | 8 МБ |
| Контекстное окно (промпт плюс ответ) | 100 000 токенов | 100 000 токенов |
Два из них — это лимиты запросов в строгом смысле: запросы в минуту и параллельные запросы. Два других — это лимиты размера одного запроса, и они не зависят от лимитов запросов. Запрос с промптом на 90 000 токенов считается одним запросом против 300 в минуту, так же как и промпт в одну строку, хотя он займёт больше времени и удержит свой слот дольше.
Пробный баланс в размере $0,50 активируется одним нажатием на странице API-ключ после входа, и к нему применяются лимиты пробного периода. Полные лимиты ключа действуют после пополнения.
Три вида 429
Все ошибки лимита приходят как HTTP 429, но они означают разные вещи и требуют разных реакций.
rate_limited: вы отправили больше запросов, чем разрешено в минуту. Снизьте скорость; окно составляет минуту, поэтому равномерное распределение запросов помогает больше, чем длинная пауза.concurrency: слишком много запросов были запущены одновременно. Подождите, пока завершится один из предыдущих. Это часто встречается при потоковой передаче, где каждый запрос живёт до конца генерации.- Временная перегрузка: также 429, с заголовком
Retry-After: 5. Это не ваша вина и не связано с лимитами вашего ключа. Подождите указанные 5 секунд и повторите запрос.
Отказавшие запросы не тарифицируются, поэтому цикл повтора стоит только один запрос, который в итоге завершится успешно. Ответы также содержат заголовки X-RateLimit-Limit-Requests и X-RateLimit-Limit-Concurrency, чтобы ваш клиент мог читать свои собственные пределы, а не хардкодить их, а также X-Balance-USD для оставшегося кредита и X-Request-Id, который следует логировать и указывать при обращении в поддержку.
Математика пропускной способности: почему 300 в минуту трудно достичь
Два лимита взаимодействуют. С 8 слотами параллельных запросов максимальное количество завершённых запросов в минуту равно 8 x 60, делённому на среднюю длительность запроса в секундах. Чтобы фактически достичь 300 запросов в минуту, вам нужны запросы, которые завершаются в среднем за 8 x 60 / 300 = 1,6 секунды. Более медленные запросы ограничиваются параллелизмом в первую очередь.
| Среднее время запроса | Максимум в минуту, полный ключ (8 слотов) | Максимум в минуту, пробный (2 слота) |
|---|---|---|
| 2 секунды | 240 | 60 |
| 10 секунд | 48 | 12 |
| 20 секунд | 24 | 6 |
Это арифметические пределы для гипотетического времени запроса, а не измерения нашего сервиса; замеряйте свои запросы. Вывод заключается в том, что длинная креативная генерация длительностью 20 секунд на полном ключе достигает максимума в 24 запроса в минуту, что далеко от разрешённых 300. Если вам нужна большая пропускная способность, сократите ответы с помощью max_tokens или запрашивайте меньше текста за вызов, а не надейтесь на более высокий лимит запросов.
Клиент, обрабатывающий все три случая
Этот клиент Python ограничивает собственный параллелизм с помощью семафора, читает заголовок Retry-After, если он присутствует, и использует экспоненциальную задержку (backoff) с небольшим джиттером в противном случае. Он использует официальный пакет openai.
import random
import threading
import time
from concurrent.futures import ThreadPoolExecutor
from openai import OpenAI, RateLimitError
client = OpenAI(
base_url="https://api.groknsfw.top/v1",
api_key="YOUR_KEY",
max_retries=0, # we handle retries ourselves
)
slots = threading.BoundedSemaphore(8) # use 2 on the trial key
def ask(prompt, tries=6):
for attempt in range(tries):
with slots:
try:
r = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": prompt}],
max_tokens=400,
)
return r.choices[0].message.content
except RateLimitError as e:
wait = e.response.headers.get("retry-after")
body = e.response.text
# sleep outside the slot so others can proceed
delay = float(wait) if wait else min(2 ** attempt, 30)
if "rate_limited" in body:
delay = max(delay, 5)
time.sleep(delay + random.random())
raise RuntimeError("still rate limited after retries")
with ThreadPoolExecutor(max_workers=8) as pool:
results = list(pool.map(ask, ["Write one line about the sea."] * 20))
print(len(results))
Та же идея в Node — это небольшая очередь, которая позволяет не более 8 ожидающих промисов, а проверка curl ниже выводит заголовки лимитов, чтобы вы могли подтвердить пределы своего ключа:
curl -s -D - -o /dev/null https://api.groknsfw.top/v1/models \
-H "Authorization: Bearer YOUR_KEY" | grep -i "x-"Потоковая передача, длинные промпты и контекст
Потоковый запрос с stream: true удерживает свой слот параллельных запросов до прибытия последнего события. Это означает, что медленный читатель учитывается против вас: если ваш код приостанавливается при обработке каждого фрагмента, слот остаётся занятым. Потребляйте поток оперативно.
Длинные промпты взаимодействуют с лимитами двумя способами. Во-первых, они занимают больше времени на обработку, поэтому снижают вашу пропускную способность по формуле выше. Во-вторых, они должны помещаться в окно 100 000 токенов вместе с ответом, поэтому оставляйте запас. Обрезка старых сообщений чата помогает решить обе проблемы, и та же обрезка снижает ваш счёт: при цене $0,15 за 1 млн входных токенов история на 20 000 токенов стоит $0,003 каждый раз, когда вы её отправляете.
Детали формата запроса приведены в быстром старте, а тарифы — на странице с ценами.
Показ лимитов вашим собственным пользователям
Лимит запросов — это также решение продукта. Если ваше приложение находится между конечными пользователями и API, сырая ошибка 429 никогда не должна доходить до них. Решите заранее, что они увидят вместо этого.
- Для чат-приложений, покажите короткое состояние «печатает…» и поставьте сообщение в очередь. Ожидание в несколько секунд незаметно для большинства пользователей, если на экране что-то происходит.
- Для пакетных инструментов, показывайте прогресс в виде количества завершённых элементов и позволяйте задаче работать в фоновом режиме. Повторные запросы являются внутренними и не должны появляться как ошибки.
- Для общих развертываний, предоставьте каждому пользователю небольшое ограничение на пользователя в вашем собственном коде, чтобы один интенсивный пользователь не занял все 8 слотов для остальных.
Записывайте три вещи для каждой ошибки: X-Request-Id, код ошибки и количество запросов, которые ваше приложение обрабатывало одновременно в тот момент. С их помощью вы можете отличить неисправного клиента от реальной нагрузки за несколько минут. Если проблема выглядит как серверная, идентификатор запроса — это то, что поддержка спросит в первую очередь.
Наконец, намеренно протестируйте путь отказа. Запустите своего клиента с намеренно низким семафором, а затем с высоким, и наблюдайте, что показывают логи. Цикл повтора, который никогда не срабатывал, — это цикл повтора, которому вы не можете доверять. Пробный ключ — удобное место для этого, так как с только 2 параллельными запросами легко получить ошибку concurrency с коротким скриптом и увидеть, как реагирует ваш код, при стоимости в несколько десятых цента за запрос.
Распространенные ошибки
Несколько сценариев вызывают большинство проблем с 429, о которых сообщают пользователи.
- Мгновенные повторы. Цикл, который отправляет запрос повторно при каждой ошибке, превращает один момент превышения лимита в шквал. Подождите, добавьте джиттер и ограничьте количество попыток.
- Повтор всех запросов подряд. Повторять стоит только запросы с кодом 429 и временные ошибки сервера. Невалидный запрос или неверный ключ будут завершаться с ошибкой одинаковым образом каждый раз.
- Упущение потоков при подсчёте. Десять открытых окон чата на полном ключе могут быть десятью потоками, что на два больше, чем 8 слотов.
- Отсутствие тайм-аута. Виснущее соединение удерживает слот занятым. Установите разумный тайм-аут для каждого запроса в вашем HTTP-клиенте.
- Расчёт под среднюю нагрузку. Трафик поступает волнами. Планируйте под вашу самую загруженную минуту, а не под среднее значение за день.
Планирование мощности перед запуском
Планируйте от обратного, исходя из пиковой нагрузки. Если вы ожидаете 30 пользователей, общающихся одновременно, и каждый ответ передаётся потоком около 15 секунд, вам понадобится 30 слотов в момент, когда все они отправят запрос, а полный ключ имеет только 8. Остальные поставьте в очередь в своём приложении, покажите короткое ожидание и обрабатывайте их по мере освобождения слотов. С 8 слотами и ответами длительностью 15 секунд вы можете обработать 8 x 60 / 15 = 32 ответа в минуту.
Для пакетных задач запустите фиксированный пул воркеров, равный вашему лимиту параллелизма, и позвольте логике повтора обрабатывать случайную перегрузку. Вместо обхода лимитов создайте очередь: это даст предсказуемое поведение и позволит показывать реальное время ожидания вашим пользователям.
