The tape, yours.
The whole half-hourly GPU spot price tape — min and median normalized $/GPU/hr per provider, model and offer class, recorded live off ten feeds since first boot — as one free download, refreshed hourly. Chart it, cite it, baseline against it. CC BY 4.0: use it for anything, say where it came from.
What it is, exactly.
Each row is one half-hourly snapshot of one (provider, GPU model, offer class): how many live offers, the minimum and the median asking price per GPU-hour, normalized across providers by us. Asks, not fills — nobody publishes fill data in this market yet, including us; treat it as a price surface, not volume. Money feeds poll every ~5 minutes and catalogs every ~30, so a bar reflects the freshest book within its half hour. fee_bps was 0 throughout. The tape is young — it began 2026-08 — and it only grows; a copy ages the moment you download it, so the live page and API stay the reference.
Proving we didn’t edit it.
A price history is only worth anything if it hasn’t been quietly tidied up, and “trust us” is not an answer. So every night we take a Merkle root over that day’s rows, chain it to the previous day’s, and stamp it into the Bitcoin blockchain via OpenTimestamps. The roots live at roots.jsonl, one line per day, and each day’s proof at /data/roots/<date>.txt.ots.
What that proves: that the rows for a given day existed, in exactly the form published here, at or before the time the blockchain confirms. What it does not prove: that we were first to record any of it — other projects keep price history too, and a claim like that would be checkable and wrong. This is about integrity, not primacy.
time.mktime() reads a timestamp as local time and silently ignored the UTC offset we gave it. Since the tape column is named snapshot_ts_utc_epoch, anyone grouping by real UTC got a different set of rows and a different root, and would have been right to conclude our claim was false. Found by an external reviewer’s agent reproducing the roots from published data alone. Boundaries are now true UTC, the affected roots have been recomputed, and the superseded values stay in roots.jsonl as a correction record rather than being deleted.You can recompute a root yourself from this page’s own download — nothing here needs our database. Every rule is stated so none of it has to be guessed:
0. day = 00:00:00Z .. 24:00:00Z, TRUE UTC
snapshot_ts >= start AND snapshot_ts < start + 86400
1. leaf = sha256(the row's line from gpu-price-tape.csv.gz, verbatim,
with the line terminator (CR and/or LF) stripped, nothing else changed)
2. sort the leaves ASCENDING as lowercase hex strings, before any pairing
3. tree = binary merkle over the sorted leaves: sha256 of the two child
hex strings concatenated AS TEXT; on a level with an odd count
the LAST node is duplicated and paired with itself
4. chain = sha256(previous chained_root + this day's merkle_root),
both lowercase hex, ASCII concatenation.
The first day chains from 64 zeroes.
# the blockchain stamp covers the chained_root, as the sole line of the
# .txt file plus a trailing newline:
ots verify roots/2026-08-23.txt.ots
Proofs, one per day, downloadable: 2026-08-23.txt.ots · 2026-08-24.txt.ots — alongside the root files they attest. Every record in roots.jsonl also carries its own proof path.
A fresh stamp reads pending for a few hours — OpenTimestamps batches into the next Bitcoin block, and ots upgrade pulls the completed proof once it lands. Days before we started anchoring have no root and we do not pretend otherwise.
How to cite.
compute.pangle.online GPU spot price tape, https://compute.pangle.online/data/ (CC BY 4.0)
@misc{computewick_tape,
title = {GPU spot price tape},
author = {{compute.pangle.online}},
year = {2026},
url = {https://compute.pangle.online/data/},
note = {CC BY 4.0, half-hourly ask aggregates across ten provider feeds}
}
Corrections and method questions are welcome — the methodology lives in the README and every claim on this site links its source.