Lighter, an onchain crypto exchange, gives builders a free public API. Below are the parts you need to ship: base URLs, the REST endpoints worth knowing, WebSocket channels, API keys, rate limits and funding units. Every call below ran on 8 November 2026, and every output is what came back. The last section shows where Lighter's own API runs out of history, and what the same jobs return from 0xArchive.
Short answer
- What it is: free public REST and WebSocket APIs for markets, order books, trades, funding and candles, plus signed endpoints for accounts and trading. Official SDKs: Python (
pip install lighter-sdk), Go and TypeScript. - REST:
https://mainnet.zklighter.elliot.ai. WebSocket:wss://mainnet.zklighter.elliot.ai/stream. Market data needs no key. - Rate limit: 60 requests per rolling minute for a standard client, with heavier endpoints weighted. Our burst test got its first
429on request 58. - History it publishes: funding and candles over REST, and every trade since 17 January 2025 as free daily files. We paged BTC hourly funding back to that day in 23 requests.
- History it does not publish: past order books. 0xArchive serves those, and trades by market, account and time window in one request.
Why build on Lighter's API
Lighter runs a public order book where every resting order carries the account that placed it. Markets, books, trades, funding and open interest are open to anyone, over REST and WebSocket. That makes it a good venue for dashboards, scanners and research tools: you can see who is quoting, not only what is quoted. The official reference is at docs.lighter.xyz/api. The older apidocs.lighter.xyz address redirects there.
Step 1: base URLs and markets
| Exchange | REST | WebSocket |
|---|---|---|
| Lighter mainnet | https://mainnet.zklighter.elliot.ai | wss://mainnet.zklighter.elliot.ai/stream |
| Lighter testnet | https://testnet.zklighter.elliot.ai | wss://testnet.zklighter.elliot.ai/stream |
| Lighter on Robinhood Chain | https://api.rh.lighter.xyz | wss://api.rh.lighter.xyz/stream |
| Lighter on Robinhood Chain testnet | https://api.rh-testnet.lighter.xyz | wss://api.rh-testnet.lighter.xyz/stream |
Lighter on Robinhood Chain is a separate exchange with its own accounts, keys and markets. This guide uses mainnet.
GET /api/v1/orderBooks lists every market. On our run it returned 246: 235 perpetuals (215 active, 20 inactive) and 11 spot markets. Each market has a numeric market_id that every other call takes. BTC is 1 and ETH is 0.
The snippets are short prototypes with no retry logic.
Step 2: the REST endpoints worth knowing
Market data lives under /api/v1/. Account reads need an auth token, orders go through sendTx and sendTxBatch, and chain data (blocks, batches, account logs) has its own Explorer API at https://explorer.elliot.ai/api with separate limits. The endpoints we tested:
| Endpoint | Returns | Our result |
|---|---|---|
/api/v1/orderBooks | Markets, fees, tick sizes, open interest | 246 markets, 222 ms |
/api/v1/orderBookDetails?market_id=1 | Mark, index and last price, 24h volume, funding clamps | 200, 194 ms |
/api/v1/orderBookOrders?market_id=1&limit=5 | Resting orders now, each with owner_account_index | 5 asks |
/api/v1/recentTrades?market_id=1&limit=100 | Latest trades, 100 at most | 100 rows |
/api/v1/fundings | Funding history at 1h or 1d | 750 rows a page |
/api/v1/candles | OHLCV candles | 500 rows a page |
/api/v1/funding-rates | Current funding on Lighter and three other exchanges | 732 rows |
/api/v1/trades only accepts sort_dir=desc. Sending asc returns 400 invalid param.
The same call from the Python SDK
Lighter's official Python SDK wraps these endpoints as typed clients. OrderApi.order_books() is /api/v1/orderBooks, and it needs no key:
"""sdk_first_read.py: a first read with Lighter's official Python SDK. Install: pip install lighter-sdk. No key needed."""
import asyncio
import lighter
async def main():
client = lighter.ApiClient() # Lighter mainnet by default
books = (await lighter.OrderApi(client).order_books()).order_books
perps = [b for b in books if b.market_type == "perp" and b.status == "active"]
print(len(perps), "active perpetual markets")
for b in perps:
if b.symbol in ("BTC", "ETH"):
print(f"{b.symbol}: market_id {b.market_id}, min size {b.min_base_amount}, "
f"min order ${float(b.min_quote_amount):.0f}, size decimals {b.supported_size_decimals}, "
f"price decimals {b.supported_price_decimals}")
await client.close()
asyncio.run(main())Run on 9 November 2026 with lighter-sdk 1.1.8:
215 active perpetual markets
BTC: market_id 1, min size 0.00007, min order $10, size decimals 5, price decimals 1
ETH: market_id 0, min size 0.0020, min order $10, size decimals 4, price decimals 2Of the 246 markets in the table above, 215 were active perpetuals, 20 inactive perpetuals and 11 spot markets. Use min_base_amount and the size and price decimals to round every order before you sign it.
Step 3: WebSocket channels
Connect to /stream, send {"type":"subscribe","channel":"..."}, then read one subscribed/... message followed by update/... messages. The public channels:
order_book/{market_id}: a full book on subscribe, then changes batched every 50 ms.trade/{market_id}: new trades.ticker/{market_id}: best bid and offer on every book change.market_stats/{market_id}ormarket_stats/all: mark and index price, open interest,current_funding_rate(the estimate for the next payment) andfunding_rate(the last payment).
Account channels (orders, positions, trades, transactions) need an auth token in the subscribe message. Send any frame at least every 2 minutes or the server closes the connection. After a disconnect, open a new connection, subscribe again and start from the fresh snapshot it sends, not from your old book.
Over 15 seconds on BTC we received 246 order_book updates, 311 ticker updates, 28 trade messages and 12 market_stats updates. From our server, the plain /stream URL returned HTTP 400 with a restricted-jurisdiction message. Lighter documents ?readonly=true for market data from those regions, and that form connected in 523 ms.
import asyncio, json
import websockets
URL = "wss://mainnet.zklighter.elliot.ai/stream?readonly=true"
async def main():
async with websockets.connect(URL) as ws:
print(await ws.recv())
await ws.send(json.dumps({"type": "subscribe", "channel": "market_stats/1"}))
for _ in range(3):
msg = json.loads(await ws.recv())
s = msg["market_stats"]
print(msg["type"], s["symbol"], "mark", s["mark_price"],
"current_funding_rate", s["current_funding_rate"])
asyncio.run(main()){"session_id":"e9c468dc-7739-4477-a7a2-8961225c86fc","type":"connected"}
subscribed/market_stats BTC mark 76040.1 current_funding_rate 0.0012
update/market_stats BTC mark 76039.2 current_funding_rate 0.0012
update/market_stats BTC mark 76039.3 current_funding_rate 0.0012That 0.0012 is a percent per hour, not a decimal. Step 4 explains.
Step 4: funding units
Lighter pays funding every hour. The same BTC hour appears in three scales depending on where you read it:
| Where | Field | BTC value | Unit |
|---|---|---|---|
Lighter /api/v1/fundings and WebSocket | rate, current_funding_rate | 0.0012 | Percent per hour |
Lighter /api/v1/funding-rates | rate | 9.6e-05 | Decimal per 8 hours |
0xArchive /v1/lighter/funding/BTC | funding_rate | 0.000012 | Decimal per hour |
All three are the same rate: 0.0012% is 0.000012, and 0.000012 times 8 is 0.000096. We checked five consecutive hours on 1 November 2026 and they matched. Lighter's history endpoint rounds to four decimal places of a percent. 0xArchive returns the unrounded value. Convert everything to one scale before you compare markets or venues.
Step 5: API keys and auth
Market data is open. Trading and private account data need credentials:
- Each account can hold API keys at indexes 0 to 254. Lighter reserves 0 to 3 for its own apps, so use 4 to 254. On Robinhood Chain, 157 is reserved too.
- Lighter's official SDKs do the signing: Python (
pip install lighter-sdk), Go (github.com/elliottech/lighter-go) and TypeScript with WASM. Each API key has its own nonce. The SDK tracks it for you, or you can read the next one fromnextNonce. - Requests are signed with the API private key. The auth token has the form
{expiry_unix}:{account_index}:{api_key_index}:{random_hex}and lasts up to 8 hours. Send it as theAuthorizationheader on REST, or as"auth"in a WebSocket subscribe. - For a dashboard that only reads, create a read token with
tokens/create. It cannot trade or withdraw and can last up to 10 years. Creating one costs 10,000 request weight, so do it once. - Authenticated requests count against your L1 address instead of your IP. That is the documented way past IP limits.
The older trade export, /api/v1/export/historicalTrades, returned 400 without a token. You do not need it for bulk trade history: Lighter now publishes that as free files, covered below.
Step 6: rate limits
From Lighter's rate limit page:
| Account type | Lighter mainnet | Lighter on Robinhood Chain |
|---|---|---|
| Standard | 60 requests a minute | 60 requests a minute |
| Plus | 24,000 weighted a minute | 120,000 weighted a minute |
| Premium (Advanced on mainnet) | 24,000 weighted a minute | 24,000 weighted a minute |
| Builder (apply through Lighter's Discord) | 240,000 weighted a minute | 150,000 weighted a minute |
All limits are per rolling minute for REST reads. On Plus and Premium accounts, order submission (sendTx and sendTxBatch) has its own budget, set by the LIT staked; standard accounts send orders out of the same 60. Limits count per IP and per L1 address. Authenticate each request and only the L1 limit applies.
Endpoints carry weights: recentTrades 600, trades 200 (600 on Robinhood Chain), accountActiveOrders 100, most others 300. Weights spend the budgets of the higher tiers: at 600 a call, a 24,000 budget allows 40 recentTrades calls a minute. A standard account counts requests instead, 60 a minute whatever the endpoint. WebSocket limits are per IP: 255 connections, 500 subscriptions per connection, 5,000 subscriptions per IP and 200 client messages a minute. Over a limit you get 429, sometimes 405. A firewall block lasts a flat 60 seconds, and REST and WebSocket limits trip together.
We tested it. 120 recentTrades calls with a different query string each time, sent over 25.9 seconds, returned 67 successes and 53 responses of 429 {"code":23000,"message":"Too Many Requests!"}. The first 429 came on request 58. Repeating one identical URL 125 times never hit the limit, because a cache in front of the API answered the repeats. Do not size a backfill from a test that repeats one URL.
Step 7: pull history from Lighter's API
Funding and candles reach the first day of a market. Page backward with end_timestamp:
import time
import requests
BASE = "https://mainnet.zklighter.elliot.ai"
books = requests.get(f"{BASE}/api/v1/orderBooks", timeout=30).json()["order_books"]
market_id = {b["symbol"]: b["market_id"] for b in books}
print(len(books), "markets; BTC market_id =", market_id["BTC"])
end_ms, rows, calls = int(time.time() * 1000), [], 0
while True:
page = requests.get(f"{BASE}/api/v1/fundings", timeout=30, params={
"market_id": market_id["BTC"], "resolution": "1h",
"start_timestamp": 0, "end_timestamp": end_ms, "count_back": 0,
}).json()["fundings"]
calls += 1
if not page:
break
rows = page + rows
end_ms = (page[0]["timestamp"] - 1) * 1000
time.sleep(1.1) # stay under 60 requests a minute
print(calls, "requests,", len(rows), "hourly rows; first:", rows[0])246 markets; BTC market_id = 1
23 requests, 15849 hourly rows; first: {'timestamp': 1737100800, 'value': '0.00000000', 'rate': '0.0000', 'direction': 'long'}BTC hourly funding starts on 17 January 2025 at 08:00 UTC. The walk took about 29 seconds. Hourly candles took 33 requests for the same 15,849 hours. For these two datasets, Lighter's free API is enough, and you should use it.
History: what Lighter's API covers and where it stops
Lighter publishes more history than most exchanges. As of 8 November 2026:
- Funding and candles over REST, back to the first hour of each market (Step 7).
- Every trade, free, as one Parquet file per UTC day in the public AWS Open Data bucket
s3://aws-public-blockchain/v1.1/lighter/core/mainnet/trades/, from 17 January 2025. No AWS account or payment is needed (--no-sign-request). Rows carry both account ids, the position before the fill and PnL. The file for 7 November 2026 was 117.5 MB and covers every market that day. - Hourly open interest for every perpetual, in the same bucket under
open_interest_hourly/.
Over REST, /api/v1/trades answered for a start time one hour back. At 65 minutes back, and at every older point we tried, it returned an empty list. Lighter's historical data page covers the bucket and the account endpoints.
What Lighter does not publish is the order book at a past moment. orderBookOrders and the order_book channel show the book now, and the historical data page lists no book dataset. For every Lighter dataset, its start date and how to pull it, see Lighter historical market data.
Same jobs on 0xArchive
0xArchive serves Lighter at https://api.0xarchive.io/v1/lighter with one key in an X-API-Key header. Lighter on Robinhood Chain has its own namespace at /v1/rh-lighter, with the same routes as mainnet except L3, which it does not capture. Trades, order books, funding, open interest and candles are on every plan. The Free plan covers the last 30 days. Build and above reach the full archive. See pricing.
| Job (BTC) | Lighter | 0xArchive |
|---|---|---|
| Hourly funding history | REST: 23 requests, 15,849 rows, from 17 Jan 2025 | 11 requests, 10,084 rows, from 25 Aug 2025 |
| Hourly candles | REST: 33 requests, 15,849 rows, from 17 Jan 2025 | 2 requests, 11,152 rows, from 1 Aug 2025 |
| BTC trades from 00:00 to 00:05 UTC, 1 Nov 2026 | Download that day's file for every market, then filter | 1 request, 3,594 trades, 0.2 s |
| Order book once a second for a minute | Not published | 1 request, 60 snapshots |
| Order book on 15 Feb 2026 | Not published | 1 request |
| Each resting order, with its account, on 1 Apr 2026 | Live only | 1 request |
| Fill log for one account, with position before and after | Build it from the daily files | Paged by account, from January 2025 |
Read it plainly. For funding, candles and bulk trades, Lighter's own free sources reach as far back as 0xArchive, or further. 0xArchive saves the file pipeline: one market, one account or one time window per request, on the same key as Hyperliquid. The data Lighter does not publish at all is the past order book, by price level and order by order. The full comparison is in Lighter's native API vs 0xArchive.
Where each 0xArchive dataset starts
From capabilities on 8 November 2026, and confirmed in our responses:
- Trades: 17 January 2025. The first weeks are thin: a 5-minute window on 1 February 2025 held 98 trades, against 3,594 in November 2026.
- Account fill log: January 2025, at
/v1/lighter/accounts/{account_index}/positions/changes. We pulled rows for 20 January 2025. Each row is a fill with the position before and after,event_type, realized PnL and fee. - Funding and open interest: 25 August 2025.
- Candles: 1 August 2025.
- Order book (L2): 29 January 2026, at
tick,1s,10s,30sorcheckpointgranularity. - L3, each resting order with its account: 5 March 2026. Rows carry
order_index,owner_account_index, side, price and size. These are Lighter account indexes, not wallet addresses.
The funding walk from Step 7, on 0xArchive:
import os
import requests
H = {"X-API-Key": os.environ["OXARCHIVE_API_KEY"]}
url = "https://api.0xarchive.io/v1/lighter/funding/BTC"
params = {"start": "2025-01-01T00:00:00Z", "end": "2026-11-08T16:00:00Z",
"interval": "1h", "limit": 1000}
rows, calls = [], 0
while True:
j = requests.get(url, headers=H, params=params, timeout=60).json()
calls += 1
rows += j["data"]
if not j["meta"].get("has_more"):
break
params["cursor"] = j["meta"]["next_cursor"]
print(calls, "requests,", len(rows), "hourly rows; first:", rows[0]["timestamp"], rows[0]["funding_rate"])11 requests, 10084 hourly rows; first: 2025-08-25T15:00:00Z 0.000011999999999999434Trades, past order books and L3, one request each:
import os
import requests
H = {"X-API-Key": os.environ["OXARCHIVE_API_KEY"]}
API = "https://api.0xarchive.io/v1/lighter"
t = requests.get(f"{API}/trades/BTC", headers=H, timeout=60, params={
"start": "2026-11-01T00:00:00Z", "end": "2026-11-01T00:05:00Z", "limit": 50000}).json()
print("trades:", len(t["data"]), "first:", t["data"][0]["timestamp"], t["data"][0]["price"], t["data"][0]["size"])
b = requests.get(f"{API}/orderbook/BTC/history", headers=H, timeout=60, params={
"start": "2026-11-01T12:00:00Z", "end": "2026-11-01T12:01:00Z",
"granularity": "1s", "depth": 5, "limit": 1000}).json()
print("order book:", len(b["data"]), "snapshots")
l3 = requests.get(f"{API}/l3orderbook/BTC", headers=H, timeout=60, params={
"timestamp": "2026-04-01T12:00:00Z", "depth": 3}).json()["data"]
o = l3["orders"][0]
print("L3 at", l3["timestamp"], "account", o["owner_account_index"], o["side"], o["price"])trades: 3594 first: 2026-11-01T00:00:00.041Z 77531.7 0.00516
order book: 60 snapshots
L3 at 2026-04-01T11:57:49.951Z account 388044 bid 68688.9Live and replay on 0xArchive
Connect to wss://api.0xarchive.io/ws with your key as a bearer token in the Authorization header, then send {"op":"subscribe","channel":"lighter_trades","symbol":"BTC"}. Live channels are lighter_orderbook, lighter_trades, lighter_funding and lighter_open_interest. lighter_l3_orderbook and lighter_candles are replay only. In 15 seconds on BTC we received 42 trade messages, 16 order book messages and 11 funding messages. Replaying five minutes of BTC trades from the day before delivered 1,638 messages in 4.2 seconds. Channel details are in the Lighter WebSocket reference.
Which one to use
- Live prices, funding estimates or the book right now: Lighter's WebSocket.
- Funding or candles back to January 2025: Lighter's REST. It is free and takes about 30 seconds per market.
- Every trade for whole days, in bulk: Lighter's public bucket.
- One market, account or window without a file pipeline, or Lighter and Hyperliquid on one key: 0xArchive.
- Past order books, by price level or order by order: 0xArchive. Lighter does not publish them.
What to know before you build
- Three funding scales. Percent per hour, decimal per 8 hours and decimal per hour. Convert first.
- Test limits with varied URLs. Cached repeats never count, so a naive test overstates your budget.
- Finalized trades end at the previous UTC day. On 8 November, 0xArchive's
meta.finalized_throughread2026-11-07T23:59:59.999Z, and a window from one hour earlier returned nothing from the history route. Read today's trades from/v1/lighter/trades/{symbol}/recentor the live channel. Replay works on finalized days. - Inactive markets. Lighter listed 20 inactive perpetuals and 0xArchive listed 28. Filter on
status. - Regions. Lighter blocks some jurisdictions. The
?readonly=trueWebSocket is the documented route for market data there.
FAQ
What is the Lighter API base URL?
https://mainnet.zklighter.elliot.ai for REST and wss://mainnet.zklighter.elliot.ai/stream for WebSocket. Testnet and Robinhood Chain use the URLs in Step 1.
What are the Lighter API rate limits?
On Lighter mainnet: 60 requests per rolling minute on a standard account, 24,000 weighted requests on Plus, Premium and Advanced, and 240,000 on Builder. Lighter on Robinhood Chain allows 120,000 on Plus and 150,000 on Builder. A 429 carries code 23000.
Does Lighter have a WebSocket API?
Yes. Public channels are order_book, trade, ticker and market_stats. Account channels need an auth token.
Does Lighter have a testnet API?
Yes. REST is https://testnet.zklighter.elliot.ai and WebSocket is wss://testnet.zklighter.elliot.ai/stream.
Is there a Lighter Python SDK?
Yes. pip install lighter-sdk installs the official Python SDK, which signs transactions and manages nonces. Lighter also publishes Go and TypeScript SDKs. You do not need any SDK to read market data: plain HTTP and WebSocket calls work.
Do I need an API key to read Lighter market data?
No. Keys are for trading and private account data.
Next steps
- Lighter data on 0xArchive and the Lighter REST reference.
- Data type pages: trades, L2 order books, L3 order books, funding.
- Lighter native API vs 0xArchive, the full history comparison.
- Funding rate arbitrage across Hyperliquid and Lighter.
- Lighter historical market data.
- 0xArchive rate limits and pricing.
Tested on 8 November 2026. The code is prototype code.