Archive API
Two halves, one hostname.
Live. /v1/live.json
serves every market Hyperliquid currently lists, with its template pointer
(template:sportsContestWinner, participantA:…|participantB:…) resolved into
the name a reader sees, plus its sides, competition, fixture, category and
deployer. Rebuilt every minute.
Resolved. Hyperliquid purges a HIP-4 market shortly after it settles: its coin, candles, order book and settlement votes disappear from the node API. Liquary preserves that record and serves all of it, back to May 2026, for free. The same record, rendered, is on the Data pages.
https://api.liquary.xyz/v1- No authentication. Every endpoint is a plain
GETwith no query parameters: each resource is one whole document.HEADand the CORS preflight (OPTIONS) work too; any other method answers405. - CORS is open (
*). - Rate limit: 300 requests per minute per IP. Over it:
429withRetry-After: 60andX-RateLimit-Limit. If you need more, reach out. - JSON only, served from pre-computed snapshots (see Freshness).
- Ordering:
archive.jsonbyexpiryMsdescending,events.jsonbysettledAtdescending, so the newest resolution comes first in both. The order is stable: to ingest incrementally, read until you meet an id you already hold. - Not served: the odds series, a bulk export, and the per-wallet data behind the trader leaderboard.
What’s inside
| Resource | What it holds |
|---|---|
/v1/live.json | Every market currently listed, named: readable title, sides, competition, fixture, category, deployer, coin tickers, expiry. Rebuilt every minute |
/v1/archive.json | Every settled price market: target, settle price, result, volume, distinct traders |
/v1/events.json | Every resolved event market: sides, winner, final score, question rules, distinct traders |
/v1/governance.json | Validator settlement activity, aggregated |
/v1/markets/{id}.json | One market: result, settlement verdict, flow (trades, traders, fees in USD, buy/sell split, settlement tail), dataQuality, the odds capture summary |
/v1/markets/{id}/settlement.json | The validators that voted the settlement, with each one’s stake weight at vote time |
/v1/markets/{id}/price.json | What the underlying did while the market was live, at 5-minute resolution |
/v1/archive/{YYYY-MM}.json | One month’s slice, May 2026 onward |
/v1/changes.json | Recent resolutions, newest first: poll this instead of re-reading everything |
/v1/openapi.json | OpenAPI 3.1 description, field by field |
/v1/manifest.json | Counts, coverage, shard index, freshness contract |
Quickstart
# Every market listed right now, with its readable name
# ("NFL: Tampa Bay Buccaneers vs Minnesota Vikings", not template:sportsContestWinner)
curl https://api.liquary.xyz/v1/live.json
# What's in the archive right now
curl https://api.liquary.xyz/v1/manifest.json
# Every settled price market (BTC/ETH/SOL/HYPE binaries & buckets)
curl https://api.liquary.xyz/v1/archive.json
# One market: result, final score, traded volume, settlement verdict
curl https://api.liquary.xyz/v1/markets/3430.jsonExample ids are illustrative: take a real one from changes.json.
The whole path, once
Watch what settles, open a market, and pull only the pieces you want:
import requests
HOST = "https://api.liquary.xyz"
API = HOST + "/v1"
get = lambda p: requests.get(API + p, timeout=30).json()
# 1. What has resolved lately. Poll this, not the aggregates: it is far
# smaller, and it tells you exactly which ids moved.
for change in get("/changes.json")["changes"][:5]:
oid = change["outcomeId"]
# 2. The record itself: the question, the winner, the volume, the
# deployer, and pointers to everything heavy.
m = get(f"/markets/{oid}.json")
row = m["market"]
print(f"#{oid} {row.get('questionTitle') or row['underlying']} "
f"-> {row.get('winnerLabel') or row['result']} ${row['totalVolume']:,.0f}")
# 3. Traded volume is not total volume. `totalVolume` also carries
# settlement payouts, merges and mints; buy + sell is what changed hands.
f = m.get("flow") or {}
if f.get("buyNotional") is not None:
print(f" traded ${f['buyNotional'] + f['sellNotional']:,.0f} "
f"in {f.get('tradeFills')} trades by {f['traders']} wallets")
# 4. How the odds were captured: `oddsMeta` says how many points were
# recorded, over which range, and with what gaps.
om = m["oddsMeta"]
print(f" odds: {om['points']} points captured, {om['gaps']} gaps")
# 5. Who voted it, when there was a vote. Two kinds of market never carry
# one, and neither is a gap: price markets settle from the oracle, and
# third-party markets are settled by their own deployer. Both read
# `settlementApplicable: false`.
if m.get("settlementUrl"):
# settlementUrl already starts with /v1: prefix the host, not API.
voters = requests.get(HOST + m["settlementUrl"], timeout=30).json()["voters"]
print(f" settled by {len(voters)} validators: {m['settlement']['reason']}")- Poll
changes.json, not the aggregates. It names the ids that moved, at a fraction of the size. - The market document is an index, not the payload. The underlying’s price and the settlement roster sit behind the URLs it hands you.
- A
404is a market the archive does not hold, not an outage: usually a wrong id or a market still trading. See status codes. totalVolumeis not what was traded. It also carries settlement payouts, merges and mints. Traded volume isbuyNotional + sellNotional, served on everyarchive.jsonandevents.jsonrow; never subtractsplitNotionalfrom the total (see coverage).
In your language
Pulling one market and its settlement voters:
const BASE = "https://api.liquary.xyz/v1";
const market = await fetch(`${BASE}/markets/813.json`).then((r) => r.json());
console.log(market.market.outcomeLabel, "won:", market.market.winnerLabel);
// How the odds were captured: a summary.
console.log(market.oddsMeta.points, "odds points recorded,", market.oddsMeta.gaps, "gaps");
// Price markets settle from the oracle: settlementApplicable is false there.
if (market.settlementApplicable && market.settlementCaptured) {
console.log(market.settlement.voterCount, "validators voted");
}
// Fills-derived: distinct traders, real fees in USD, buy/sell split.
// flow.basis says which record it comes from ("ledger" once the settlement
// sweep has recomputed the market); null only when no fill is known.
if (market.flow) {
console.log(market.flow.traders, "traders,", market.flow.fills, "fill rows, fees $" + market.flow.fee.toFixed(2), "(" + market.flow.basis + ")");
}
// The whole archive is served: a market settled in May 2026 answers like any other.
const old = await fetch(`${BASE}/markets/20.json`);
console.log(old.status); // 200Polling without waste
Every successful response carries an ETag. Send it back as If-None-Match
and an unchanged document answers 304 with no body:
ETAG=$(curl -sI https://api.liquary.xyz/v1/changes.json | grep -i '^etag:' | cut -d' ' -f2)
curl -s -o /dev/null -w '%{http_code}\n' -H "If-None-Match: $ETAG" \
https://api.liquary.xyz/v1/changes.json # 304ETag, Retry-After and X-RateLimit-Limit are CORS-exposed, so a browser
can read them cross-origin.
To ingest the archive, read archive.json and events.json once (plus one
settlement.json per market whose voters you want), then poll changes.json
with an ETag and fetch only the markets it names.
Freshness
The API serves static snapshots, not live queries. They are regenerated
when a market settles (within about a minute) and once a night: the
aggregates in full, the per-market documents a slice of the archive per night.
Every document carries generatedAt (epoch ms): read it rather than assuming
real time.
Edge caching adds a little on top: aggregates can lag a few minutes after a
settlement, and an already-cached per-market file up to an hour. A newly
settled market’s file is fresh from its first read, unless it was requested
before it existed: that 404 stays cached for up to five minutes.
For live prices and order books, use Hyperliquid’s own /info API.
Stability
The v1 schema is stable. Changes are additive only: new fields may
appear, existing fields will not change meaning or disappear. A breaking change
would ship as a new version prefix, with v1 kept serving.
That promise covers shape, not coverage. Every change to what is served is listed in the changelog.
Availability is best-effort, with no uptime guarantee and no SLA. Cache what you depend on.
Using the data
The data is licensed CC BY 4.0:
use it freely, including commercially, with attribution. Credit “Liquary”
with a link to liquary.xyz (or this documentation)
wherever the data appears. The licence is also declared in
manifest.json and in
the OpenAPI document.