Name
Hyperliquid rate limits in practice: what a 24/7 collector taught us
Per-IP limits, which /info calls are heavy, what a 429 storm looks like from the inside, and the pacing rules that stopped us punching holes in our own data.
We run a collector that polls the public Hyperliquid API around the clock for every xyz market, and a second process that occasionally backfills history for research. For three months those two shared one IP address. This note is what we learned when they collided.
The limit is per IP, not per process
Hyperliquid throttles the public REST API by source IP. Every process on the same machine, container or NAT draws from one budget. Our production collector was healthy for weeks until a research script on the same box started backfilling fundingHistory at about three requests per second. Within a minute the collector's l2Book polls were returning HTTP 429, and the collector logged a gap in a data set that is supposed to be continuous.
The practical rule: treat the IP as the unit of capacity. Anything that runs next to a production consumer of the API needs a written request budget, or it needs its own egress address.
Not all /info calls cost the same
Some /info request types are weighted by the size of their response. Two that bit us:
fundingHistoryis heavy. Paginating history for dozens of names in a tight loop is the fastest way we found to trip the limit.frontendOpenOrdersanduserFillsfor a market-making wallet return thousands of rows (500 KB and up). A handful of those calls pushed an entire IP into 429, and unrelated endpoints such asclearinghouseStatewere throttled along with them.
Check the official documentation for the current weights; they change. The shape of the problem does not: a few large responses can cost more than thousands of small ones.
What a 429 storm looks like
- The first sign is usually not in the process that caused it. It shows up in whatever else on the box is polling steadily, as a burst of 429s followed by silence in the data.
- Retrying immediately makes it worse. The budget refills over time, so the only fix is to stop sending.
- Gaps are permanent unless your storage layer records them. We now log every failed poll with its reason, so a missing snapshot is distinguishable from a quiet market.
Pacing rules we now run with
These are the numbers we settled on for one IP that also hosts a production collector. They are conservative on purpose.
| Call | Minimum spacing | Notes |
|---|---|---|
fundingHistory |
5 s between calls | never in a loop without a sleep |
other /info reads |
1 s between calls | batch by market where the endpoint allows |
| on any 429 | back off 60 s | then resume at half the previous rate |
| daily budget | written down per job | e.g. 220 calls/day for one research runner, 62 for another |
After each bulk job we grep the collector logs for 429 before trusting that day's data.
Checklist before you ship a poller
- Count every process that will share the egress IP, including your own dev machine if it tunnels through the server.
- Give heavy history calls their own schedule, off-peak relative to your live polling.
- Log failures with timestamps so gaps are explainable later.
- Back off on 429; never retry in a tight loop.
- If you need more than one budget, split by IP, not by process.
Related: The WebSocket cap of 15 users per IP is a separate limit with different failure behaviour.