Ключевые выводыБюджет на IP — 1 200 весов в минуту, а не 1 200 запросов. Типичный info-вызов стоит 20 весов, поэтому практический потолок — около 60 вызовов в минуту.
- Бюджет на IP — 1 200 весов в минуту, а не 1 200 запросов. Типичный info-вызов стоит 20 весов, поэтому практический потолок — около 60 вызовов в минуту.
- Шесть «дешёвых» эндпоинтов чтения (
l2Book,allMids,clearinghouseState,orderStatus,spotClearinghouseState,exchangeStatus) стоят по 2 веса;userRole— 60; объёмные ответы добавляют вес сверх базовой цены. - Второй, независимый лимит привязан к кошельку: наторгованный объём задаёт базовую квоту из расчёта 1 действие за 1 USDC поверх стартового буфера в 10 000 запросов, а действие
reserveRequestWeightпозволяет докупить действия напрямую по 0,0005 USDC за запрос. Он считает только действия — info-запросы не учитываются. - Ни в ответах 200, ни в 429 нет никаких заголовков о лимитах, а тело 429 — буквально
nullбезRetry-After; мы проверили это живым прогоном. Единственный способ знать остаток бюджета — считать вес на своей стороне. - Батчинг резко удешевляет IP-бюджет — пакет из 40 ордеров стоит 2 веса вместо 40, — но адресный лимит всё равно засчитает его как 40 запросов.
- Дополнительные IP-адреса умножают бюджет чтения для параллельных нагрузок, но никак не расширяют лимит действий кошелька: он следует за адресом в блокчейне, а не за соединением.
- Ни один из двух бюджетов сервер не сообщает по ходу работы, так что считать нужно оба на своей стороне: вес — по каждому исходящему IP, действия — по кошельку.
Краткое содержание подготовлено с помощью ИИ.
Сколько на самом деле стоит запрос
Число, которое все цитируют, — 1 200 в минуту на IP — это бюджет веса, а не счётчик запросов. Каждый REST-вызов к api.hyperliquid.xyz списывает вес из общего пула, накрывающего и /info, и /exchange, и размер списания различается в шестьдесят раз в зависимости от того, что вы вызываете. Сторонние гайды нередко упрощают это до «1 200 запросов в минуту» — и именно из-за такого упрощения сборщик данных внезапно упирается в стену на одном запросе в секунду: вес info-запроса по умолчанию равен 20, так что документированный бюджет — это около 60 типичных info-вызовов в минуту, а не 1 200.
Официальная документация по лимитам задаёт три ценовых уровня для /info и формулу для /exchange:
| Вызов | Вес | Сколько вызовов в минуту допускает бюджет |
|---|---|---|
l2Book, allMids, clearinghouseState, orderStatus, spotClearinghouseState, exchangeStatus |
2 | 600 |
Любой другой документированный info-запрос (meta, candleSnapshot, userFills, openOrders, …) |
20 | 60 |
userRole |
60 | 20 |
Одиночное действие /exchange (ордер, отмена, изменение) |
1 | 1 200 |
| Батч из n ордеров | 1 + floor(n/40) | — |
| Запрос к explorer API | 40 | 30 |
В этой таблице два неожиданных места. Во-первых, торговать дёшево, а читать дорого: одиночный ордер стоит 1 вес, а вызов метаданных, чтобы этот ордер корректно выставить, — 20. Модель поощряет ботов, которые получают рыночные данные по WebSocket, а REST трогают только ради действий. Во-вторых, фактический расход веса в минуту зависит от размера ответов, а не только от числа вызовов: у ряда эндпоинтов есть надбавки сверх базовой цены — им ниже посвящён отдельный раздел.
Конкретный расчёт: опрос meta раз в секунду стоит 20 × 60 = 1 200 весов — весь минутный бюджет на один цикл метаданных. Тот же опрос allMids обойдётся в 120, оставив свободными 90% бюджета.
Два лимита, действующих одновременно
IP-бюджет — только половина картины. Пользовательские лимиты Hyperliquid отдельно отслеживают адрес кошелька, и две системы отвечают на разные вопросы: лимит по IP защищает фронтенд API от потока запросов, а адресный назначает цену праву отправлять действия — ордера, отмены, изменения — пропорционально тому, сколько вы реально торгуете.
Формула адресного лимита: каждый 1 USDC наторгованного объёма с момента появления адреса даёт 1 запрос, и каждый адрес стартует с буфером 10 000. Боту, выставляющему ордера по 100 USDC, для безубыточности этого бюджета нужен fill rate около 1%. Когда запас исчерпан, адресу разрешён один запрос в 10 секунд — этого хватает доковылять, но не торговать. Стену смягчают два предохранителя: у отмен накопительный лимит выше — min(limit + 100 000, limit × 2), так что снять свои открытые ордера можно всегда, — а info-запросы не считаются вовсе: залимиченный адрес свободно читает собственное состояние. Суб-аккаунты считаются отдельными пользователями со своими буферами.
Наторгованный объём больше не единственный способ поднять этот потолок. Действие reserveRequestWeight на /exchange резервирует дополнительные действия напрямую — по 0,0005 USDC за запрос, списание идёт с Perps-баланса, — а необязательное поле destination позволяет оплатить их другому существующему L1-адресу вместо своего. То есть у текущей модели два входа: накопленный объём задаёт базовую квоту, а зарезервированный вес расширяет её по требованию. Именно так новая стратегия покупает запас до того, как заработает торговую историю под него.
В отличие от IP-бюджета, этот лимит наблюдаем. Info-запрос userRateLimit возвращает точное состояние любого адреса:
curl -s https://api.hyperliquid.xyz/info \
-H "Content-Type: application/json" \
-d '{"type": "userRateLimit", "user": "0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036"}'
Ответ, который мы получили при подготовке статьи, — по одному из самых активных адресов биржи:
{
"cumVlm": "15466177056.6800003052",
"nRequestsUsed": 2682032,
"nRequestsCap": 15466187056,
"nRequestsSurplus": 0
}
nRequestsCap соответствует целой части квоты по наторгованному объёму плюс стартовый буфер 10 000 — дробные USDC в cumVlm доли действия не покупают. Читать этот потолок нужно как пожизненный лимит, а не как остаток: два соседних поля уже учитывают зарезервированную ёмкость, nRequestsUsed отдаётся как max(0, использовано − зарезервировано), а nRequestsSurplus — как max(0, зарезервировано − использовано). Для свежего кошелька тот же вызов вернёт потолок 10 000 — это стоит проверить до деплоя, а не после.
Правило батчинга связывает две системы, и работает оно в противоположные стороны. Пакет из 40 ордеров стоит 1 + floor(40/40) = 2 веса против IP-бюджета — в двадцать раз дешевле, чем слать их по одному, — но адресная бухгалтерия всё равно засчитает его как 40 запросов. Батчинг бережёт бюджет IP, а бюджет адреса растёт только от наторгованного объёма или от веса, зарезервированного через reserveRequestWeight. Иными словами, лимиты /exchange не обходятся упаковкой: каждый ордер в итоге оплачивается исполнением, зарезервированным весом или тающим буфером в 10 000.
Эндпоинты, которые стоят дороже, чем кажется
Базовый вес info-эндпоинта 20 — для части вызовов лишь входная цена. Тринадцать «исторических» эндпоинтов — recentTrades, historicalOrders, userFills, userFillsByTime, fundingHistory, userFunding, nonUserFundingUpdates, twapHistory, userTwapSliceFills, userTwapSliceFillsByTime, delegatorHistory, delegatorRewards, validatorStats — добавляют 1 вес за каждые 20 элементов в ответе. Ответ на 500 элементов стоит поэтому 20 базовых + 25 надбавки = 45 весов. Размеры бывают заметно больше: текущая карточка userFills допускает до 2 000 последних сделок, а userFillsByTime отдаёт не больше 2 000 за ответ из 10 000 доступных последних — при том что общее замечание о пагинации на странице лимитов по-прежнему говорит о максимум 500 элементах для запросов с временным диапазоном. Эти два утверждения соседствуют в документации, так что считайте бюджет по фактически полученному ответу, а не по предполагаемому размеру страницы: цикл бэкфилла, идущий по истории страница за страницей, способен сжигать вес в несколько раз быстрее базовой цены.
У candleSnapshot надбавка мягче — 1 вес за каждые 60 свечей — и своя жёсткая граница: доступны только последние 5 000 свечей на пару монета-интервал, точка. Забрать их все одним вызовом стоит порядка 100 весов — как разовый прогрев это нормально. Чего эндпоинт не умеет ни за какую цену — дотянуться до более глубокой истории: startTime в далёком прошлом просто не сработает, так что исследовательский датасет придётся либо накапливать собственным сборщиком, пока данные свежие, либо брать не из API.
Запросы к explorer API стоят по 40, blockList добавляет ещё 1 за блок, а давно не запрашивавшиеся блоки могут взвешиваться тяжелее — за массовой историей чейна документация отправляет в публичный S3-бакет, а не в API. И одна странность, о которой стоит знать: userRole — тривиальная с виду проверка, является ли адрес пользователем, вольтом или суб-аккаунтом, — стоит 60 весов, как три вызова метаданных. Кэшируйте.
Как выглядит настоящий 429: мы проверили
Биржи редко описывают собственные ответы троттлинга, и сторонние разборы заполняют пробел выдумками — нам попадались гайды, утверждающие, что Hyperliquid возвращает заголовок Retry-After и поле retry_after в JSON и даже «приостанавливает API-ключи». Обычной аутентификации по API-ключу у Hyperliquid нет вообще: действия подписываются самим кошельком или одобренным API-кошельком (он же agent wallet). Поэтому 7 августа 2026 года мы прощупали мейннет-эндпоинт /info с одной машины запросом meta (вес 20) в трёх темповых профилях и записали, что реально пришло в ответ:
| Темп | Номинальный вес/мин | Результат |
|---|---|---|
| 0,8–1 запрос/с | 960–1 200 — на границе или чуть ниже | 194 запроса за два прогона, ни одного 429 |
| ~2,8 запроса/с | ~3 360 | 276 запросов прошли, первый 429 на 98-й секунде; одиночный повтор через 6 секунд вернул 200 |
| Залп из 80 запросов в 8 параллельных соединений | — | TCP-соединения сброшены до какого-либо HTTP-статуса; прошли 3 из 80 |
Рамки этого прогона намеренно узкие: одна машина, один адрес, один эндпоинт, одна дата. В каждом профиле мы останавливались на первом отказе, а не наращивали нагрузку дальше, так что цифры описывают поведение API в конкретном окне, а не гарантированную кривую ограничений. Воспроизводить перерасход на боевой площадке и незачем: полезная половина результата — форма самого 429, а она видна по одному отклонённому запросу.
В более широком продолжении — эксперименте с лидербордом и позициями кошельков Hyperliquid — мы проверили clearinghouseState, allMids и meta на датацентровых и ISP-адресах. Первый 429 появился после расхода в 1,8–2,3 раза больше документированного бюджета, но устойчивый плановый темп составил 574 вызова clearinghouseState в минуту с одного датацентрового адреса — близко к документированным 600. Это подтверждает практическое правило: планируйте по опубликованному бюджету, а дополнительный запас считайте допуском на всплески, а не надёжной мощностью.
Три практических вывода. Первый — анатомия самого 429: тело — четыре байта null, и ни Retry-After, ни лимитных заголовков, ничего пригодного для парсинга. Эта биржа, в отличие от Binance с её счётчиками X-MBX-USED-WEIGHT, никогда не сообщает, сколько бюджета у вас осталось — ни до стены, ни после. Единственный индикатор остатка, который у вас будет, — тот, что вы построите на своей стороне. Второй — лимит не срабатывает гильотиной на 1 201-м весе: перерасход шёл полторы минуты до первого отказа, а одиночный повтор через шесть секунд прошёл. Это одно наблюдение, а не распределение времени восстановления: мы его не измеряли. На люфт в любом случае не стоит закладываться — планируйте под документированные 1 200, а всё, что мягче, пусть будет приятным сюрпризом. Третий — резкие всплески соединений отбиваются вообще ниже HTTP-слоя. Большинство неудачных попыток в залповом прогоне завершились сбросом соединения до какого-либо HTTP-ответа, и по клиентской ошибке нельзя установить, какой именно узел инициировал сброс: граница биржи, CDN перед ней или что-то на сетевом пути. Практическое следствие от этого не меняется: клиент получает socket error вместо HTTP-статуса, и retry-логика, завязанная на status_code == 429, не срабатывает никогда. Наш тестовый клиент вдобавок открывал новое TCP-соединение на каждый запрос, и одиночные сбросы попадались даже на темпе один запрос в секунду — обрабатывайте обе формы отказа и переиспользуйте соединения.
Раз сервер не даёт счётчиков, считать приходится так — бюджетчик на один процесс и один исходящий IP, демонстрационный уровень:
import time, requests
API = "https://api.hyperliquid.xyz/info"
BUDGET = 1_200 # документированный вес в минуту на IP
SAFETY = 0.9 # тратим не больше 90% — заголовков для сверки нет
session = requests.Session() # одно соединение: всплеск новых получает сброс
spent = [] # (монотонное время, вес) вызовов за последние 60 секунд
def acquire(weight):
now = time.monotonic() # не зависит от перевода системных часов
while spent and now - spent[0][0] > 60:
spent.pop(0)
if sum(w for _, w in spent) + weight > BUDGET * SAFETY:
return False
spent.append((now, weight))
return True
def info(payload, weight):
delay = 1
while True:
while not acquire(weight):
time.sleep(0.25)
try:
r = session.post(API, json=payload, timeout=10)
except (requests.ConnectionError, requests.Timeout): # сброс или зависание, статуса нет
time.sleep(delay); delay = min(delay * 2, 30)
continue
if r.status_code == 429: # Retry-After нет — отступаем вслепую
time.sleep(delay); delay = min(delay * 2, 30)
continue
r.raise_for_status()
return r.json()
Чего здесь сознательно нет: код рассчитан на один исходящий IP. Бюджет выдаётся на адрес, поэтому клиенту, ходящему через несколько прокси, нужен отдельный счётчик на каждый исходящий адрес, с ключом по прокси, — если направить в этот единственный глобальный список десять прокси, модель покажет десятую часть реальной ёмкости и начнёт душить работу без причины. Дальше: код доверяет вызывающей стороне правильный вес (включая надбавки за размер ответа — их вы узнаёте только после ответа, так что досписывайте задним числом), держит состояние в памяти процесса, так что рестарт забывает последнюю минуту трат, и ничего не координирует — нескольким воркерам за одним IP нужен общий счётчик в Redis или аналоге; этот паттерн мы разбирали в статье про Binance. Ордеров код не отправляет и состояния биржи не меняет, поэтому худший исход потерянного ответа — лишний повтор, а не задвоенная сделка.
Как остаться в рамках бюджета
Официальных способов снять нагрузку с REST у Hyperliquid достаточно. WebSocket выносит рыночные данные за пределы REST-бюджета целиком — одна подписка на l2Book заменяет шестьсот опросов по 2 веса в минуту — в рамках собственных лимитов на IP: 10 соединений, 30 новых соединений в минуту, 1 000 подписок, 2 000 сообщений, отправленных в Hyperliquid за минуту по всем соединениям, 100 одновременных post-сообщений в полёте и не больше 10 разных пользователей в пользовательских подписках. Батчинг сжимает стоимость действий на стороне IP, кэш ответов убивает повторные вызовы meta, а массовая история чейна живёт в S3-бакете, а не в explorer API. Бот, который читает по стриму, пишет батчами и кэширует метаданные, стену в 1 200 почти не видит.
После этих оптимизаций остаются нагрузки, которые параллельны по своей природе: аналитический сервис, опрашивающий clearinghouseState по сотням отслеживаемых кошельков; сборщик рыночных данных, снимающий стаканы по всем листингованным монетам; парк независимых ботов, которым не стоит делить один бюджет и одну точку отказа. IP-бюджет не растягивается — но умножается, потому что выдан на каждый IP-адрес. Честная граница здесь — адресный лимит действий: он следует за кошельком, и никакое количество IP его не поднимет — этот потолок двигают только наторгованный объём и вес, зарезервированный через reserveRequestWeight. Зато дополнительные адреса умножают всё, что меряется на IP: вес чтения, WebSocket-соединения, слоты подписок.
Ровно под такую нагрузку собраны наши прокси для криптобирж: выделенные IPv4-адреса, статичные на весь срок тарифа, каждый со своим бюджетом в 1 200 весов и своей квотой WebSocket, с HTTP, HTTPS, SOCKS4 и SOCKS5 на каждом адресе и доступом по белому списку IP. Один адрес на сборщик не даёт всплеску в одном воркере затормозить остальные — а сброшенному TCP-соединению уронить что-либо, кроме процесса, который его вызвал.