Market data
Cross-market listings, deals, flips and crafting costs across the seventeen markets we track.
Per-market price history, aggregated or raw
/api/marketplace/{market}/prices/historyPer-market price history — the transpose of GET /api/item/{id}/prices/history: this endpoint fixes the market and returns every item.
Each item is one aggregate row by default: mean, minimum and maximum listing price, and the day count behind them. Pass points=true for the raw daily series instead, paginated by item. avgListingPrice averages one point-in-time cheapest ask per day — not volume-weighted, not intraday, and not a sale price; for what items actually sold for, use GET /api/marketplace/{market}/sales-summary. Read avgDays and observedDays on every response: coverage is unequal between markets and often shorter than the window asked for, so an average over 3 days is otherwise indistinguishable from one over 30.
windows=, fields=, stats=, since= and dataPoints= each change the shape of the response and are documented individually below.
For Steam’s own sale volume there is a far deeper series than this archive holds: GET /api/item/{id}/sales/market?maxDays=0 returns one point per day back to the first sale Steam recorded for the item, where the archive behind this endpoint begins when we started capturing each market. It is a sale volume rather than a listing price, so the two answer different questions.
Warning: the newest daily bucket is provisional and self-heals overnight, because capture resumes from the start of the newest already-counted day. A window that includes today runs low unless the last bucket is excluded.
Warning: bids and asks are different quantities and must never be compared, averaged together or substituted. price and avgListingPrice are what you pay; bidPrice and avgBidPrice are what you would receive, and the difference is the spread you would cross to sell into this market. Only Steam, DMarket, Tradeit, RustSkins and Mannco publish a bid this API can read, so null is the normal answer and never means nobody is bidding. It is null on the other twelve markets, and that is a gap in what we can reach rather than a gap in the market: five of them are trade bots, which hold their own stock and run no order book, but a trade bot still quotes a price it will buy at, and Tradeit publishes exactly that. Read null as not published here, never as nobody bidding.
Warning: soldTotal is null whenever the counts cannot be summed honestly. Ten of the seventeen markets publish no sold count at all, and a trailing 24h or 30d figure is not a daily one. A further two — Mannco and RustSkins — publish one we do not collect nightly. The archive also holds a historical series imported from three vendors’ own trade histories: RustTM up to six thousand sales per item, RustSkins the last fifty sales per item and CS.Deals the last five thousand sales per item, the whole ledger on quieter items. For one of them — RustSkins — that series is not maintained, so past its newest dated point soldTotal returns to null: that means not collected, never nothing sold. null never means zero.
Parameters
| Name | In | Required | Description |
|---|---|---|---|
| market | path | yes | Market name, e.g. Skinport. A market that is not published returns 404 — the same answer an unknown market gets. |
| period | query | no | Time window. Defaults to 30d. An unrecognised value falls back rather than erroring.One of: |
| points | query | no | true returns the raw daily series instead of aggregates. It is the only shape here whose size grows with history depth — the aggregate is one row per item at any window, while a raw 90-day series runs to tens of megabytes per market — so prefer the default unless you need the shape of the move, and bound it with dataPoints=. |
| page | query | no | 1-based page index. Raw mode only. |
| pageSize | query | no | Items per page (max 500). Raw mode only. It also decides the resolution of each series: a raw page reads at most 12,000 observations and shares them equally between the items on it, so each item may return 12000 / pageSize of them — 120 at the default pageSize=100, 24 at the maximum of 500, and never more than 400. Every raw response reports the figure as maxPointsPerItem; lower pageSize to buy more points per item. An item holding more than the ceiling is sampled evenly across the window with the oldest and newest kept, so the ceiling bounds the resolution of a series and never its window. |
| windows | query | no | Comma-separated list of windows to aggregate in one response, e.g. 30d,60d,90d — one request instead of one per window. Each item then carries a windows map keyed by the window label, and the flat avgListingPrice, minPrice, maxPrice and sold fields are replaced by it, because a number whose window you have to infer is the defect avgDays exists to prevent. Every block carries its own avgDays, and you should read it before comparing windows: a 60-day and a 90-day average backed by the same 17 observed days are the same number twice, and the day count is the only thing on the wire that says so. In this mode period reports the widest window you asked for, because that is the window scanned and the one that decides which items appear at all; a window an item has no data in comes back with avgDays: 0 and null prices rather than being omitted. Accepts the same vocabulary as period, and a period= you pass as well is included rather than dropped. Unrecognised entries are ignored, and the parameter is ignored entirely when points=true. |
| stats | query | no | Comma-separated subset of avg,min,max,sold,bid limiting what each window block carries. An opt-out: the default is all of them, and an unrecognised list falls back to all of them rather than to none. avgDays is always present and cannot be excluded. bid adds the market’s best standing buy order to each block beside the ask. Multi-window mode only — it narrows the per-window blocks, where fields= narrows the flat row; the two are siblings rather than alternatives, and each is ignored in the other one’s mode. |
| since | query | no | ISO 8601 timestamp. Returns only the items observed strictly after it — pass the nextSince from your previous response, which every response carries whether or not you sent since. This feed updates once a day, so polling more often is cheap but usually returns an empty items: that is the parameter working, not a fault. The comparison is strict, so re-sending the same cursor returns nothing rather than repeating the last day. It selects which items come back and does not change the window they are aggregated over — a period=30d&since=<yesterday> row is still a 30-day average, of the items that moved yesterday. Applies in every mode. An unparseable value is a 400 rather than a silent full response, because a cursor quietly dropped would look exactly like a working one while you re-downloaded the catalogue on every poll. |
| fields | query | no | Comma-separated subset of avg,min,max,last,sold,bid,liquidity limiting what the flat aggregate row carries. An opt-in: the default is every group except liquidity, and an unrecognised list falls back to that same default rather than to none. Use it with since= and a poll costs almost nothing. liquidity is the one group outside the default, because it is the only field here that does not come from our price history: it scores Steam’s live order book and 30-day sales volume, so asking for it adds a second question to the request. id, marketHashName and avgDays can never be excluded — avgDays because it is the denominator that makes every other number readable. last and sold are groups rather than single fields: lastPrice and lastSupply travel together because a supply without the observation it was measured at is not interpretable, and soldTotal, avgSoldPerDay, soldDays and soldWindow because a sold total without its window cannot be read at all. bid adds the market’s best standing buy order — what it would pay you — beside the ask. An excluded field is absent from the row, while a field that is present and null means no such observation exists; the two are not the same. Aggregate mode only — the per-window blocks are narrowed by stats=. |
| dataPoints | query | no | Thin each item’s raw series to at most this many observations. The first and last are always kept and the rest are spread evenly between them, which is what a chart needs: the shape of the move at N points rather than every row plus a page cursor. The spread is even across the series rather than across the calendar, so a market that stopped reporting for a fortnight has that gap in its sample too — read each point’s own timestamp rather than assuming a fixed spacing. Raw mode only (points=true) and ignored otherwise, exactly as stats= is ignored outside multi-window mode; an unparseable value is ignored rather than rejected, and you then get the full series and no dataPoints key. Clamped to 400, the per-item ceiling this endpoint already enforces, and echoed back as the ceiling applied rather than as a per-item count. A soldCount in a thinned series cannot be summed: the days between the samples were dropped, not observed as zero. |
| priceKind | query | no | Which quantity the raw series carries, one of ask, sale or all. Defaults to ask, which is what every observation archived before 2026-09-03 is and what this endpoint has always returned.
An ask is the cheapest listing a market was offering that day. A sale is a transaction — what somebody actually paid — and depth differs wildly by market: sale rows exist where a market publishes its own trade history, and reach years further back than our own capture does. Every point carries its own priceKind, so a mixed response is always readable. Where a market publishes only a trailing aggregate rather than individual trades, a sale point is that aggregate as read at capture time — a rolling 24 hours, offset from the calendar day it is filed under by the hour the capture runs.
Raw mode only (points=true), and ignored otherwise — including in multi-window mode, exactly as dataPoints= is. The default shape of this endpoint is an aggregate, and an average over asks and transactions together is not a worse number but an unreadable one: the gap between the two is the market’s spread, not noise. observedDays, total and nextSince describe the kind you asked for, so a sale-mode cursor is a sale-mode cursor.
Note: an unrecognised value falls back to ask rather than to everything, which is the opposite of how fields and stats behave on this same endpoint. A typo there costs you some fields; here it would hand you two quantities that cannot be compared.One of: |
Request
curl "https://api.scmm.app/api/marketplace/<market>/prices/history?period=7d"Response
- market*
enum - period*
stringThe window as requested. In multi-window mode it reports the widest window you asked for, because that is the window scanned and therefore the one that decides which items appear; every window you asked for is listed under
windows. - windowDays*
number | nullDays the requested window spans.
nullforall. - observedDays*
numberDistinct UTC days this market actually has data for inside the window. This is the honest ceiling on every average in the response and is frequently lower than windowDays.
- points*
booleantruewhen this response carries raw points rather than aggregates. - page*
number1-based page index. Always 1 in aggregate mode, which is not paginated.
- pageSize*
numberHow many items this response carries, and it means two different things in the two modes. In raw mode (
points=true) it is the page size you asked for, clamped to 500, and an empty page still reports one. In aggregate and multi-window mode the endpoint is not paginated — one call returns the whole roster — sopage/pageSizeare echoed to keep one envelope shape andpageSizeis the number of items in the response, which istotalunlesssince=narrowed it. PassingpageSize=2in aggregate mode therefore comes back as the item count, not as 2. - total*
numberItems this market has history for in the window, before paging.
array<object>The windows you requested and their coverage. Present only when you passed
windows=, and its presence is also how you know whichitemsshape you got, the same waypointsreports the raw or aggregate split.- stats
array<string>Which statistics the per-window blocks carry, echoed back so an absent field is never ambiguous between “you opted out” and “the server does not serve it”. Multi-window mode only.
- since
stringThe delta cursor this response was filtered by, echoed back. Present only when you passed
since=, so a genuinely empty delta is never confused with a cursor the server ignored. - fields
array<string>Which field groups the flat aggregate row carries, echoed back. Present only when you passed
fields=, so an absent key means you got all of them. Applies to the flat row only — the per-window blocks are narrowed bystats=. - dataPoints
numberThe point ceiling each item’s raw series was thinned to, echoed back. It is present only when
dataPoints=was sent withpoints=true, and its presence is the only thing that distinguishes a sampled series from a market with sparse coverage.soldCountin a thinned series cannot be summed: the days between the samples were dropped, not observed as zero. The value is the ceiling that was applied rather than a per-item count, so an item with fewer observations comes back whole, and it is the clamped figure: a request above 400 is answered at 400 and reports 400. - maxPointsPerItem
numberThe most observations any one item can return on this page. Present on every raw response (
points=true) and on no other shape. A raw page reads at most 12,000 rows and shares them equally between the items on it, so this is12000 / pageSize, never above the endpoint’s own 400-point per-item limit:pageSize=100allows 120 observations per item andpageSize=500allows 24. LowerpageSizeto buy more points per item. An item holding more than the ceiling is sampled evenly across the window with the oldest and newest always kept — the same ruledataPointsapplies, and with the same consequence, thatsoldCountin a sampled series cannot be summed. An item holding fewer comes back whole. It is a ceiling on the number of points, never on the window:period,windowDaysandobservedDaysalways describe a window the series actually spans. - nextSince*
string | nullThe cursor to send as
since=on your next poll. Use it rather than your own clock: rows are stamped with the UTC day they describe and written shortly after that day begins, so a wall-clock cursor can sit after the timestamp of rows that have not been written yet, and those rows would then never appear in any delta. Present on every response, including an empty one.nullonly when this market has no observations in the window. - items*
array<any>Aggregates by default; one block per requested window when
windows=is passed; raw series whenpoints=true.
A market's own published sale statistics
/api/marketplace/{market}/sales-summaryA marketplace's own published sale statistics, read through from the vendor and attributed to them: minimum, maximum, mean and median, and the sale volume behind each, across their trailing 24h, 7d, 30d and 90d windows. Prices are USD hundredths (divide by 100), like every other price on this API.
The response defaults to the 30d and 90d windows and to the average alone; pass windows= and stats= for the rest. volume always ships and cannot be excluded. These are trailing windows rather than daily buckets, so they cannot be summed, differenced, or divided into a per-day rate. They are read live from the vendor behind a cache and are not captured into our history: stale: true means the vendor call failed and this is the last good payload, and fetchedAt says when it was read.
Warning: these are sale prices — what items actually sold for — and not listing prices. They are a different quantity from avgListingPrice on the price-history endpoint, which averages the cheapest ask observed each day. Do not compare or merge the two.
Warning: a null here means the vendor sold none of this item in that window. It is an observation, always paired with volume: 0, and it is the opposite of soldCount on the price-history endpoint, whose null means the market publishes no sold count at all.
Warning: read volume beside every average — an average over two sales looks identical to one over two hundred.
Parameters
| Name | In | Required | Description |
|---|---|---|---|
| market | path | yes | Market name. A market that is not published, or one that publishes no sale statistics, returns 404 — the same answer an unknown market gets. |
| windows | query | no | Comma-separated subset of 24h,7d,30d,90d. Defaults to 30d,90d. Unrecognised entries are ignored, and an entirely unrecognised list falls back to the default rather than erroring. |
| stats | query | no | Comma-separated subset of avg,min,max,median. Defaults to avg alone, which is the shape this endpoint is sized for; asking for all four roughly doubles the payload. volume is always present and cannot be excluded. |
Request
curl "https://api.scmm.app/api/marketplace/<market>/sales-summary?windows=<windows>"Response
- market*
enum - source*
stringWhose figure this is. Attribution is required when re-publishing it.
- sourceUrl*
stringThe vendor's own site, for attribution in a UI.
- appId*
stringSteam app id this summary covers — the vendor is queried per app.
- currency*
stringCurrency the vendor reported in. Prices are USD hundredths regardless.
- fetchedAt*
stringWhen we last successfully called the vendor.
- expiresAt*
stringWhen this payload goes stale and the next request refreshes it.
- stale*
booleantruewhen the vendor call failed and this is the last good payload rather than a fresh one. CheckfetchedAtto see how old it is. - windows*
array<string>Which windows each item carries, in the order requested.
- stats*
array<string>Which statistics each window block carries, echoed back so an absent field is never ambiguous. An opt-IN — the default is
avgalone.volumeis always present and cannot be excluded: 23.5% of 30-day averages rest on two sales or fewer, so an average without its count is a confident wrong number. - total*
number array<object>
Crafting cost analysis (containers + resources)
/api/marketplace/craftingFor each craftable container: the cheapest craft-from-resources total vs. the cheapest direct buy, plus the resource breakdown. For each base resource: the cheapest "break a larger item" cost vs. the cheapest direct buy.
Parameters
| Name | In | Required | Description |
|---|---|---|---|
| currency | query | no | 3-letter currency code. Prices convert to it. Defaults to USD. |
Request
curl "https://api.scmm.app/api/marketplace/crafting?currency=USD"Response
array<object>array<object>
List current marketplace deals
/api/marketplace/dealsItems with the largest absolute or percentage discount versus their store/market reference price.
Parameters
| Name | In | Required | Description |
|---|---|---|---|
| start | query | no | Zero-based offset into the ranked results (infinite scroll). Defaults to 0. |
| count | query | no | Page size. Defaults to 50, max 100. |
| limit | query | no | Deprecated alias for count. Defaults to 50. |
| mode | query | no | flips ranks profitable buy→Steam flips by profit %. Otherwise cheapest offers (default). |
| sort | query | no | Cheapest mode: discount (default), price (market offer price) or steam (Steam Community Market price). Flips mode: profit (default) or price. |
| dir | query | no | Sort direction: asc or desc (default). |
| filter | query | no | Case-insensitive substring match on item name. |
| market | query | no | Only deals whose cheapest offer is on this MarketType. |
| minPrice | query | no | Minimum market price, in dollars. |
| maxPrice | query | no | Maximum market price, in dollars. |
| minDiscount | query | no | Cheapest mode: minimum discount % vs. the Steam Community Market price. Flips mode: minimum flip-profit %. |
| sellNow | query | no | Flips mode only. true (default) prices the sell leg at Steam's highest buy order (sell right now). false uses the lowest sell order (list it and wait). |
| currency | query | no | 3-letter currency code. Prices convert to it; minPrice/maxPrice are interpreted in it. |
Request
curl "https://api.scmm.app/api/marketplace/deals?start=1"Response
array<object>- total*
numberTotal deals matching the filters across all pages
- start*
numberZero-based offset of this page into the ranked set
- count*
numberPage size requested (items.length ≤ this)
List marketplace items
/api/marketplace/itemsPaginated list of items tracked across markets.
Parameters
| Name | In | Required | Description |
|---|---|---|---|
| page | query | no | 1-based page index. Defaults to 1. |
| pageSize | query | no | Items per page. Defaults to 24, max 500. Values above the cap are clamped, never rejected. |
| currency | query | no | 3-letter currency code. Defaults to USD. |
Request
curl "https://api.scmm.app/api/marketplace/items?page=1"Response
array<object>- total*
number - page*
number - pageSize*
number
Get a single marketplace item with cross-market prices
/api/marketplace/items/{id}One item with every market’s current offer for it, cheapest first. Accepts the item guid or its nameHash. It is the market-side view of GET /api/item/{id}: fewer catalogue fields, and the per-market offers are the point rather than an add-on.
Each offer carries its own price, fee and supply, and isAvailable says whether it can be bought now — an offer the market holds no stock of stays in the list with supply: 0.
Parameters
| Name | In | Required | Description |
|---|---|---|---|
| id | path | yes | Item guid or name-hash. |
| currency | query | no | 3-letter currency code. Defaults to USD. |
Request
curl "https://api.scmm.app/api/marketplace/items/<id>?currency=USD"Response
- marketPrices*
map<string, number> | null array<object>- guid*
string - nameHash*
string | null - appId*
string - itemId*
number - workshopId*
number - type*
string | null - name*
string - description*
string | null - collection*
string | null - imageUrl*
string | null - isTradable*
boolean - isMarketable*
boolean - marketItemGuid*
string | null - marketItemId*
string | null - marketBuyOrderHighestPrice*
number | null - marketSellOrderLowestPrice*
number | null - marketLast168hrSales*
number | null - releasePrice*
number | null - releasedOn*
string | null
Per-market presentation metadata
/api/marketplace/marketsOne row per market carrying the presentation flags a client needs to render it consistently: call-to-action emphasis, whether the market supports bulk buying, and whether it is verified.
Request
curl "https://api.scmm.app/api/marketplace/markets"Response
- marketType*
enum - hidden*
booleanThe SCMM web UI does not render this market anywhere, while the API keeps publishing its data. Published as a field rather than an omission so a consumer can tell "hidden" from "absent".
- featuredCta*
boolean - bulkBuy*
boolean - verified*
boolean - pricesIngested*
booleanwe run a price job for this market and it is not in
DISABLED_MARKETS.
Price history across every market, aggregated
/api/marketplace/prices/historyPrice history for every item on every market we track, in one request — the cross-market sibling of GET /api/marketplace/{market}/prices/history, which fixes one market and returns every item.
Each item is returned once, carrying a markets map keyed by market name. A block inside that map is exactly the row the per-market endpoint returns, without the repeated item identity, so a query can be moved between the two endpoints unchanged: period, windows, stats, fields and since all keep their vocabulary, their defaults and their meaning. Use markets= to narrow the roster, and read each block’s own avgDays — coverage is a property of the market, not of the request, and it differs widely between them.
This endpoint is not paginated: one call carries the whole catalogue, and the response is the same size at every window because an aggregate is one block per item per market regardless of how many days it rests on. Narrow it with markets= and fields= rather than by paging, and poll it with since=.
Every window here is bounded by when we began archiving each market, which each response reports per market. For Steam’s own sale volume there is a far deeper series: GET /api/item/{id}/sales/market?maxDays=0 returns one point per day for a single item, back to the first sale Steam recorded for it. That is a sale volume rather than a listing price, so read it beside these figures rather than in place of them.
Warning: there is no raw mode here. A raw series is items multiplied by days multiplied by markets, which is a download rather than a response. For the daily observations behind these aggregates use GET /api/item/{id}/prices/history, which returns every market for one item, or GET /api/marketplace/{market}/prices/history?points=true, which returns one market for every item, paginated.
Warning: a market that has no observation for an item is absent from that item’s markets map, rather than present and empty. The roster that answered is on the response as markets, each entry with its own day count, so an absent key means this market published nothing for this item and never means the market was not searched.
Parameters
| Name | In | Required | Description |
|---|---|---|---|
| period | query | no | Time window. Defaults to 30d. An unrecognised value falls back rather than erroring. Read it beside each block’s avgDays: the window you ask for is a request, and the day count you get back is what the average actually rests on.One of: |
| windows | query | no | Comma-separated list of windows to aggregate in one response, e.g. 30d,60d,90d. Each market block then carries a windows map keyed by the window label instead of flat statistics, and every block carries its own avgDays per window. Read those before comparing windows: two windows backed by the same observed days are the same number twice, and the day count is the only thing on the wire that says so. Accepts the same vocabulary as period, and a period= you pass as well is included rather than dropped. Unrecognised entries are ignored. |
| stats | query | no | Comma-separated subset of avg,min,max,sold,bid limiting what each window block carries. An opt-out: the default is all of them, and an unrecognised list falls back to all of them rather than to none. avgDays is always present and cannot be excluded. Multi-window mode only — it narrows the per-window blocks, where fields= narrows the flat ones, and each is ignored in the other one’s mode. |
| since | query | no | ISO 8601 timestamp. Returns only the items observed strictly after it — pass the nextSince from your previous response rather than your own clock. An item that moved on any market in the roster comes back with all of its markets, because a delta selects items rather than cells. The cursor is the newest observation across the roster you asked for, so take it from a request with the same markets= you intend to keep polling: a cursor from a wider roster can skip a slower market’s day. An unparseable value is a 400 rather than a silent full response. |
| fields | query | no | Comma-separated subset of avg,min,max,last,sold,bid,liquidity limiting what each market block carries. This endpoint defaults to avg alone, unlike its per-market sibling, because a default here is multiplied by every market in the roster; the value applied is echoed back on every response, so what you got is never something you have to infer. An unrecognised list falls back to the full set of default groups rather than to none. liquidity scores the item as a whole rather than any single market block, because it reads Steam’s order book and 30-day sales volume — one measurement per item, whichever markets carry it. avgDays can never be excluded, because it is the denominator that makes every other number in the block readable. last and sold are groups rather than single fields: a supply without the observation it was measured at is not interpretable, and a sold total without its window cannot be read at all. An excluded field is absent from the block, while a field that is present and null means no such observation exists. |
| markets | query | no | Comma-separated list of markets to include, e.g. Skinport,DMarket. Defaults to every market we publish. Unrecognised entries are ignored rather than rejected, but a list of nothing but unrecognised entries narrows to nothing and returns an empty roster — this parameter is a filter, so refusing every named market is a real answer rather than a reason to hand back the largest response on the API. Narrowing it is the cheapest way to shrink this response: the payload is roughly proportional to how many markets carry each item. |
Request
curl "https://api.scmm.app/api/marketplace/prices/history?period=7d"Response
- period*
stringThe window as requested. In multi-window mode it reports the widest window you asked for, because that is the window scanned and therefore the one that decides which items appear; every window you asked for is listed under
windows. - windowDays*
number | nullDays the requested window spans.
nullforall. - observedDays*
numberDistinct UTC days any market in the roster has data for inside the window. This is the ceiling across the whole roster and therefore the wrong number to attribute to any single market: read the per-market figure under
marketsfor that. array<object>The markets this response carries, each with its own coverage, in a fixed order. It is the roster that survived
markets=, and it is how you tell “this market published nothing for this item” — the market is listed here, the item’s key is not — from “I did not ask for this market”, where it is absent from both.- total*
numberDistinct items with history in the window across the roster, before any
since=narrowing. array<object>The windows you requested and their coverage. Present only when you passed
windows=, and its presence is how you know each market block carries awindowsmap rather than flat statistics. Its observedDays is again the ceiling across the roster, not any single market’s.- stats
array<string>Which statistics the per-window blocks carry, echoed back so an absent field is never ambiguous between “you opted out” and “the server does not serve it”. Multi-window mode only.
- since
stringThe delta cursor this response was filtered by, echoed back. Present only when you passed
since=, so a genuinely empty delta is never confused with a cursor the server ignored. - fields
array<string>Which field groups each market block carries, echoed back on every aggregate response — including one narrowed to an empty roster. It is unconditional here, unlike on the per-market route, because this endpoint defaults to
avgalone: the default is itself a narrowing, so leaving it to be inferred would be the one thing a consumer cannot afford to get wrong. Absent only in multi-window mode, wherestats=is the narrowing parameter and is echoed in its place. - nextSince*
string | nullThe cursor to send as
since=on your next poll. Use it rather than your own clock: rows are stamped with the UTC day they describe and written shortly after that day begins, so a wall-clock cursor can sit after the timestamp of rows that have not been written yet, and those rows would then never appear in any delta. It is the newest observation across the roster you asked for, so take it from a request with the samemarkets=you intend to keep polling — a cursor taken from a wider roster can skip a slower market’s day. Present on every response, including an empty one.nullonly when no market in the roster has an observation in the window. array<object>One entry per item, each carrying every requested market that has data for it.