APIHIP-4 ArchiveOverview

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 GET with no query parameters: each resource is one whole document. HEAD and the CORS preflight (OPTIONS) work too; any other method answers 405.
  • CORS is open (*).
  • Rate limit: 300 requests per minute per IP. Over it: 429 with Retry-After: 60 and X-RateLimit-Limit. If you need more, reach out.
  • JSON only, served from pre-computed snapshots (see Freshness).
  • Ordering: archive.json by expiryMs descending, events.json by settledAt descending, 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

ResourceWhat it holds
/v1/live.jsonEvery market currently listed, named: readable title, sides, competition, fixture, category, deployer, coin tickers, expiry. Rebuilt every minute
/v1/archive.jsonEvery settled price market: target, settle price, result, volume, distinct traders
/v1/events.jsonEvery resolved event market: sides, winner, final score, question rules, distinct traders
/v1/governance.jsonValidator settlement activity, aggregated
/v1/markets/{id}.jsonOne market: result, settlement verdict, flow (trades, traders, fees in USD, buy/sell split, settlement tail), dataQuality, the odds capture summary
/v1/markets/{id}/settlement.jsonThe validators that voted the settlement, with each one’s stake weight at vote time
/v1/markets/{id}/price.jsonWhat the underlying did while the market was live, at 5-minute resolution
/v1/archive/{YYYY-MM}.jsonOne month’s slice, May 2026 onward
/v1/changes.jsonRecent resolutions, newest first: poll this instead of re-reading everything
/v1/openapi.jsonOpenAPI 3.1 description, field by field
/v1/manifest.jsonCounts, 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.json

Example 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 404 is a market the archive does not hold, not an outage: usually a wrong id or a market still trading. See status codes.
  • totalVolume is not what was traded. It also carries settlement payouts, merges and mints. Traded volume is buyNotional + sellNotional, served on every archive.json and events.json row; never subtract splitNotional from 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); // 200

Polling 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   # 304

ETag, 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.