Market Data Snapshot API
The Market Data Snapshot API gives you on-demand access to ElectronX market data over a single REST endpoint. Each request returns a point-in-time snapshot as a JSON payload: you choose what kind of data comes back with the channel parameter, and you set the scope with the product_ids parameter. This guide covers how the endpoint is structured, how to authenticate, what each channel returns, how to read the timestamps, and how to work with republished ISO prices. It is written for a developer or quantitative user who wants to pull ElectronX market data into their own tools.
What this API covers, and what it does not
The API is backed by the same market data source as the ElectronX trading interface, and it covers all seven supported ISOs: ERCOT, PJM, CAISO, MISO, SPP, ISO-NE, and NYISO. Real-time, day-ahead, and settlement prices are available across the relevant channels.
The API returns market-wide data only. It does not expose account-specific information. Open orders, executions, settlements, committed collateral, positions, and cash balances are not available through this endpoint. In other words, this is a market data feed, not a drop copy of your own trading activity. If a drop copy is something you need, contact your ElectronX Market Operations representative to discuss it separately.
How the snapshot model works
Each request returns a single snapshot: the state of the requested data at one moment in time, rather than a continuous stream. To keep a view current, poll the endpoint on whatever cadence your use case calls for.
Every response carries a response header, X-Snapshot-Age-Ms, that reports the age of the returned snapshot in milliseconds. It is the most direct measure of how fresh a response is relative to the moment you made the request.
Snapshots may be up to roughly 15 seconds old. Read the X-Snapshot-Age-Ms response header to check the age of the data before relying on it.
Endpoint
The endpoint is available in both the Beta and Production environments. All requests are GET requests, with parameters supplied in the query string.
| Environment | Endpoint |
|---|---|
| Beta | https://beta.electronx.com/api/v1/mkdata/snapshot |
| Production | https://trade.electronx.com/api/v1/mkdata/snapshot |
Parameters are supplied in the query string:
?channel=CHANNEL&product_ids=PRODUCT&api_key=YOUR_API_KEY
Authentication
Requests are authenticated with an individual API key, supplied as the api_key query parameter. Each key is tied to a specific user and is generated in the ElectronX trading platform. Append your key to the end of the request URL:
...&api_key=YOUR_API_KEY
Throughout this guide, YOUR_API_KEY is a placeholder. Replace it with the individual key issued to you. See examples in the Worked examples section below; see Getting access for how to obtain your key.
Request parameters
| Parameter | Required | Description |
|---|---|---|
channel | Yes | The type of market data to return. A single value only. Unlike product_ids, this parameter does not accept a comma-separated list; to pull more than one channel, make a separate request for each. See Channels. |
product_ids | Yes | Comma-separated list of products to return, at ISO, hub, or contract granularity. See Product scope. |
api_key | Yes | Your individual API key. See Authentication. |
One thing worth internalizing early: channel and product_ids behave differently. You can name many products in one call, but only one channel. A request for the order book across ERCOT and PJM is one call; a request for the order book and the ticker is two calls.
Channels
Set the channel parameter to one of the following values. The description explains what the channel returns and when you would reach for it.
| Channel | Returns |
|---|---|
status | Instrument trading state only: OPEN, CLOSED, EXPIRED, or HALTED for each contract. Use it to check whether a contract is currently tradable before acting on other data. |
instrument | Full contract reference data: expiry, start moment, ISO, hub, contract type and code, hour ending, multiplier, price scale, tick size, and payout limits. For Bounded Futures, the payout limits include a floor and a ceiling; the floor can be below $0 on some variants, so do not assume it is non-negative when you parse this field. Use it to map a symbol to its parameters. This channel also carries the hour_ending, hour_ending_date, and node_timezone fields described under Reading timestamps and time zones. |
level2 | Order book depth: resting bid and ask quantities at each price level. |
ticker | Last traded price for a contract, with session high, session low, and session volume. |
market_trades | Recent executed trades across the market, returned as one set per market (ISO and hub), covering the newest 5,000 trades or the last 48 hours, whichever comes first. Each trade includes a trade id, product, price, size, side, and execution time, and each market carries a complete_since timestamp marking the point from which its trade history is complete. See Working with the market_trades channel. |
marks | Reference marks per contract: best bid, best ask, and day-ahead (DAM) value, each with its own timestamp. |
settlement | Final settlement price and payout price for delivered hours. Binary contracts also include the day-ahead price. |
hot | The most liquid contracts and products, ranked by liquidity across ISOs. |
physical | Republished ISO system prices, such as day-ahead, RT5, RT15, SPP15, and SETTLE60. See Working with the physical channel. |
Product scope
The product_ids parameter accepts a comma-separated list at three levels of granularity.
| Granularity | Format | Example |
|---|---|---|
| An entire ISO | ISO | ERCOT, PJM, CAISO, MISO, SPP, ISONE, NYISO |
| An ISO and hub | ISO-HUB | ERCOT-HB_NORTH |
| A specific contract | CONTRACT-ID | BFUT100-CAISO-HB_SP15-260628-08 |
Supported ISOs: ERCOT, PJM, CAISO, MISO, SPP, ISONE, NYISO.
A contract id encodes the product and its scope. Reading the example above: BFUT100 is a Bounded Futures contract whose bounded-range ceiling is 100 (the CAISO Standard variant), CAISO is the ISO, HB_SP15 is the hub, 260628 is the date in yy-mm-dd form, and 08 is the hour ending. Binary Options use the BOPT prefix in the same positional pattern.
The numeric suffix on a Bounded Futures symbol is the ceiling of that contract's bounded price range, and the ceiling differs by ISO. Some ISOs also append an ISO-unique character after the ceiling when the same ceiling exists on another ISO. If you are mapping symbols programmatically, treat the suffix as the ceiling for that specific ISO's variant rather than a fixed value across the board, and use the per-ISO product specifications on the products page as the reference for exact symbols and bounds. The floor is a separate value, set per ISO and per variant, and is not encoded in the symbol; on some variants it is below $0, so a symbol that shows only a ceiling does not imply a floor of $0.
Reading timestamps and time zones
Time appears in more than one place in the responses, and the fields do not all mean the same thing. Reading them correctly is the difference between a snapshot you can trust and one you misinterpret.
Two time fields on order book data
An order book (level2) response can carry two time-related values:
event_timesits on each individual bid or ask update. It is the time that quote occurred in the matching engine.timestampis the time that contract's order book last changed. In a single call spanning multiple products, each contract carries its owntimestamprather than one shared value.
Because timestamp reflects the last change to the book, it behaves differently by contract. On a thinly quoted contract, the timestamp can look old even though the response itself is current: it simply means the book has not changed since that time, not that the data is stale. On an actively quoted contract, the timestamp updates continuously. To judge how fresh a response is relative to your actual request, use the X-Snapshot-Age-Ms response header rather than timestamp.
Note that timestamp carries a different meaning on the physical channel, where it identifies the delivery interval a price applies to. See Response fields below.
UTC versus node-local time
All timestamps are in UTC, indicated by the trailing Z.
One set of fields is the exception, and it maps to delivery hours. A contract's delivery references, hour_ending and hour_ending_date, are in the node's local time, not UTC. Each instrument record carries node_timezone so you can line them up.
For example, an ERCOT contract shows node_timezone America/Chicago, hour_ending 17, and an expiry of 2026-07-05T22:00:00Z, because 5:00 PM Central in July is 22:00 UTC. The same hour ending on a CAISO contract, where node_timezone is America/Los_Angeles, has a UTC expiry two hours later, reflecting Pacific time. Read the delivery hour in the node's local time, and read the expiry timestamp in UTC.
Working with the market_trades channel
Unlike the point-in-time channels, market_trades returns a trailing window of recent executed trades rather than a single current value. The exchange keeps the newest trades per market, up to 5,000 trades or the last 48 hours, whichever comes first, and returns them one set per market. A request at ISO granularity returns multiple sets, one per market, and some may be empty, so iterate over every set in the response rather than assuming a single set.
Each set carries a complete_since timestamp: the point from which every trade in that market is present in the snapshot. Trades are identified by trade_id, so if you combine a snapshot with a live feed, deduplicate on that value.
Prices on the market_trades channel are integers in hundredths of a dollar. Divide by 100 to read the price in dollars per MWh: 3150 is $31.50. The level2 channel scales price_level the same way. This differs from the physical channel, which returns prices as decimal dollar strings such as "24.60". The instrument channel reports each contract's price_scale if you want to derive the divisor rather than assume 100.
Each trade in a set carries the following fields.
| Field | Description |
|---|---|
trade_id | Unique identifier for the trade. Use it to deduplicate. |
product_id | The contract that traded. |
price | Trade price, as an integer in the contract's price scale (hundredths of a dollar). For example, 3150 is $31.50. |
size | Number of contracts in the trade. |
side | The side of the trade, SIDE_BUY or SIDE_SELL. |
transact_time | Execution time, in UTC. |
Working with the physical channel
The physical channel republishes ISO system prices. A single request returns multiple price series in one response, for example day-ahead, real-time, and settlement prices, with each row tagged by a variant field. To use one series, request the channel and filter the returned rows by their variant value. For day-ahead prices, for example, keep the rows where variant is DA24.
Response fields
Each row in a physical response carries the following fields.
| Field | Description |
|---|---|
market | The ISO the price belongs to (for example, ERCOT or PJM). |
listing | The hub or location the price applies to. |
variant | The price series this row belongs to. See Price variants. |
timestamp | The delivery interval the price applies to. |
recorded_at | When the price was recorded. |
price | The price value. |
Price variants
The physical channel returns the following price variants.
| Variant | Interval | Description |
|---|---|---|
DA24 | Hourly | Day-ahead market price. |
RT5 | 5-minute | 5-minute real-time price. Published for every ISO except CAISO. |
RT15 | 15-minute | 15-minute real-time price. CAISO publishes its real-time prices here, on 15-minute intervals. See the note below. |
SETTLE60 | Hourly | Final hourly settlement price. |
INDIC60 | Hourly | Indicative (preliminary) hourly settlement price. |
SPP15 | 15-minute | ERCOT 15-minute real-time Settlement Point Price. This is an ERCOT price metric and is not related to SPP, the Southwest Power Pool ISO, or to SP15, a CAISO hub. See the note below. |
Three similar-looking terms appear in this API and they mean different things. SPP15 is a variant on the physical channel and refers to ERCOT's 15-minute real-time Settlement Point Price. SPP on its own is one of the supported ISOs, the Southwest Power Pool, and is used as a value for product_ids. SP15 is a CAISO hub (for example, CAISO-HB_SP15). When you read or construct a request, keep these three apart.
CAISO's real-time prices are published as RT15, because CAISO publishes them on 15-minute intervals. CAISO has no RT5 series. Earlier, these same 15-minute prices were published under RT5. If your code reads CAISO real-time prices from RT5 rows, or from contract IDs ending in -RT5, change it to read RT15. Every other ISO still publishes RT5.
Variant availability by ISO
Not every variant is published for every ISO. DA24 and SETTLE60 are available across all seven ISOs. Real-time prices are RT5 for every ISO except CAISO, which publishes RT15. The others are ISO-specific, as shown below.
| ISO | DA24 | RT5 | RT15 | SETTLE60 | INDIC60 | SPP15 |
|---|---|---|---|---|---|---|
| ERCOT | Yes | Yes | No | Yes | No | Yes |
| PJM | Yes | Yes | No | Yes | Yes | No |
| CAISO | Yes | No | Yes | Yes | No | No |
| MISO | Yes | Yes | No | Yes | Yes | No |
| SPP | Yes | Yes | No | Yes | No | No |
| ISONE | Yes | Yes | No | Yes | No | No |
| NYISO | Yes | Yes | No | Yes | No | No |
Worked examples
Example: retrieving day-ahead prices
Request the physical channel for the ISO you want, then keep the rows where variant equals DA24:
copyhttps://trade.electronx.com/api/v1/mkdata/snapshot?channel=physical&product_ids=ERCOT&api_key=YOUR_API_KEY
A day-ahead row in the response looks like this:
copy{
"market": "ERCOT",
"listing": "HB_HOUSTON",
"variant": "DA24",
"timestamp": "2026-09-14T08:00:00Z",
"recorded_at": "2026-09-13T17:58:23Z",
"price": "24.60"
}
Retrieve the current order book for ERCOT and PJM
Because product_ids accepts a list, both ISOs come back in one call. As a single request URL:
copyhttps://trade.electronx.com/api/v1/mkdata/snapshot?channel=level2&product_ids=ERCOT,PJM&api_key=YOUR_API_KEY
The same request, with the parameters shown separately:
copyGET https://trade.electronx.com/api/v1/mkdata/snapshot
?channel=level2
&product_ids=ERCOT,PJM
&api_key=YOUR_API_KEY
The same request in Python 3
copy#!/usr/bin/env python3
import requests
r = requests.get(
"https://trade.electronx.com/api/v1/mkdata/snapshot",
params={
"channel": "level2",
"product_ids": "ERCOT,PJM",
"api_key": "YOUR_API_KEY",
},
)
print(r.json())
print("X-Snapshot-Age-Ms:", r.headers.get("X-Snapshot-Age-Ms"))
Replace YOUR_API_KEY with the individual key issued to you by ElectronX.
Response
The response body is a JSON payload whose shape depends on the requested channel. In addition, the following response header is returned:
| Header | Description |
|---|---|
X-Snapshot-Age-Ms | Age of the returned snapshot, in milliseconds. |
As a reminder, snapshots may be up to roughly 15 seconds old. Read X-Snapshot-Age-Ms to confirm the age of the data before relying on it.
Getting access
API keys are individual to each user, and each key is specific to an environment: a key generated in Beta authenticates only against the Beta endpoint, and a key generated in Production authenticates only against Production. You can hold one active key in each environment.
Generate your key from the REST API Snapshot tab in the Settings area of the ElectronX trading platform, in the environment you are currently in. The tab indicates which environment the key applies to. You can view your key there on demand: the same value appears each time, with a copy-to-clipboard control, and it is available across your devices in that environment.
If you have any trouble accessing the tab or your key, contact support@electronx.com.