Stocks Starter and Options Starter both include Massive's snapshot endpoints, but neither includes trades or quotes, so on those two plans a snapshot leaves last_trade and last_quote out of the response altogether instead of sending them empty. Developer adds the last trade and Advanced adds the last quote. An options snapshot's underlying_asset is decided by a different plan again: its market data comes from your Stocks plan, not your Options plan.
Applies to
- Plans: Starter and higher. The pricing page lists snapshots on every Starter, Developer and Advanced individual plan and on no Basic plan.
- Endpoints: /v3/snapshot (the unified snapshot), /v2/snapshot/locale/us/markets/stocks/tickers/{stocksTicker}, and the options snapshots /v3/snapshot/options/{underlyingAsset} and /v3/snapshot/options/{underlyingAsset}/{optionContract}.
- Asset classes: stocks and options for the plan-dependent fields. The unified snapshot also returns indices, forex and crypto.
Which snapshot fields depend on your plan
These snapshot fields depend on your plan, according to the documentation for the unified snapshot, the single-ticker stocks snapshot and the option contract snapshot. The last column comes from the pricing page:
| Field | What decides it | Stocks and Options individual plans that return it |
|---|---|---|
| last_trade (lastTrade on the single-ticker stocks snapshot) | Whether your plan includes trades | Developer and Advanced |
| last_quote (lastQuote on the single-ticker stocks snapshot) | Whether your plan includes quotes | Advanced |
| fmv and fmv_last_updated | Whether you hold a Business plan | None; fair market value is on Business plans |
| underlying_asset on an options snapshot | Your Stocks plan, not your Options plan | With no Stocks plan it holds only the underlying's ticker; what else appears depends on your Stocks plan |
A last trade or last quote your plan does not include is left out of the snapshot, not returned as null, so test for the key before reading it.
How to see which fields your plan returns
To see which snapshot fields your plan returns, ask the unified snapshot for one ticker from each asset class:
curl -X GET "https://api.massive.com/v3/snapshot?ticker.any_of=AAPL,O:SPY281215C00700000,I:SPX,X:BTCUSD,C:EURUSD&apiKey=YOUR_API_KEY"
Response with a Stocks Starter key, trimmed:
{
"results": [
{
"ticker": "AAPL",
"type": "stocks",
"session": { "close": 336.67, "price": 336.51 },
"last_minute": { "close": 336.5, "transactions": 19 }
},
{ "ticker": "O:SPY281215C00700000", "error": "NOT_ENTITLED", "message": "Not entitled to this ticker." },
{ "ticker": "I:SPX", "error": "NOT_ENTITLED", "message": "Not entitled to this ticker." },
{ "ticker": "X:BTCUSD", "error": "NOT_ENTITLED", "message": "Not entitled to this ticker." },
{ "ticker": "C:EURUSD", "error": "NOT_ENTITLED", "message": "Not entitled to this ticker." }
],
"status": "OK"
}
On Stocks Starter the full AAPL row holds market_status, name, ticker, type, session and last_minute, with no last_trade or last_quote, and the four tickers outside the Stocks plan each come back as a row of their own.
The same request with an Options Starter key that holds no Stocks plan, at the same time, returns this, trimmed to its first two rows:
{
"results": [
{ "ticker": "AAPL", "error": "NOT_ENTITLED", "message": "Not entitled to this ticker." },
{
"open_interest": 838,
"underlying_asset": { "ticker": "SPY" },
"ticker": "O:SPY281215C00700000",
"type": "options"
}
],
"status": "OK"
}
On Options Starter the option row has no last_trade, last_quote or break_even_price, and its underlying_asset holds only the ticker, because the key has no Stocks plan. AAPL is a stock, so the Options Starter key gets a NOT_ENTITLED row for it.
The same request with a key whose plans include trades and quotes, at the same time, returns this, trimmed to its first two rows:
{
"results": [
{
"ticker": "AAPL",
"type": "stocks",
"last_quote": { "timeframe": "REAL-TIME", "ask": 336.49, "bid": 336.41 },
"last_trade": { "timeframe": "REAL-TIME", "price": 336.45, "size": 1 }
},
{
"break_even_price": 873.865,
"last_quote": { "midpoint": 173.865, "timeframe": "REAL-TIME" },
"last_trade": { "price": 173.2, "timeframe": "REAL-TIME" },
"underlying_asset": { "price": 777.0688, "ticker": "SPY", "timeframe": "REAL-TIME" },
"ticker": "O:SPY281215C00700000",
"type": "options"
}
],
"status": "OK"
}
With trades and quotes in the plan, both rows carry last_trade and last_quote, and the option's underlying_asset carries the price of SPY. The break_even_price of 873.865 is the 700 strike plus the quote's 173.865 midpoint.
A ticker outside your plan comes back as a NOT_ENTITLED row
A ticker outside your plan is not dropped from the unified snapshot. It comes back as its own row with "error": "NOT_ENTITLED" and "message": "Not entitled to this ticker.", inside an HTTP 200 whose status is OK, as the Stocks Starter response above shows for an option, an index, a crypto pair and a forex pair. Check every row for an error field rather than comparing the number of rows with the number of tickers you sent. To find which product covers a ticker, see Why am I not authorized (NOT_AUTHORIZED) when calling Massive's REST API or WebSockets?, which shows the same row.
The single-ticker stocks snapshot on Stocks Starter
The single-ticker stocks snapshot follows the same rule under its own field names, lastTrade and lastQuote:
curl -X GET "https://api.massive.com/v2/snapshot/locale/us/markets/stocks/tickers/AAPL?apiKey=YOUR_API_KEY"
Response with a Stocks Starter key, trimmed:
{
"ticker": {
"ticker": "AAPL",
"day": { "c": 336.67, "v": 33371101.0 },
"min": { "t": 1791405180000, "n": 18, "c": 336.5031, "v": 211 },
"prevDay": { "c": 333.63 }
},
"status": "OK"
}
On Stocks Starter the full ticker object holds day, min, prevDay, ticker, todaysChange, todaysChangePerc and updated, with no lastTrade or lastQuote. The minute bar does carry n, the number of transactions in the bar. Its t of 1791405180000 is in milliseconds: the minute from 4:33 to 4:34 PM ET. On Stocks Starter the newest minute bar trails the time of the request by about 15 minutes, in line with the 15-minute delay the documentation lists for Stocks Starter.
Getting several tickers in one request
The unified snapshot at /v3/snapshot returns the latest state your plan allows for up to 250 tickers across stocks, options, indices, forex and crypto in a single request. Its field set differs by type as well as by plan: an index has a value rather than a quote, a forex pair has a last_quote but no last_trade, and only options carry Greeks, so read type before you read the rest. The snapshot is the present only, with no range parameter, so it is the wrong tool for four common needs:
| What you want | Use instead |
|---|---|
| Bars over a date range | /v2/aggs/ticker/{ticker}/range/... |
| Every trade or quote in a window | /v3/trades and /v3/quotes |
| The same values continuously | The WebSocket, rather than polling the snapshot |
| Every stock's daily bar at once | /v2/aggs/grouped/locale/us/market/stocks/{date} |
Polling the snapshot in a loop is the pattern most likely to run into a rate limit, and it still lags by the poll interval. If you need it continuously, stream it.
Why this happens
Snapshot fields follow your plan because, as the snapshot documentation states, the last trade is returned only if your plan includes trades and the last quote only if it includes quotes, and Stocks Starter and Options Starter include snapshots but neither trades nor quotes. The differences between asset classes have a separate cause, the markets themselves: forex has no consolidated trade tape, so there is nothing to put in a last-trade field for a currency pair; an index is a calculated value rather than something that trades; and Greeks only exist for a contract with an underlying and an expiry.
If you see an error
"error": "NOT_ENTITLED" with "message": "Not entitled to this ticker." on one row of a unified snapshot means that ticker is outside your plan. The other rows are unaffected.
HTTP 403 with {"status":"NOT_AUTHORIZED","message":"You are not entitled to this data. Please upgrade your plan at https://massive.com/pricing"} on a single-ticker, option contract or option chain snapshot means the whole endpoint is outside your plan. An Options Starter key with no Stocks plan gets this on /v2/snapshot/locale/us/markets/stocks/tickers/AAPL, and a Stocks Starter key with no Options plan gets it on /v3/snapshot/options/SPY/O:SPY281215C00700000.
A symbol we do not recognize is not missing from results: it comes back as its own row with "error": "NOT_FOUND" and "message": "Ticker not found.". Check the prefix: O: for options, I: for indices, X: for crypto and C: for forex.
A 429 from a snapshot loop is the rate limit. Move to the WebSocket rather than adding retries.
Related
- Why am I not authorized (NOT_AUTHORIZED) when calling Massive's REST API or WebSockets?
- What are the different Massive subscriptions I can use?
- What is the max number of tickers I can pass through Massive's snapshot?
- How can I get the daily prices for all stocks using Massive's market data?
- What is the difference between RESTful APIs and WebSockets?
- Does Massive support Greeks for index option contracts?

