Market Data
Real-time quotes, historical OHLCV, order book depth, and data feeds for stocks, crypto, and private securities
Liquidity.io provides market data across all asset classes -- public equities, crypto, private securities, pre-IPO, fixed income, commodities, and forex. Data is available via REST polling, Server-Sent Events (SSE), and WebSocket.
Asset Universe
| Market Rail | Categories | Examples | Data Source |
|---|---|---|---|
| Public | Stocks, Fixed Income, Commodities, Forex | AAPL, TSLA, SPY, GLD | Multi-broker feed, on-chain oracle |
| Private | Privates, Pre-IPO | SPACEX-PRE, STRIPE-PRE | Internal ATS |
| Digital | Crypto | BTC, ETH, SOL | BitGo Prime, DEX |
List Available Assets
GET /v1/assets?marketRail={rail}&category={category}&tradeable=true| Param | Type | Values |
|---|---|---|
marketRail | string | public, private, digital |
category | string | stocks, crypto, privates, pre-ipo, fixed-income, commodities, forex |
tradeable | boolean | Filter to tradeable assets only |
search | string | Full-text search by symbol or name |
Response:
{
"data": [
{
"id": "AAPL",
"symbol": "AAPL",
"name": "Apple Inc. Common Stock",
"type": "stocks",
"exchange": "NASDAQ",
"status": "APPROVED",
"price": 249.93,
"change": -4.30,
"changesPercentage": -1.69,
"image": "https://cdn.liquidity.io/assets/AAPL.png"
},
{
"id": "BTC",
"symbol": "BTC",
"name": "Bitcoin",
"type": "crypto",
"exchange": null,
"status": "APPROVED",
"price": 83421.50,
"change": 1250.00,
"changesPercentage": 1.52,
"image": "https://cdn.liquidity.io/assets/BTC.png"
}
],
"message": "ok"
}Real-Time Quotes
REST Polling
Poll the quote endpoint for the latest price data. Recommended interval: 5 seconds.
GET /v1/assets/{id}/quoteResponse:
{
"assetId": "AAPL",
"last": 249.93,
"open": 252.61,
"high": 254.91,
"low": 249.01,
"prevClose": 254.23,
"change": -4.30,
"changePercent": -1.69,
"volume": 979956,
"timestamp": "2026-03-19T00:34:44Z"
}SSE (Primary Real-Time Channel)
SSE is the primary real-time protocol. It is simpler than WebSocket, works through proxies and load balancers, and reconnects automatically on failure.
GET /api/realtime?subscribe=quote:AAPL,quote:BTC,quote:ETH
Authorization: Bearer {iam_access_token}
Accept: text/event-streamEvents:
event: quote
data: {"assetId":"AAPL","last":249.93,"bid":249.87,"ask":250.01,"volume":979956,"timestamp":"2026-03-19T00:34:44Z"}WebSocket (Legacy)
WebSocket is supported for backwards compatibility. New integrations should use SSE.
wss://api.{env}.liquidity.io/wsSubscribe to trades:
{
"type": "subscribe",
"channel": "trades",
"symbol": "BTC-USD"
}Order Book Depth
REST Polling
Poll the order book endpoint for current bid/ask depth. Recommended interval: 3 seconds.
GET /v1/assets/{id}/bookResponse:
{
"assetId": "AAPL",
"bids": [
{ "price": 239.87, "size": 100 },
{ "price": 239.50, "size": 250 }
],
"asks": [
{ "price": 265.40, "size": 100 },
{ "price": 265.75, "size": 150 }
],
"timestamp": "2026-03-19T00:34:45Z"
}SSE Book Updates
GET /api/realtime?subscribe=book:AAPLevent: book
data: {"assetId":"AAPL","bids":[{"price":249.87,"size":100}],"asks":[{"price":250.01,"size":100}],"timestamp":"2026-03-19T00:34:45Z"}WebSocket Book (DEX)
For the on-chain DEX, the WebSocket provides incremental order book updates:
{
"type": "subscribe",
"channel": "orderbook",
"symbol": "BTC-USDT",
"depth": 20
}You receive a full snapshot first, followed by incremental updates. A size of 0 means the price level has been removed.
Historical Data (OHLCV)
Chart Endpoint
GET /v1/assets/{id}/chart?range={range}&interval={interval}| Param | Values |
|---|---|
range | 1d, 5d, 1m, 3m, 6m, 1y, 5y |
interval | 1m, 5m, 15m, 1h, 1d |
Response:
{
"data": [
{
"time": 1773820800000,
"open": 255.00,
"high": 255.01,
"low": 255.00,
"close": 255.01,
"volume": 1084
},
{
"time": 1773824400000,
"open": 255.01,
"high": 255.50,
"low": 254.90,
"close": 255.45,
"volume": 2340
}
]
}Range and Interval Combinations
| Range | Recommended Intervals | Max Data Points |
|---|---|---|
1d | 1m, 5m | ~390 (1m) |
5d | 5m, 15m | ~390 (5m) |
1m | 15m, 1h | ~480 (15m) |
3m | 1h, 1d | ~540 (1h) |
6m | 1h, 1d | ~126 (1d) |
1y | 1d | ~252 (1d) |
5y | 1d | ~1260 (1d) |
Aggregated Market Data
Multi-Venue Orderbook
The @liquidityio/trading SDK aggregates order books across venues for best-execution analysis:
import { Client, Config, NativeVenueConfig, CcxtConfig } from '@liquidityio/trading';
const config = new Config()
.withNative('liquidity', NativeVenueConfig.liquidDex('https://api.liquidity.io/v1'))
.withCcxt('binance', CcxtConfig.create('binance').withCredentials('key', 'secret'))
.withSmartRouting(true);
const client = new Client(config);
await client.connect();
// Aggregated orderbook across all connected venues
const aggBook = await client.aggregatedOrderbook('BTC-USDC');
// Best prices across all venues
const bestBid = aggBook.bestBid();
const bestAsk = aggBook.bestAsk();
console.log(`Best bid: ${bestBid?.price} on ${bestBid?.venue}`);
console.log(`Best ask: ${bestAsk?.price} on ${bestAsk?.venue}`);
// Aggregated depth at each price level
const bids = aggBook.aggregatedBids();
const asks = aggBook.aggregatedAsks();
for (const level of bids.slice(0, 5)) {
console.log(`Bid ${level.price}: ${level.quantity} total`);
}
// Find best venue for a specific order size
const bestBuy = aggBook.bestVenueBuy(new Decimal('1.0'));
console.log(`Best venue to buy 1 BTC: ${bestBuy?.venue} at ${bestBuy?.price}`);Multi-Venue Ticker
// Get ticker from all venues for a symbol
const tickers = await client.tickers('BTC-USDC');
for (const t of tickers) {
console.log(`${t.venue}: bid=${t.bid} ask=${t.ask} last=${t.last} vol=${t.volume24h}`);
}
// Get ticker from a specific venue
const binanceTicker = await client.ticker('BTC-USDC', 'binance');Data Schemas
Quote Schema
| Field | Type | Description |
|---|---|---|
assetId | string | Asset identifier |
last | number | Last traded price |
open | number | Opening price (current session) |
high | number | Session high |
low | number | Session low |
prevClose | number | Previous session close |
change | number | Price change from previous close |
changePercent | number | Percentage change from previous close |
volume | number | Trading volume (shares or units) |
timestamp | string | ISO 8601 timestamp |
OHLCV Schema
| Field | Type | Description |
|---|---|---|
time | number | Unix timestamp in milliseconds |
open | number | Opening price for the interval |
high | number | Highest price in the interval |
low | number | Lowest price in the interval |
close | number | Closing price for the interval |
volume | number | Volume traded in the interval |
Trade Schema
| Field | Type | Description |
|---|---|---|
tradeId | string | Unique trade identifier |
assetId | string | Asset identifier |
price | number | Execution price |
size | number | Trade size |
side | string | buy or sell (taker side) |
timestamp | string | ISO 8601 timestamp |
Book Level Schema
| Field | Type | Description |
|---|---|---|
price | number | Price level |
size | number | Total quantity at this price level |
Code Examples
TypeScript -- Subscribe to Real-Time Quotes
const BASE_URL = 'https://api.liquidity.io';
function subscribeToQuotes(token: string, assets: string[]) {
const subscriptions = assets.map(a => `quote:${a}`).join(',');
const url = `${BASE_URL}/api/realtime?subscribe=${subscriptions}`;
const eventSource = new EventSource(url, {
headers: { 'Authorization': `Bearer ${token}` },
});
eventSource.addEventListener('quote', (event) => {
const quote = JSON.parse(event.data);
console.log(
`${quote.assetId}: $${quote.last} ` +
`(${quote.changePercent >= 0 ? '+' : ''}${quote.changePercent}%) ` +
`vol=${quote.volume}`
);
});
eventSource.onerror = () => {
console.error('SSE error, EventSource will reconnect automatically');
};
return eventSource;
}
// Subscribe to AAPL, BTC, and ETH quotes
const es = subscribeToQuotes(accessToken, ['AAPL', 'BTC', 'ETH']);
// Clean up
// es.close();TypeScript -- Fetch Historical OHLCV
async function fetchOHLCV(
assetId: string,
range: string,
interval: string,
token: string,
): Promise<{ time: number; open: number; high: number; low: number; close: number; volume: number }[]> {
const url = `${BASE_URL}/v1/assets/${assetId}/chart?range=${range}&interval=${interval}`;
const response = await fetch(url, {
headers: { 'Authorization': `Bearer ${token}` },
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const json = await response.json();
return json.data;
}
// Fetch 1-day chart with 5-minute candles
const candles = await fetchOHLCV('AAPL', '1d', '5m', accessToken);
for (const c of candles) {
console.log(
`${new Date(c.time).toISOString()} O=${c.open} H=${c.high} L=${c.low} C=${c.close} V=${c.volume}`
);
}Python -- Fetch Quotes and Historical Data
import asyncio
import aiohttp
from datetime import datetime
BASE_URL = "https://api.liquidity.io"
async def get_quote(session: aiohttp.ClientSession, asset_id: str, token: str) -> dict:
url = f"{BASE_URL}/v1/assets/{asset_id}/quote"
headers = {"Authorization": f"Bearer {token}"}
async with session.get(url, headers=headers) as resp:
resp.raise_for_status()
return await resp.json()
async def get_ohlcv(
session: aiohttp.ClientSession,
asset_id: str,
range_: str,
interval: str,
token: str,
) -> list[dict]:
url = f"{BASE_URL}/v1/assets/{asset_id}/chart?range={range_}&interval={interval}"
headers = {"Authorization": f"Bearer {token}"}
async with session.get(url, headers=headers) as resp:
resp.raise_for_status()
data = await resp.json()
return data["data"]
async def get_book(session: aiohttp.ClientSession, asset_id: str, token: str) -> dict:
url = f"{BASE_URL}/v1/assets/{asset_id}/book"
headers = {"Authorization": f"Bearer {token}"}
async with session.get(url, headers=headers) as resp:
resp.raise_for_status()
return await resp.json()
async def main():
async with aiohttp.ClientSession() as session:
token = "your_access_token"
# Real-time quote
quote = await get_quote(session, "AAPL", token)
print(f"AAPL: ${quote['last']} ({quote['changePercent']:+.2f}%)")
# Historical data
candles = await get_ohlcv(session, "AAPL", "1d", "5m", token)
for c in candles[-5:]: # Last 5 candles
ts = datetime.fromtimestamp(c["time"] / 1000).strftime("%H:%M")
print(f" {ts} O={c['open']:.2f} H={c['high']:.2f} L={c['low']:.2f} C={c['close']:.2f}")
# Order book
book = await get_book(session, "AAPL", token)
print(f"\nBook: {len(book['bids'])} bids, {len(book['asks'])} asks")
if book["bids"]:
print(f" Best bid: {book['bids'][0]['price']} x {book['bids'][0]['size']}")
if book["asks"]:
print(f" Best ask: {book['asks'][0]['price']} x {book['asks'][0]['size']}")
asyncio.run(main())Python -- SSE Streaming with sseclient
import json
import sseclient
import requests
def stream_quotes(token: str, assets: list[str]):
subs = ",".join(f"quote:{a}" for a in assets)
url = f"{BASE_URL}/api/realtime?subscribe={subs}"
headers = {
"Authorization": f"Bearer {token}",
"Accept": "text/event-stream",
}
response = requests.get(url, headers=headers, stream=True)
client = sseclient.SSEClient(response)
for event in client.events():
if event.event == "quote":
data = json.loads(event.data)
print(f"{data['assetId']}: ${data['last']} vol={data['volume']}")
# stream_quotes(token, ["AAPL", "BTC", "ETH"])Rate Limits
| Endpoint Type | Limit |
|---|---|
Public market data (/v1/assets/*) | 100 requests/minute |
| Authenticated endpoints | 300 requests/minute |
| SSE connections per user | 5 |
| WebSocket connections per user | 5 |
| Subscriptions per connection | 50 |
Rate limit headers are included in all REST responses:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1710807360When the limit is exceeded, the API returns 429 Too Many Requests with error code RATE_LIMIT_EXCEEDED.
Polling Intervals
If real-time channels (SSE, WebSocket) are unavailable, use REST polling with these recommended intervals:
| Data | Endpoint | Interval |
|---|---|---|
| Quote | GET /v1/assets/{id}/quote | 5 seconds |
| Order book | GET /v1/assets/{id}/book | 3 seconds |
| Order status | GET /v1/orders/{id} | 5 seconds |
| Chart data | GET /v1/assets/{id}/chart | 60 seconds |