Reproduce the Cloud GPU Price Index
Every published week of the Price Index ships with a receipt: the quotes that entered that week’s value, one row each, with the page it was read from and when. A receipt and one command give the published number back. No key, no account, no library beyond Python’s own. The running week is published here and nowhere else - it is recomputed every hour; a closed week is final, and its receipt is also deposited in the dataset archive, where the same command works without this site.
The formula, in three lines
- Quotes. NVIDIA H100 SXM, on-demand, listed in USD, not a marketplace floor. For each provider and region, the latest read of the week, counted only if that read is within 48 hours of the week’s last scrape. That list is the receipt.
- Level. The median of
usd_per_gpu_hourover the receipt, rounded half up to the cent. A week is published only when at least 8 providers are in it. - Change. The base-100 index multiplies the previous week’s value by the geometric mean of this week’s price over last week’s, across the (provider, region) pairs present in both receipts. The first week is 100.
Check it yourself: the week of October 19, 2026 (2026-W43, final)
The receipt is /price-index/receipts/2026-W43.csv. Run this in any terminal with Python 3:
curl -s https://gpueconomy.com/price-index/receipts/2026-W43.csv | python3 -c "import csv, sys, statistics; from decimal import Decimal as D, ROUND_HALF_UP; r = [x for x in csv.DictReader(sys.stdin) if x['included'] == 'true']; m = statistics.median(D(x['usd_per_gpu_hour']) for x in r); print(m.quantize(D('0.01'), ROUND_HALF_UP), len(r), 'quotes', len({x['provider_id'] for x in r}), 'providers')"It prints 3.39 38 quotes 36 providers. The published value for that week is $3.39 per GPU-hour, the median of 38 quotes from 36 providers (base-100 index 105.1) — the build of this page recomputed it from the same file and would have stopped on a closed week that did not match.
| Provider | Region | USD / GPU-hour | Scope | Read at |
|---|---|---|---|---|
| no-kristiansand-1 | $1.79 | GPU only | ||
| — | $2.36 | from | ||
| — | $2.42 | from | ||
| — | $2.45 | instance | ||
| — | $2.60 | from | ||
| Community Cloud | $2.69 | instance | ||
| — | $2.95 | instance | ||
| — | $2.95 | instance | ||
| — | $2.98 | instance | ||
| — | $2.99 | instance | ||
| Southeast Asia | $2.99 | instance | ||
| Secure Cloud | $2.99 | instance | ||
| — | $3.00 | instance | ||
| — | $3.14 | instance | ||
| — | $3.19 | from | ||
| — | $3.20 | instance | ||
| — | $3.20 | instance | ||
| — | $3.25 | instance | ||
| — | $3.29 | instance | ||
| — | $3.49 | instance | ||
| Elice Cloud | — | $3.57 | instance | |
| — | $3.59 | instance | ||
| — | $3.90 | instance | ||
| — | $3.95 | GPU only | ||
| — | $3.99 | from | ||
| — | $3.99 | instance | ||
| Secure Cloud | $3.99 | instance | ||
| — | $3.99 | instance | ||
| — | $4.39 | from | ||
| GPU Droplets | $4.41 | instance | ||
| — | $4.50 | instance | ||
| — | $4.56 | instance | ||
| Paperspace | $5.95 | instance | ||
| — | $6.16 | instance | ||
| us-east-1 | $6.88 | instance | ||
| us-west8 | $9.80 | GPU only | ||
| — | $10.00 | instance | ||
| eastus | $11.06 | instance |
Check a month: September 2026
The monthly average is the mean of every published hourly point of the month (specification 2b). From the file of every point:
curl -s https://gpueconomy.com/price-index-hourly.csv | python3 -c "import csv, sys; from decimal import Decimal as D, ROUND_HALF_UP; r = [x for x in csv.DictReader(sys.stdin) if x['scraped_at'].startswith('2026-09') and x['status'] == 'published']; print((sum(D(x['usd_per_gpu_hour']) for x in r) / len(r)).quantize(D('0.000001'), ROUND_HALF_UP), len(r), 'points')"It prints 3.883267 589 points, the published value over 589 of the month's 720 hours. A month with a fixed panel takes, at each hour, the median of the panel's pairs only - the pairs are in that month's panel CSV.
Check an hour: 2026-10-28 14:40 UTC
Every hourly point is written once with the quotes it was computed from, in the hourly receipt of its week:
curl -s https://gpueconomy.com/price-index/hourly/2026-W44.csv | python3 -c "import csv, sys, statistics; from decimal import Decimal as D; r = [x for x in csv.DictReader(sys.stdin) if x['scraped_at'] == '2026-10-28T14:40:04.23143+00:00']; print(statistics.median(D(x['usd_per_gpu_hour']) for x in r).normalize(), len(r), 'quotes')"It prints 3.39 38 quotes, the published point.
The base-100 link between two weeks
Given two consecutive receipts, this prints the week-over-week link (the geometric mean of the price ratios) and how many (provider, region) pairs it rests on. The index for a week is the previous week’s index times that link.
python3 -c "import csv, math, sys, urllib.request as u
def week(w): return {(x['provider_id'], x['region']): float(x['usd_per_gpu_hour']) for x in csv.DictReader(u.urlopen(u.Request('https://gpueconomy.com/price-index/receipts/' + w + '.csv', headers={'User-Agent': 'gpueconomy-reproduce/1.0'})).read().decode().splitlines()) if x['included'] == 'true'}
a, b = week(sys.argv[1]), week(sys.argv[2]); pairs = a.keys() & b.keys()
print(round(math.exp(sum(math.log(b[k] / a[k]) for k in pairs) / len(pairs)), 6), len(pairs), 'pairs')" 2026-W42 2026-W43The same check without this site
The receipts of closed weeks are deposited with each dated snapshot of the dataset, so a number cited from here can be checked years from now even if this domain is gone: Zenodo, concept DOI 10.5281/zenodo.22706541 (it always resolves to the latest snapshot; each snapshot also has its own version DOI), and the same files on Hugging Face. The running week is not in the archive - it is recomputed every hour and lives on this domain alone; a snapshot carries the weeks that had closed when it was taken.
The same arithmetic as above, on the deposited copy of 2026-W39 instead of ours:
curl -sL https://huggingface.co/datasets/gpueconomy/cloud-gpu-price-index/resolve/main/receipt-2026-W39.csv | python3 -c "import csv, sys, statistics; from decimal import Decimal as D, ROUND_HALF_UP; r = [x for x in csv.DictReader(sys.stdin) if x['included'] == 'true']; m = statistics.median(D(x['usd_per_gpu_hour']) for x in r); print(m.quantize(D('0.01'), ROUND_HALF_UP), len(r), 'quotes', len({x['provider_id'] for x in r}), 'providers')"The newest snapshot is dated 2026-10-02 and holds the receipts of 2026-W37, 2026-W38, 2026-W39. Closed since then and not yet deposited: 2026-W40, 2026-W41, 2026-W42, 2026-W43 - on this domain only until the next snapshot carries them. The snapshot's own README.md lists the receipts it holds.
Every receipt
| Week | ISO week | Vintage | Median | Quotes | Providers | Receipt |
|---|---|---|---|---|---|---|
| 2026-W44 | provisional | $3.39 | 38 | 36 | 2026-W44.csv | |
| 2026-W43 | final | $3.39 | 38 | 36 | 2026-W43.csv | |
| 2026-W42 | final | $3.53 | 36 | 34 | 2026-W42.csv | |
| 2026-W41 | final | $3.49 | 33 | 31 | 2026-W41.csv | |
| 2026-W40 | final | $3.49 | 31 | 29 | 2026-W40.csv | |
| 2026-W39 | final | $3.70 | 26 | 24 | 2026-W39.csv | |
| 2026-W38 | final | $3.90 | 21 | 19 | 2026-W38.csv | |
| 2026-W37 | final | $3.92 | 20 | 18 | 2026-W37.csv |
Columns: week_start, iso_week, vintage, gpu_id, provider_id, region, usd_per_gpu_hour, price_scope, source_url, scraped_at, included, included_rule, excluded_by, methodology_version. included_rule points at the panel rules. CC BY 4.0, like every file here.
What else a receipt lists: the quotes kept out
Every H100 SXM on-demand quote we read for the week is in its receipt. The ones that make the value carry included=true; the ones kept out carry included=false and, in excluded_by, the rule that kept them out: marketplace_floor (a Vast.ai floor moves with supply and is not a rate card), eur_listing (a converted figure would move with the exchange rate) or not_confirmed_48h (not read again within 48 hours of the week's last scrape). The same codes, with their wording, are exclusion_rules in price-index.json. A median over the whole file is therefore not the index — both commands above read the included=true rows only. Spot and reserved rates are not candidates at all, so they are not listed as exclusions: they answer a different question.
Data updated: