gpu market history
Show a GPU's price and availability over time
Show a GPU's price and availability over time
Synopsis
Prints the price and stock curve for one GPU model, optionally narrowed to a region and a capacity class. Leave both off and the series merges every region and both classes — the aggregator view, and usually the one you want.
Requires an API key with the market:read scope. The *current* price is public; the *curve* is not, so this endpoint is authenticated and rate-limited per key.
Reading the output
h100_sxm · us-west · secure · every node size
15-minute buckets · 2026-09-01T00:00:00Z → 2026-09-01T01:00:00Z · 4 buckets · 1 sold out · 1 with no data collected
BUCKET PRICE/GPU-HR AVAILABLE
2026-09-01T00:00:00Z $3.85 (from an 8-GPU node) 24
2026-09-01T00:15:00Z $4.20 3
2026-09-01T00:30:00Z $4.20 0 (sold out)
2026-09-01T00:45:00Z no data no data
0 (sold out) and no data are not the same thing, and this is the most important thing to understand about the series.
| Cell | JSON | Meaning |
|---|---|---|
0 (sold out) | "available_count": 0 | Sold out — 0 available. We sampled that bucket and nothing was on the market. A real, observed stockout you can act on. |
no data | "available_count": null | No data collected for this window. We did not sample that bucket — a failed poll, a skipped cycle, an incident on our side. It is not a stockout. |
A missing bucket is a statement about *our sampling*, never about *supply*. Do not treat a run of no data as a shortage, and do not draw a line across it: the points either side are not known to be connected. The header line tells you how many buckets in the window are gaps, so you can judge the coverage of a series before you trust its shape.
A price with no available capacity is normal and not a contradiction: a listing can carry a price while nothing behind it is obtainable.
Derived per-GPU rates
PRICE/GPU-HR is always a per-GPU hourly rate. When that rate was divided out of a whole-node price, it is labelled:
$3.85 (from an 8-GPU node)
The qualifier is there because a bare per-GPU figure implies you can rent one GPU at that price, which for whole-node capacity is not true — you would be renting the node. A rate with no parenthetical came from single-GPU capacity and needs no qualification.
Window limits
The window caps below are enforced by the API. The CLI checks them first, so an over-wide window fails immediately instead of costing you a request.
--bucket | Scope | Maximum window |
|---|---|---|
15m | --region and --capacity-class set | 90 days |
15m | either one left off | 31 days |
1d | any | 365 days (the full retention window) |
The 31-day limit exists because an unscoped 15-minute series has to scan every region and class in every bucket. It costs you nothing you cannot get another way — there are two cheap ways past it, and the error names both:
- scope the series with
--regionand--capacity-class, which buys the full 90 days at the same resolution; or - ask for
--bucket 1d, which re-aggregates the same data server-side and reaches back a year.
--from/--to you get the last 7 days. History is retained for 365 days; nothing older than that is stored.
gpu market history [flags]
Examples
# The last week of H100 SXM, every region, both classes and node sizes
gpu market history --gpu-model h100_sxm
# A specific month, scoped — scoping is what buys you the 90-day raw window
gpu market history --gpu-model h100_sxm --region us-west --capacity-class secure \
--from 2026-08-01 --to 2026-09-01
# A year at daily resolution, machine-readable
gpu market history --gpu-model h100_sxm --bucket 1d --from 2025-09-01 -o json
# Count the buckets we actually sampled (null = not sampled, so filter it out
# rather than treating it as a zero)
gpu market history --gpu-model h100_sxm -o json |
jq '[.points[] | select(.available_count != null)] | length'
# The cheapest per-GPU rate we ever recorded in the window
gpu market history --gpu-model h100_sxm -o json |
jq '[.points[].price_per_gpu_hour | select(. != null)] | min'
GPU model
--gpu-model takes a model id from gpu gpu-types list — h100_sxm, rtx_4090, a100_80gb. Punctuation and case don't matter, so RTX-4090 and rtx 4090 both resolve to rtx_4090, and the JSON echoes the canonical id back so you can see which series you were served.
An id we don't list is refused, with the nearest models named:
$ gpu market history --gpu-model h100_80gb
Error: gpu_model "h100_80gb" is not a GPU model we list, so it has no market
history; did you mean h100_nvl, h100_pcie, h100_sxm? See GET /v1/gpu-types for
the models we carry (validation_failed)
This is the same rule as node size below, for the same reason. h100_80gb is a
reasonable thing to type — it's how the part is marketed — but we split that
generation by interconnect because the three SKUs are differently priced. Before
this was refused it returned a full series of nulls, which is exactly what a
real model we hadn't sampled looks like: a typo was indistinguishable from an
outage on our side, and it read as a broken chart rather than a wrong id.
A model we do list but haven't sampled in your window is still a 200 with a
null-filled series. That is the honest answer and the distinction this surface is
built on — see the note on null in the examples above.
Node size
A 1-GPU box and an 8-GPU node are different products at different per-GPU rates, so the series keeps them apart. --cluster-size 8 charts 8-GPU nodes only; omitting it merges every shape, which is the default and what "what does this GPU cost" means without further qualification.
# Only 8-GPU nodes
gpu market history --gpu-model h100_sxm --cluster-size 8
Accepted sizes are 1, 2, 4, 8, 10, 16 — the shapes the catalog is offered in. Anything else is refused rather than answered with an empty series, because an empty series is indistinguishable from a genuine gap and this surface exists so "no data" and "nothing to have data about" are never the same answer.
available_cluster_sizes in the JSON reports the shapes that actually have rows for this model, region, class and window — narrower than the accepted list, and what a picker should offer. An empty array means we looked and found none.
There is no history from before the per-size split. Node size became a dimension of the table on 2026-09-10; buckets recorded earlier are the merged series and carry no per-size breakdown. They are served by the default (merged) view and are deliberately absent from a size-filtered one — inventing a breakdown that was never sampled would be a false claim about the past. So a --cluster-size query starts at that date, while the merged view is continuous across it.
Options
--bucket string Resolution: 15m (up to 90 days scoped, 31 days unscoped) or 1d (up to 365 days) (default "15m")
--capacity-class string Narrow the series to one capacity class: secure or community (omit to merge both)
--cluster-size int Narrow the series to one node shape, e.g. 8 for 8-GPU nodes (omit to merge every size; a 1-GPU box and an 8-GPU node are different products at different per-GPU rates)
--from string Start of the window, RFC3339 or YYYY-MM-DD (default: 7 days before --to)
--gpu-model string GPU model to chart, e.g. h100_sxm (required)
-h, --help help for history
--region string Canonical region to narrow the series to, e.g. us-west (omit to merge every region)
--to string End of the window, RFC3339 or YYYY-MM-DD (default: now)
Options inherited from parent commands
--api-base string API base URL (override with GPUAI_API_BASE env) (default "https://api.gpu.ai/v1")
--debug Enable debug logging to stderr
-o, --output string Output format: table|json (default table on TTY, json otherwise)
SEE ALSO
- gpu market - Look at how a GPU's price and availability have moved over time
- gpu watch create - Get alerted when the market moves, instead of going to look