Check
meta.as_of, meta.stale, and the quality fields before using a result. Current positions are periodic snapshots, not a real-time account feed. For historical reads, also check coverage and finality. See Finality and freshness and Quality.Venues and account keys
Positions are published for Hyperliquid core perps, HIP-3 builder perps, Lighter mainnet, and Lighter on Robinhood Chain. Spot markets and HIP-4 outcome markets have no positions routes.
Hyperliquid addresses are 42-character hex strings starting with
0x and are case-insensitive. Lighter paths take integer account indexes, not wallet addresses. Use the L1 address lookup to find an address’s Lighter mainnet accounts. Mainnet and Robinhood Chain account indexes are separate identifiers, even when their numbers match.
Historical coverage
A request before coverage returns
200 with an empty result, meta.coverage_from, and a meta.notice identifying the coverage start. This applies to as-of reads and change logs on every venue, and to hourly position history on both Lighter deployments. Hyperliquid and HIP-3 hourly position history before the first snapshot returns an empty page without a notice. An empty result outside coverage does not mean the account had no positions.
Routes
Every route isGET. Prefix each path with the venue namespace from the table above.
Related routes:
GET /v1/lighter/accounts?l1_address=0x...lists the Lighter mainnet account indexes an L1 address owns. Robinhood Chain has no L1 lookup.GET /v1/data-quality/positionsreports, per venue, the latest live snapshot and its age, the latest hourly snapshot,built_through, andfinalized_through.
First request
SetOXARCHIVE_API_KEY in your environment and set WALLET_ADDRESS to the public Hyperliquid address you want to query. Keep API keys in server-side code or a local shell, not browser code or source control.
data.positions and data.account. The account summary can be null; see Account summary. An empty positions result also includes data.account_seen.
This illustrative response uses synthetic values. It is not a captured account response or a prediction of what your request will return.
Example snapshot response
Example snapshot response
LIGHTER_ACCOUNT_INDEX to the account index returned by the L1 lookup:
Current, as-of, and reconstructed state
Withouttimestamp, the account positions routes return the latest snapshot. meta.stale is true, with a meta.notice, when that snapshot is more than 12 minutes old.
With timestamp (epoch milliseconds), the response is the state after every event before that instant:
- An exact UTC hour with an available hourly snapshot returns that snapshot.
meta.sourceissnapshot. - Other instants return reconstructed state, with
meta.source: "reconstructed". Mark price, value, and unrealized PnL describe the served instant when a mark is available. Check row quality and nullable fields rather than assuming a complete snapshot. - A request later than the available change history is limited to
meta.built_through. The response addsmeta.requested_endandmeta.clamped_toso you can detect the difference. - An instant before coverage returns no positions,
data.account_seen: "outside_coverage", ameta.notice, andmeta.coverage_from.
null:
meta.as_of identifies the time the returned data describes, not the request time. Market routes read snapshots only: pass hour as an exact UTC hour in epoch milliseconds, and check the resolved snapshot in meta.snapshot_ts.
Empty results
When an account holds no positions on the first page,data.account_seen says why:
Finality and freshness
Historical responses can report two time boundaries. They serve different purposes:
On Lighter and Robinhood Chain,
finalized_through follows the finalized trade record and usually trails the present by about a day. Change legs after that boundary carry finalized: false; Lighter position rows based on those trades also remain preliminary.
Current and exact-hour account positions, current account summaries, market positions, current market summaries, and bulk pages do not carry these boundaries. Use GET /v1/data-quality/positions to check finality and snapshot age before a job. A fresh snapshot is not necessarily finalized.
Quality
Position rows and summaries carryquality; snapshot responses also report meta.quality. Change-log rows use continuity and finalized instead.
meta.quality is complete, partial, or degraded. Read it together with row-level quality. A row marked complete does not establish that the entire requested history is available or finalized.
Known limits
- History begins at the dates in Historical coverage. Earlier state is unavailable, and
never_seenapplies only within that coverage. - Recent Lighter and Robinhood Chain snapshots can report
meta.quality: "degraded"while their rows arepreliminaryandfinalized: false. Recheck quality andmeta.finalized_throughafter finalization before using them as final historical records. - Hyperliquid’s published fill data has gaps in three short windows in June and July 2025. A small number of positions around those windows are marked
partial. - Lighter rows have no leverage multiple, cumulative funding, or liquidation price: those members are
nullandliquidation_price_statusisunavailable. Account summaries on Lighter are position aggregates only. - Spot markets, including Lighter spot pairs, and HIP-4 outcome markets have no positions routes. Robinhood Chain has no L1 address lookup.
Field reference
Financial values such as sizes, prices, and PnL are decimal strings, not JSON numbers. Counts and fields such asmax_leverage are integers. Preserve decimal precision and treat null as unknown, not zero.
Robinhood Chain monetary values are in USDG, including Lighter fields whose names contain usdc.
Times in data are RFC 3339 UTC strings, with fractional seconds when non-zero. Times in meta always include milliseconds. Request parameters (timestamp, start, end, hour) use epoch milliseconds.
Position
Lighter rows add
account_index (a string, because indexes can exceed JavaScript integers), account_kind (user, settlement, insurance, or system), initial_margin_fraction, allocated_margin, margin_mode, mark_source, and finalized.
Change
Each change row describes one trade, liquidation, deleverage, or settlement leg for the account, with the position before and after it. Some Lighter legs leave the position unchanged. Change-log responses usemeta.source: "changes". A leg with continuity: "quarantined" is excluded from position state; do not apply it as a normal fill when processing the log.
Lighter change rows add
position_size_before and position_size_after, fee_rate, fee_usdc, and usdc_amount. On Robinhood Chain, fee_token is USDG and the USDC-named amount fields are in USDG.
Account summary
On an account positions response,data.account contains a summary only on the first page of an eligible snapshot read:
Otherwise, including on later pages and reconstructed reads,
data.account is null.
All account summaries carry total_position_value, total_unrealized_pnl, long_value, short_value, n_positions, and quality. A total with any unpriced position is null, never a partial sum.
Hyperliquid and HIP-3 also carry account_value, cross_account_value, collateral, total_margin_used, cross_maintenance_margin_used, withdrawable, account_mode, and snapshot_as_of. withdrawable is null outside periods where it was captured. HIP-3 summaries are scoped to one dex.
Lighter summaries identify the account with account_index and contain position aggregates only, not account balances.
Market rows and summaries
Market and bulk rows carry the account key (user_address on Hyperliquid, account_index and account_kind on Lighter), symbol, size, side, entry_price, mark_price, position_value, unrealized_pnl, leverage_type, liquidation_price, and quality. The first page of /positions/{symbol} adds meta.totals for the whole filtered set.
Market summaries carry long_count, short_count, long_size, short_size, long_value, short_value, long_avg_entry_price, short_avg_entry_price, long_positions_with_entry, short_positions_with_entry, long_top10_value_share, short_top10_value_share, top10_value_share, and quality.
Parameters
Market summary routes accept a
limit parameter up to 2,000, but a history page contains at most 168 hourly points. Follow the returned cursor for the remaining hours.
Pagination
Passmeta.next_cursor back as cursor, keeping the route and all other parameters unchanged. Stop when no next cursor is returned. Treat cursors as opaque values; do not decode or modify them. They expire after 24 hours. Reusing a cursor with different parameters returns invalid_cursor.
Market and bulk pages use the same snapshot throughout pagination. If that snapshot is no longer available, the next page returns 409 with snapshot_advanced. Restart from the first page without a cursor; do not combine pages from the old and new snapshots.
History pages can end on an hour boundary with fewer than limit rows and still have a next cursor. A short page does not mean you have reached the end.
Bulk export in Arrow
Request Arrow on a bulk route withformat=arrow or Accept: application/vnd.apache.arrow.stream. An Arrow response is an Apache Arrow IPC stream with up to 50,000 rows per page. It has no JSON envelope: read x-count, x-next-cursor, and x-schema-version from the response headers. The schema version is positions.v1.
Always check the response Content-Type before parsing. The route returns JSON if Arrow output is not enabled, even when you requested Arrow.
Market-wide positioning
include_system=true to include settlement, insurance, and other system accounts. /positions/{symbol}/summary without start or end returns one summary for the latest snapshot; with a window it returns one summary per available hour.
Lighter L1 address lookup
SetWALLET_ADDRESS to the L1 address you want to look up:
data holds l1_address, total_accounts, and accounts, each with account_index, account_type, and first_seen. The lookup covers Lighter mainnet only.
SDKs
Positions resources are available in SDK version1.12.0 and later for Python, TypeScript, and Rust. The examples below use OXARCHIVE_API_KEY and WALLET_ADDRESS from your environment. See SDK installation and REST usage for setup and language-specific method names.
Billing and caching
Position routes cost one credit per 1,000 rows returned, with a minimum of one credit per request. Account summary routes and the Lighter L1 lookup cost one credit per request. Your plan’s history window applies totimestamp, hour, and [start, end).
When caching results, retain their timestamps, quality, and finality metadata. A cached current response still describes its original meta.as_of, and preliminary history should be refreshed after finalization.
Errors
Errors use the standard error body withcode, error, param when a parameter is at fault, and an error_code for the cases below.
Common mistakes
- Treating
data.account_seen: "never_seen"as evidence that an account never traded. It is scoped to covered history. - Reading the
nullmembers of a reconstructed row as zero, or typingleverageas nullable andleverage.typeas onlycrossorisolated. A reconstruction keeps theleverageandcum_fundingobjects, withleverage.typeset tounknownon Hyperliquid and HIP-3, and itsnullvalues mean unknown, not zero. - Joining Lighter account indexes across mainnet and Robinhood Chain. They are separate account spaces.
- Paging with changed parameters. A cursor belongs to the exact request that issued it.