Perpetuals
Perpetual futures on stocks, ETFs, commodities, indices, currencies, pre-IPO companies and crypto, priced around the clock.
How the feed is built
A perpetual is a futures contract with no expiry, traded 24 hours a day, 7 days a week. The methodology in full is on the Perpetuals product page.
- One composite price
- Built from every independent source that lists the contract: the bid is the median of the live source bids and the ask the median of the live source asks, so the book is never crossed and no single source moves the price.
source_count- On every response and frame: how many sources stand behind the value.
- Underlying class
- A perpetual is an instrument type, not an asset class, so each symbol reports its underlying:
equity,etf,commodity,index,forex,pre_ipoorcrypto. - Units
- US dollar stablecoin terms, as the contracts trade.
Symbol list
Every perpetual contract with its name, underlying class and source count. Open to any valid key, with or without Perpetuals on the account.
Query parameters
underlying- Optional filter: equity, etf, commodity, index, forex, pre_ipo or crypto. An unknown value is ignored.
Request
curl -sS "https://api.tickerlayer.com/perpetuals/symbols?underlying=commodity" \
-H "x-api-key: <YOUR_API_KEY>"Response 200 OK
{
"symbols": [
{
"symbol": "XAUUSDT",
"name": "Gold",
"instrument_type": "perpetual",
"underlying_asset_class": "commodity",
"base": "XAU",
"source_count": 6
}
],
"count": 8
}Notes
source_count here is how many sources list the contract. On a price it is how many of them are live behind that value right now.Latest quote
Composite best bid and ask with the displayed size summed across the contributing sources.
Path parameters
symbol- Perpetual contract symbol (for example XAUUSDT, TSMUSDT, BTCUSDT). Trimmed and uppercased before lookup. Retrieve the full list from GET /perpetuals/symbols.
Request
curl -sS "https://api.tickerlayer.com/perpetuals/quote/XAUUSDT" \
-H "x-api-key: <YOUR_API_KEY>"Send runs against the live API with your account key (never exposed in the browser).
Response 200 OK
{
"symbol": "XAUUSDT",
"bid": 4316.31,
"ask": 4316.36,
"bid_size": 118.049,
"ask_size": 51.115,
"source_count": 6,
"timestamp": 1789640504033
}Notes
timestamp is the event time of the freshest contributing source, in epoch milliseconds, and never steps backwards. A composite can carry one more decimal than the contract tick: the median of two sources one tick apart is a half tick. 403 perpetuals access not enabled for your account: the account does not include Perpetuals. 404 unknown symbol: the symbol is not in the perpetuals list. 404 with a no ... available message: the contract is known but has no live value right now.Last trade
The most recent execution across sources, with the aggressor side when the source reports it.
Path parameters
symbol- Perpetual contract symbol (for example XAUUSDT, TSMUSDT, BTCUSDT). Trimmed and uppercased before lookup. Retrieve the full list from GET /perpetuals/symbols.
Request
curl -sS "https://api.tickerlayer.com/perpetuals/trade/last/TSMUSDT" \
-H "x-api-key: <YOUR_API_KEY>"Send runs against the live API with your account key (never exposed in the browser).
Response 200 OK
{
"symbol": "TSMUSDT",
"price": 423.36,
"size": 1.5,
"side": "buy",
"timestamp": 1789640506413
}Notes
404 no trade available; the next print fills it. side is buy, sell or null. 403 perpetuals access not enabled for your account: the account does not include Perpetuals. 404 unknown symbol: the symbol is not in the perpetuals list. 404 with a no ... available message: the contract is known but has no live value right now.Snapshot
Quote, mark price and last trade in one call, with the contract name and underlying class. Built for first paint.
Path parameters
symbol- Perpetual contract symbol (for example XAUUSDT, TSMUSDT, BTCUSDT). Trimmed and uppercased before lookup. Retrieve the full list from GET /perpetuals/symbols.
Request
curl -sS "https://api.tickerlayer.com/perpetuals/snapshot/TSMUSDT" \
-H "x-api-key: <YOUR_API_KEY>"Send runs against the live API with your account key (never exposed in the browser).
Response 200 OK
{
"symbol": "TSMUSDT",
"name": "Taiwan Semiconductor Manufacturing Company",
"instrument_type": "perpetual",
"underlying_asset_class": "equity",
"bid": 423.31,
"ask": 423.39,
"bid_size": 61.2,
"ask_size": 48.7,
"mark_price": 423.35,
"last_price": 423.36,
"last_size": 1.5,
"source_count": 6,
"timestamp": 1789640504033,
"mark_timestamp": 1789640505932,
"last_timestamp": 1789640506413,
"prev_close": 421.12,
"change": 2.23,
"change_percent": 0.5295
}Notes
last_* only as fresh as other demand makes it; they are null when no print is held. prev_close is the close of the last completed UTC daily bar, and change and change_percent measure the composite mid price against it (the mark price when there is no book). A perpetual has no session, so the day turns at 00:00 UTC. The three fields are null for a moment after the UTC day turns, until the new close is read. 403 perpetuals access not enabled for your account: the account does not include Perpetuals. 404 unknown symbol: the symbol is not in the perpetuals list. 404 with a no ... available message: the contract is known but has no live value right now.Previous bar
The most recently completed UTC daily candle for the contract.
Path parameters
symbol- Perpetual contract symbol (for example XAUUSDT, TSMUSDT, BTCUSDT). Trimmed and uppercased before lookup. Retrieve the full list from GET /perpetuals/symbols.
Query parameters
interval- Returns the most recently settled bar at that interval instead of the previous daily bar, and adds
interval,bar_start,bar_endandas_ofto the response. Leave it out for the daily bar.Values1m5m15m1h4h1d
Request
curl -sS "https://api.tickerlayer.com/perpetuals/agg/XAUUSDT/prev" \
-H "x-api-key: <YOUR_API_KEY>"Response 200 OK
{
"symbol": "XAUUSDT",
"result": {
"o": 4305.9,
"h": 4312.7,
"l": 4287.5,
"c": 4298.4,
"v": 214380.51,
"t": 1789257600000
}
}Notes
404 no bar available when the daily bar cannot be resolved yet. A bar requested with ?interval= is held for a short settle lag after it closes, so small clock differences between callers do not change which bar comes back, and it is the same bar the range endpoint returns for that window. Cache-Control and ETag mark when the value can next change.Aggregate bars (OHLCV)
Historical OHLCV bars between two UTC calendar dates (inclusive).
Path parameters
symbol- Perpetual contract symbol (for example XAUUSDT, TSMUSDT, BTCUSDT). Trimmed and uppercased before lookup. Retrieve the full list from GET /perpetuals/symbols.
multiplier- Bar size in units of
timespan:1,5or15withminute;1or4withhour;1withday. Other pairs return400, and the message lists the valid ones. timespan- Case-insensitive. Other values return
400.Valuesminutehourday from- First UTC date, inclusive, as
YYYY-MM-DD. to- Last UTC date, inclusive, as
YYYY-MM-DD. Not beforefrom.
Query parameters
limit- Page size, up to
5000.results_countis the size of this page andtotal_countthe bars in the whole range; follownext_offsetuntil it isnull.Default500 offset- Zero-based row offset, applied after sorting.Default
0 sort- Order of bars by timestamp
t, applied before pagination.ValuesascdescDefaultdesc
Request
curl -sS "https://api.tickerlayer.com/perpetuals/agg/XAUUSDT/1/day/2026-09-01/2026-09-14?sort=desc&limit=3" \
-H "x-api-key: <YOUR_API_KEY>"Response 200 OK
{
"symbol": "XAUUSDT",
"results_count": 3,
"total_count": 14,
"results": [
{ "o": 4298.4, "h": 4331.2, "l": 4290.1, "c": 4316.3, "v": 278600.08, "t": 1789344000000 },
{ "o": 4305.9, "h": 4312.7, "l": 4287.5, "c": 4298.4, "v": 214380.51, "t": 1789257600000 },
{ "o": 4279.2, "h": 4310.0, "l": 4271.8, "c": 4305.9, "v": 236911.37, "t": 1789171200000 }
],
"next_offset": 3
}Notes
- Bars are built from traded prices on the contract's primary market source, one source per contract so a chart is never stitched from two series.
vis that source's volume in base units, not the whole market. The live composite quote can sit a few ticks away from the latest bar close. - Maximum span per request: 7 days for 1 minute bars, 31 for 5 minute, 90 for 15 minute, 365 for 1 hour, 730 for 4 hour. A few contracts carry shorter windows; the
400message states the limit for that symbol. - History starts when the contract was first listed, which for many perpetuals is recent. Bars are UTC, the day opens at 00:00 UTC, and the newest bar is still forming.
Mark price
Composite mark price: the median of the live source mark prices. The mark is the reference a perpetual market uses to value positions and trigger liquidations.
Path parameters
symbol- Perpetual contract symbol (for example XAUUSDT, TSMUSDT, BTCUSDT). Trimmed and uppercased before lookup. Retrieve the full list from GET /perpetuals/symbols.
Request
curl -sS "https://api.tickerlayer.com/perpetuals/mark/XAUUSDT" \
-H "x-api-key: <YOUR_API_KEY>"Send runs against the live API with your account key (never exposed in the browser).
Response 200 OK
{
"symbol": "XAUUSDT",
"mark_price": 4316.244,
"source_count": 6,
"timestamp": 1789640505932
}Notes
403 perpetuals access not enabled for your account: the account does not include Perpetuals. 404 unknown symbol: the symbol is not in the perpetuals list. 404 with a no ... available message: the contract is known but has no live value right now.WebSocket
Three channels on the same stream, key and subscribe message as every other feed, on the Business plan. Frames follow the stream dialect: prices and sizes are strings and the event time is ts.
| Channel | Carries | Snapshot on subscribe |
|---|---|---|
perpetuals.quotes | The composite top of book. | The last known quote. |
perpetuals.marks | The composite mark price. | The last known mark. |
perpetuals.trades | Executions across sources, in arrival order. | Never. |
- Conflation
- Quotes and marks are conflated to the latest value ten times a second: a price change goes out on the next flush, a size-only change waits up to 250 ms. With source-level access, the book of each source can come tick by tick instead.
- Snapshot
- Replayed frames carry
snapshot: true, because a quiet contract can sit unchanged for a while and silence would be ambiguous. Sendsnapshot: falseto skip them.
{
"action": "subscribe",
"channels": ["perpetuals.quotes", "perpetuals.marks", "perpetuals.trades"],
"symbols": ["XAUUSDT", "TSMUSDT"]
}{
"type": "quote",
"channel": "perpetuals.quotes",
"asset": "perpetuals",
"symbol": "XAUUSDT",
"bid": "4316.31",
"ask": "4316.36",
"bid_size": "118.049",
"ask_size": "51.115",
"source_count": 6,
"ts": 1789640504033
}{
"type": "mark",
"channel": "perpetuals.marks",
"asset": "perpetuals",
"symbol": "XAUUSDT",
"mark_price": "4316.244",
"source_count": 6,
"ts": 1789640505932
}{
"type": "trade",
"channel": "perpetuals.trades",
"asset": "perpetuals",
"symbol": "TSMUSDT",
"price": "423.36",
"size": "1.5",
"side": "buy",
"ts": 1789640506413
}Source-level streams
The standard feed is the composite. Enterprise accounts that run their own aggregation, such as oracle networks and quantitative desks, can also get every quote, mark and trade per source, next to the composite, on the same routes and channels. Without it, a ?sources=true request answers 403 source-level access not enabled for your account and the stream sends composite frames only. Talk to us.
| Field | Meaning |
|---|---|
source_id | The identifier of the source. |
venue_symbol | The symbol on that source. |
native_currency | The currency it quotes in. Native values are never converted; USD values come next to them on request. |
market_type | perpetual or spot. |
GET /perpetuals/quote/XAUUSDT?sources=true
{
"symbol": "XAUUSDT",
"bid": 4316.31,
"ask": 4316.36,
"bid_size": 118.049,
"ask_size": 51.115,
"source_count": 6,
"timestamp": 1789640504033,
"sources": [
{
"source_id": "pv_01",
"venue_symbol": "XAUUSDT",
"native_currency": "USDT",
"market_type": "perpetual",
"bid": 4316.3,
"ask": 4316.38,
"bid_size": 15.15,
"ask_size": 15.3,
"timestamp": 1789640504021
}
]
}{
"type": "venue_quote",
"channel": "perpetuals.quotes",
"asset": "perpetuals",
"symbol": "XAUUSDT",
"source_id": "pv_01",
"venue_symbol": "XAUUSDT",
"native_currency": "USDT",
"market_type": "perpetual",
"bid": "4316.3",
"ask": "4316.38",
"bid_size": "15.15",
"ask_size": "15.3",
"ts": 1789640504021
}- REST
- One row per live source.
/perpetuals/markreturns the same rows withmark_pricein place of the book, and/perpetuals/trade/lastadds the four fields to the print itself. - Stream
perpetuals.markscarriesvenue_markframes and everytradeframe gains the four fields. Composite frames keep theirquoteandmarktypes, so the composite is never mistaken for one source.
USD values
Source rows are priced in the stablecoin each source quotes in, USDT or USDC. Ask for USD values and every source row also carries its prices in USD, next to the native ones and never in place of them. Without the flag, rows and frames are exactly as above.
| Where | Flag | Applies to |
|---|---|---|
| REST | usd=true | The source rows of /perpetuals/quote, /perpetuals/mark and /perpetuals/trade/last, together with sources=true. |
| Stream | "usd": true | The venue_quote, venue_mark and source-level trade frames of the symbols in that subscribe. |
| Field | Meaning |
|---|---|
usd_bid | bid in USD (quote rows). |
usd_ask | ask in USD (quote rows). |
usd_mark_price | mark_price in USD (mark rows). |
usd_price | price in USD (trades with source fields). |
usd_rate | The rate applied, from native_currency to USD: the mid of a USD-quoted spot market for that stablecoin. Every USD field is the native value times this rate. |
{
"action": "subscribe",
"channels": ["perpetuals.quotes", "perpetuals.marks"],
"symbols": ["XAUUSDT"],
"usd": true
}{
"type": "venue_quote",
"channel": "perpetuals.quotes",
"asset": "perpetuals",
"symbol": "XAUUSDT",
"source_id": "pv_01",
"venue_symbol": "XAUUSDT",
"native_currency": "USDT",
"market_type": "perpetual",
"bid": "4316.3",
"ask": "4316.38",
"bid_size": "15.15",
"ask_size": "15.3",
"usd_bid": "4314.595062",
"usd_ask": "4314.67503",
"usd_rate": "0.999605",
"ts": 1789640504021
}- No fresh rate
- The USD fields are still there, as
null. A rate is never assumed to be 1. Rows not priced in a stablecoin, such as theUSDTARSreference book, always carrynull. - Per symbol
- The most recent subscribe for a symbol decides: subscribe again without
usdto go back to native values only. Composite frames never carry USD fields. - Errors
usdon a channel other than the three perpetuals channels fails withUNSUPPORTED_USD, and a value that is nottrueorfalsewithINVALID_USD.
Every book change
Source rows are conflated like the composite by default. Subscribe with "quote_mode": "tick_by_tick" and every change to the top of book of a source, price or size, goes out as its own venue_quote frame, in the order the source published it, with its event time in ts. Same frame and fields, plus seq, a count of that book's changes.
| Value | Source rows on perpetuals.quotes |
|---|---|
"conflated" | Default. The latest book of each source per flush, like the composite. |
"tick_by_tick" | Every change to the top of book of each source, one frame per change. |
{
"action": "subscribe",
"channels": ["perpetuals.quotes"],
"symbols": ["BTCUSDT", "XAUUSDT"],
"quote_mode": "tick_by_tick"
}{
"type": "venue_quote",
"channel": "perpetuals.quotes",
"asset": "perpetuals",
"symbol": "BTCUSDT",
"source_id": "pv_02",
"venue_symbol": "BTCUSDT",
"native_currency": "USDT",
"market_type": "perpetual",
"bid": "86512.4",
"ask": "86512.5",
"bid_size": "3.112",
"ask_size": "0.415",
"seq": 1791460812437213,
"ts": 1791460812437
}- Applies to
- The
venue_quoteframes of theperpetuals.quotessymbols in that subscribe. Compositequoteframes,venue_markframes and trades are unchanged. It combines withusdandsnapshot. - Per symbol
- The most recent subscribe for a symbol decides: subscribe again with
"conflated"or withoutquote_modeto go back. - Starting point
- When a symbol starts tick by tick, the current book of each source comes first, flagged
snapshot: true, then every change after it. A symbol you already stream gets that restatement whenever it switches mode, either way, even withsnapshot: false, and so does every tick-by-tick symbol after a restart on our side or when source-level access comes back. A new symbol withsnapshot: falsegets no rows and starts with the next change. - seq
- An integer that starts at a large arbitrary value (it needs 64 bits and fits a JSON number exactly), goes up by one with every change of that source's book and never goes down. A bigger step means changes were missed: frames a connection could not take, a source that dropped, a restart on our side. The same value again means the same book. Compare it only within one source and symbol; to resync, read the source rows with
?sources=trueor subscribe again. - Withdrawals
- Still announced with
venue_status. The nextvenue_quotefor that source and symbol means the book is back, even when its values are the same as before. - Volume
- A busy book changes many times a second, mostly in size, so read the socket without blocking. Frames a connection cannot take in time are skipped, not queued, and
seqshows the gap. - Connections
- A symbol streams tick by tick on a limited number of connections at a time, per account and across the service. Past that, or without source-level access, the subscribe still succeeds: those symbols come conflated, and a
quote_modemessage after the acknowledgement names them, withQUOTE_MODE_LIMIT,scopeandlimit, orQUOTE_MODE_UNAVAILABLE. A capped symbol switches to tick by tick by itself once a connection frees up, announced by aquote_modemessage with"tick_by_tick". - Messages
- Every tick-by-tick subscribe is answered with a
quote_modemessage naming the symbols that got"tick_by_tick", and another for any that stay conflated. One can also arrive on its own: when a capped symbol switches, when source-level access ends, after a reconnect on our side. The latest one for a symbol is its mode, and avenue_quotethat carriesseqis a tick-by-tick frame. - Errors
quote_modewithoutperpetuals.quotes, or next to a channel other than the perpetuals ones, fails withUNSUPPORTED_QUOTE_MODE. Any other value fails withINVALID_QUOTE_MODE.
{
"type": "system",
"event": "quote_mode",
"channel": "perpetuals.quotes",
"quote_mode": "conflated",
"code": "QUOTE_MODE_LIMIT",
"message": "These symbols already stream tick by tick on as many of this account's connections as allowed. This connection gets their conflated rows and switches to tick by tick when one frees up.",
"symbols": ["BTCUSDT"],
"scope": "account",
"limit": 2
}Normalized rows
With source-level access, "format": "normalized" renders every source row as one flat row priced in USD, the same shape for every source. The symbols in that subscribe get these rows in place of venue_quote and venue_mark; composite frames and trades do not change.
| Field | Meaning |
|---|---|
exchange | The source id, as in source_id on the other source rows. |
symbol | The source's base with USD: MSFTUSD, xyz:MSFTUSD, MSFT-USD-SWAP; USD/JPY as USDJPYUSD (xyz:USDJPYUSD). |
type | top_of_book on perpetuals.quotes, mark_price on perpetuals.marks, impact_price with impact prices. |
bid, mid, ask | USD, as strings. Always bid <= mid <= ask, all positive. A mark row carries its price in all three. |
timestamp | The source's event time, ISO 8601 with milliseconds. |
quote | Always USD. |
quote_source | The stablecoin the source quotes in, USDT or USDC. |
venue_symbol | The source's own instrument. |
fx_rate | The stablecoin/USD rate applied to the source prices. |
fx_sources | Ids of the reference markets behind fx_rate. |
fx_timestamp | Time of the oldest reading behind fx_rate, ISO 8601. |
contract | The perpetuals symbol of the subscribe. |
heartbeat_at | Only on repeats: when the row was sent again. Never on a new update. |
impact_usd | Only on impact_price rows: the order size in US dollars. |
{
"action": "subscribe",
"channels": ["perpetuals.quotes", "perpetuals.marks"],
"symbols": ["XAUUSDT"],
"format": "normalized"
}{
"exchange": "pv_01",
"symbol": "XAUUSD",
"type": "top_of_book",
"bid": "4314.595062",
"mid": "4314.635046",
"ask": "4314.67503",
"timestamp": "2026-10-10T12:00:03.477Z",
"quote": "USD",
"quote_source": "USDT",
"venue_symbol": "XAUUSDT",
"fx_rate": "0.999605",
"fx_sources": ["fx_01", "fx_02"],
"fx_timestamp": "2026-10-10T12:00:00.012Z",
"contract": "XAUUSDT",
"channel": "perpetuals.quotes"
}- Options
exchangeskeeps only those sources (["pv_01", "pv_04"]).heartbeat_msis the repeat interval, 5000 by default, 1000 to 60000, 0 for none.heartbeat_cap_msis the price age at which repeats stop, 300000 by default, 10000 to 1800000.impactandimpact_usdadd impact prices. The most recent subscribe for a symbol decides; subscribe again withoutformatto go back to the default frames.- USD rate
- A new row goes out only while the reference markets agree on the stablecoin's rate and every reading behind it is at most 60 seconds old. Otherwise the row is not sent at all: no row is better than a price on a rate that cannot be confirmed. A repeat is an exact copy of a row you were sent, its rate included.
- Repeats
- A top of book with no real update for
heartbeat_msis sent again exactly as it was, plusheartbeat_at. Itstimestampnever moves, soheartbeat_atminustimestampis the price age. Repeats stop once that age reachesheartbeat_cap_ms, and at once when the row is withdrawn or the subscription changes. Without a rate, or while the source is not delivering, they pause and resume inside the cap. Marks are never repeated. - Silence
- A withdrawn row, a source that is down and a lost rate are silence: this format has no status rows. A book seen again after a withdrawal or a restart on our side comes back as a repeat with
snapshot: true, never as a new update. - Snapshots
- The snapshot on subscribe and every restatement are repeats: they carry
heartbeat_atandsnapshot: true. A repeat withsnapshot: truemay hold prices you were not sent yet, for example a book that moved during a pause: take its values. A repeat without it is always an exact copy of the last row you were sent. A top of book older thanheartbeat_cap_msis not sent. Withsnapshot: false, quiet books start repeating one interval after the subscribe, the first time withsnapshot: true. - Applies to
perpetuals.quotesandperpetuals.marks. It combines withquote_mode: tick-by-tick rows carryseq. Spot reference books have no normalized rows. A source that quotes a contract in another unit (in_composite: false) is delivered in that unit.- REST
/perpetuals/quote/{symbol}?format=normalizedand the same on/perpetuals/markanswersymbol,formatandrows; addexchange=pv_01for one source./perpetuals/symbols?format=normalizedlists every source instrument this way, withbase,quote_source,in_compositeandimpact(whether impact prices are kept for it). Impact prices are on the stream only.- Messages
- A normalized subscribe is answered with a
formatmessage naming its symbols, withimpact_usdwhen impact prices were asked for. Without source-level access the subscribe still succeeds on the composite frames, and that message carriesFORMAT_UNAVAILABLE.IMPACT_UNAVAILABLEnames the symbols for which no source of the subscribe keeps impact prices; the rest of the subscribe is in effect. - Errors
INVALID_FORMAT,INVALID_EXCHANGES,INVALID_HEARTBEATandINVALID_IMPACTfor values outside the above;UNSUPPORTED_FORMATnext to a channel other than the perpetuals ones;UNSUPPORTED_FORMAT_OPTIONfor the options withoutformat.
Impact prices
With "impact": true in a normalized subscribe on perpetuals.quotes, each source also sends impact_price rows: the average price of a market order of impact_usd US dollars walked through that source's own book, best level first, on each side. bid is the average price of selling that amount, ask of buying it, fees not included.
{
"action": "subscribe",
"channels": ["perpetuals.quotes"],
"symbols": ["XAUUSDT"],
"format": "normalized",
"impact": true,
"impact_usd": 10000
}{
"exchange": "pv_01",
"symbol": "XAUUSD",
"type": "impact_price",
"bid": "4314.211407",
"mid": "4314.79381",
"ask": "4315.376213",
"timestamp": "2026-10-10T12:00:03.402Z",
"quote": "USD",
"quote_source": "USDT",
"venue_symbol": "XAUUSDT",
"fx_rate": "0.999605",
"fx_sources": ["fx_01", "fx_02"],
"fx_timestamp": "2026-10-10T12:00:00.012Z",
"contract": "XAUUSDT",
"channel": "perpetuals.quotes",
"impact_usd": 10000
}- Size
impact_usdis one of 1000, 5000, 10000, 25000, 50000 or 100000; 10000 when left out. The order is sized in US dollars at the same agreed rate the prices are converted with.- Thin books
- A side the source's book cannot fill at that size has no price, and the row is not sent: an impact price is never extrapolated past the levels a source shows. Always bid <= mid <= ask, with bid at or below the best bid and ask at or above the best ask.
- Updates
- A row goes out when the impact price moves;
timestampis the source's time of the book it was computed from. A quiet impact price is repeated like the top of book: the same row plusheartbeat_ateveryheartbeat_ms, untilheartbeat_cap_msafter itstimestamp. The snapshot on subscribe carries the current impact prices as repeats. A book that is lost, stops following the source, or no longer fills the size is silence: its repeats stop. - Coverage
- Impact prices are kept for a set of source instruments, flagged
impact: truein/perpetuals/symbols?format=normalized. A symbol without one gets the rest of its normalized rows, and anIMPACT_UNAVAILABLEmessage names it.
Row liveness and withdrawal
A source row's timestamp is the source's own event time, so a quiet book shows an old timestamp while the row is alive, which is common while the underlying market is closed. Do not alert on timestamp age: one rule, below, applies to every source and does not look at market hours.
fresh_book_ms- A book that changed within this window is live.
quiet_book_max_ms- Past 90 seconds an unchanged book stays live only while the same source's mark keeps arriving, and never longer than this.
withdraw_after_lane_loss_ms- A source connection that drops, goes silent or skips a sequence has its rows withdrawn after this, until a fresh book arrives.
- On REST
- A withdrawn row leaves at once and is never carried forward.
- On the stream
- Its own frame,
venue_status, so a consumer that switches ontypenever meets an empty price. The nextvenue_quoteorvenue_markfor that source and symbol means it is back; no frame in between means live and unchanged. - Status route
/perpetuals/sources/statusserves the same values in milliseconds.
{
"type": "venue_status",
"channel": "perpetuals.quotes",
"asset": "perpetuals",
"symbol": "XAUUSDT",
"source_id": "pv_02",
"venue_symbol": "XAUUSDT",
"native_currency": "USDT",
"market_type": "perpetual",
"status": "withdrawn",
"ts": 1789640504021
}ts is the event time of the last row delivered for that source. The same frame arrives on perpetuals.marks when a source mark is withdrawn.
Rows outside the composite
- Spot reference books
- Tagged
market_type: spot: top of book only, no mark and no funding. Delivered, never in the composite. - Different units
- A source that prices a contract in another unit than the rest, today one source on the two pre-IPO contracts. Its row is delivered raw and must not be rescaled, because the source publishes no fixed conversion factor: treat it as its own series.
- Outliers
- A live row too far from the cross-source median is left out of the composite for as long as it strays, while the row itself is still delivered unfiltered;
source_counton the composite says how many rows counted.
| Contracts | Left out beyond |
|---|---|
| Crypto | 1.5% from the median |
| Pre-IPO | 20% from the median |
| Everything else | 4% from the median |
Rows that never enter the composite carry in_composite: false in /perpetuals/symbols?sources=true, the complete list.
Per-source health
One row per source, for monitoring that runs on the source rows rather than the composite. Needs source-level access. Computed on every request from live state, so poll it as often as your alerting needs; there is no cache to wait out.
Request
curl -sS "https://api.tickerlayer.com/perpetuals/sources/status" \
-H "x-api-key: <YOUR_API_KEY>"Response 200 OK
{
"timestamp": 1789992789776,
"thresholds": {
"withdraw_after_lane_loss_ms": 5000,
"fresh_book_ms": 90000,
"quiet_book_max_ms": 1800000
},
"sources": [
{
"source_id": "pv_01",
"role": "composite",
"status": "up",
"symbols_listed": 229,
"symbols_live": 229,
"last_update": 1789992789712,
"oldest_live_update": 1789992731004
}
],
"fx": {
"USDT": { "status": "ok", "rate": 0.999605, "fx_sources": ["fx_01", "fx_02"], "fx_timestamp": "2026-09-21T11:33:05.012Z" },
"USDC": { "status": "ok", "rate": 0.99985, "fx_sources": ["fx_01", "fx_03"], "fx_timestamp": "2026-09-21T11:33:01.230Z" }
},
"normalization": {
"since_process_start": { "since": 1789900000000, "prices": 1520344, "usd": 1520101, "share": 0.99984, "by_source": [] },
"month_to_date": { "month": "2026-10", "prices": 18403210, "usd": 18399871, "share": 0.999819, "by_source": [] }
}
}timestamp- When this answer was computed, epoch milliseconds.
thresholds- The row liveness rule above, in milliseconds. The same for every source.
source_id- Permanent identifier of the source. It never changes and is never reused.
role- composite: its rows can enter the composite. reference: spot books, source rows only.
status- up: at least 90% of the listed symbols are live. degraded: fewer than 90% but at least one. down: none, and every row from this source has been withdrawn.
symbols_listed- Symbols this source can deliver a row for.
symbols_live- Of those, how many have a live row right now.
last_update- When we last received anything from this source, epoch milliseconds on our clock. Null when nothing is live.
oldest_live_update- Receive time of the quietest row that is still live. Null when nothing is live.
fx- Per stablecoin, the rate normalized rows use right now (
rate,fx_sources,fx_timestampin ISO 8601), orunavailablewith the reason. normalization- Share of source prices that had an agreed USD rate when they happened, per source: since our last restart and month to date (UTC).
Symbol and source universe
The symbol list with every source row each symbol can deliver. Needs source-level access. Poll it, for example once an hour, and diff it to learn about a new symbol, a new source row or a removed one before it reaches your ingest.
Request
curl -sS "https://api.tickerlayer.com/perpetuals/symbols?sources=true" \
-H "x-api-key: <YOUR_API_KEY>"Response 200 OK
{
"symbols": [
{
"symbol": "BNBUSDT",
"name": "BNB",
"instrument_type": "perpetual",
"underlying_asset_class": "crypto",
"base": "BNB",
"source_count": 6,
"sources": [
{
"source_id": "pv_01",
"venue_symbol": "BNBUSDT",
"native_currency": "USDT",
"market_type": "perpetual",
"in_composite": true
},
{
"source_id": "pv_07",
"venue_symbol": "BNBUSDT",
"native_currency": "USDT",
"market_type": "spot",
"in_composite": false
}
]
}
],
"count": 266
}Notes
source_id values are permanent. in_composite is the fixed rule for that row, not a live verdict. A symbol that exists only as a spot row is listed with instrument_type: spot and answers with an empty composite and its source rows. Additions are also announced in the docs updates rail.