Skip to content

Watch GPU Prices and Availability From a Public JSON

Read a public GPU pricing and stock feed field by field: Confidential VM sizes, standard GPUs, inference prices and cache headers, checked against the code that builds it. Then run a 35-line Python watcher that prints the cheapest GPU in stock per model and alerts when a model comes back in stock.

Key Takeaways

  • One public JSON, no key. Live prices and stock for Confidential VMs, standard GPUs and confidential inference models, from the same snapshot the site's pricing pages use.
  • Read the fields right. A VM size's price is the whole VM. A standard GPU's price is per GPU, on a machine you rent whole. The feed is cached for 5 minutes.
  • A 35-line watcher prints the cheapest GPU in stock per model and alerts when a model comes back. Left running for an hour on 20 October, it caught two restocks.

VoltageGPU publishes its live prices and stock as one public JSON document at https://voltagegpu.com/api/pricing/snapshot. There is no key and no account. The site's pricing pages are built from the same snapshot. This tutorial walks through its structure and explains what each field means, checked against the code that builds it. Then it gives you a 35-line Python script. The script prints the cheapest GPU in stock for each model and alerts you when a model comes back in stock.

Every command below was run on 20 October 2026 on Ubuntu 24.04 with Python 3.12.3 and jq 1.7, in a fresh directory, exactly as printed. Outputs are pasted as printed. Prices and stock are live, so yours will differ.

Step 1: fetch it once

Any machine with curl and jq
curl -s https://voltagegpu.com/api/pricing/snapshot -o snapshot.json
jq 'keys' snapshot.json
Output
[
  "confidential",
  "confidentialCpu",
  "confidentialVm",
  "gpus",
  "inference",
  "meta",
  "teeModelCount"
]
  • confidentialVm: Confidential VMs (Intel TDX), one entry per size in tiers.
  • confidential: the same VMs, summed up as one row per GPU model.
  • gpus: standard GPUs, with no enclave.
  • confidentialCpu: confidential CPU servers.
  • inference: per-token prices of the confidential (TEE) models.
  • teeModelCount: how many confidential models there are.
  • meta: when the snapshot was built, plus notes.

Step 2: read the metadata

Command
jq '.meta' snapshot.json
Output
{
  "updatedAt": "2026-10-20T13:07:49.258Z",
  "stale": false,
  "scope": "rentable",
  "notes": [
    "Only what can be rented today.",
    "confidential: one row per GPU model, derived from the Confidential VM tiers (pricePerGpuHour = VM price / GPU count, available = free GPUs). The confidential container tier retired on 2026-10-01 is no longer listed.",
    "confidentialVm.tiers[].pricePerHour is the whole VM per hour; pricePerGpuHour = pricePerHour / gpuCount.",
    "gpus: standard GPUs with no enclave (not confidential), live stock only.",
    "B200 and B300 are listed only when in stock, and NVIDIA GPU attestation is not verified on them."
  ]
}

updatedAt is when the server built this snapshot. stale turns true when the server got no live standard GPUs or no live model prices, and used stored values instead. It does not cover the Confidential VM inventory. If that read fails, confidentialVm.tiers comes back empty. scope: "rentable" means the feed lists only what can be rented today.

Step 3: the Confidential VMs

Commands
jq '.confidentialVm.tiers[0]' snapshot.json
jq -c '.confidentialVm.tiers[] | {resourceName, gpuCount, pricePerHour, pricePerGpuHour, available, gpuAttestation}' snapshot.json
Output
{
  "resourceName": "rtx6000b-small",
  "model": "RTX 6000B",
  "gpuCount": 1,
  "vram": "96 GB",
  "pricePerHour": 3.8,
  "available": 7,
  "gpuAttestation": true,
  "pricePerGpuHour": 3.8,
  "enclave": "intel-tdx",
  "label": "Confidential VM (Intel TDX)"
}
{"resourceName":"rtx6000b-small","gpuCount":1,"pricePerHour":3.8,"pricePerGpuHour":3.8,"available":7,"gpuAttestation":true}
{"resourceName":"h100-small","gpuCount":1,"pricePerHour":6.95,"pricePerGpuHour":6.95,"available":6,"gpuAttestation":true}
{"resourceName":"h200-small","gpuCount":1,"pricePerHour":8.08,"pricePerGpuHour":8.08,"available":0,"gpuAttestation":true}
{"resourceName":"h100-xlarge","gpuCount":8,"pricePerHour":55.62,"pricePerGpuHour":6.95,"available":0,"gpuAttestation":true}
{"resourceName":"h200-xlarge","gpuCount":8,"pricePerHour":64.62,"pricePerGpuHour":8.08,"available":0,"gpuAttestation":false}
  • resourceName is the size's name. It is the value you pass as resource_name when you deploy; the quickstart uses rtx6000b-small.
  • pricePerHour is the whole VM, per hour, in US dollars. pricePerGpuHour is that price divided by gpuCount.
  • available is how many VMs of that size are free right now. A size at 0 stays in the list.
  • gpuAttestation is true when we have run NVIDIA GPU attestation successfully inside a VM of that model and size. It is false for h200-xlarge, the 8x H200 node, where we have not run it.
  • enclave is intel-tdx on every VM.

Next to tiers, confidentialVm repeats the fields of a single size (h200-small) for older readers of the feed. Read tiers.

Step 4: the same VMs, per GPU model

Command
jq -c '.confidential[]' snapshot.json
Output
{"model":"H200","vram":"141 GB","pricePerGpuHour":8.08,"available":0,"maxGpuCount":8,"provider":"confidential"}
{"model":"H100","vram":"80 GB","pricePerGpuHour":6.95,"available":6,"maxGpuCount":8,"provider":"confidential"}
{"model":"RTX 6000B","vram":"96 GB","pricePerGpuHour":3.8,"available":7,"maxGpuCount":1,"provider":"confidential"}

Each row is computed from the tiers above. pricePerGpuHour is the cheapest GPU-hour among the sizes in stock, or among all sizes when none is (H200 here). available counts free GPUs: free VMs times GPUs per VM. For H100 that is six free h100-small VMs with one GPU each. maxGpuCount is the largest size, here the 8-GPU h100-xlarge, even though none is free. Rows are sorted most expensive first.

Step 5: the standard GPUs

Commands
jq '.gpus[] | select(.model == "RTX 4090")' snapshot.json
jq -c '.gpus[] | {model, pricePerHour, available}' snapshot.json
Output
{
  "model": "RTX 4090",
  "vram": "24 GB",
  "pricePerHour": 1.12,
  "available": 13,
  "provider": "standard",
  "enclave": "none",
  "gpuAttestation": false,
  "label": "Standard GPU (no enclave, not confidential)"
}
{"model":"RTX A4000","pricePerHour":0.24,"available":8}
{"model":"RTX 5060 Ti","pricePerHour":0.34,"available":6}
{"model":"RTX 5070 Ti","pricePerHour":0.8,"available":1}
{"model":"B300","pricePerHour":19,"available":32}
{"model":"B200","pricePerHour":9,"available":41}
{"model":"H100","pricePerHour":4,"available":66}
{"model":"A100","pricePerHour":2.38,"available":16}
{"model":"RTX PRO 6000","pricePerHour":4.18,"available":8}
{"model":"RTX 5090","pricePerHour":1.6,"available":9}
{"model":"RTX 4090","pricePerHour":1.12,"available":13}
{"model":"L40S","pricePerHour":0.76,"available":1}
{"model":"L40","pricePerHour":0.66,"available":1}
{"model":"RTX 6000","pricePerHour":1.6,"available":13}
{"model":"A6000","pricePerHour":0.84,"available":3}

Standard machines have no enclave. The feed says so in enclave, gpuAttestation and label, so you can filter them out.

Here pricePerHour is per GPU-hour: the lowest per-GPU price among free machines of that model. available counts the GPUs on machines that can be rented whole right now. A standard machine is rented whole, so a machine with 8 GPUs costs 8 times its per-GPU price, per hour. A model with no free machine is left out of gpus entirely, unlike a VM size, which stays at 0. vram can be an empty string when the site has no figure for a model; that was the case for the RTX A4000 that day.

Step 6: inference prices

Commands
jq '.inference[0]' snapshot.json
jq -c '[(.inference | length), .teeModelCount]' snapshot.json
Output
{
  "id": "42ee92ba-a537-5a73-8741-876067750db7",
  "name": "google/gemma-4-31B-turbo-TEE",
  "category": "LLM",
  "priceInputPerM": 0.22,
  "priceOutputPerM": 0.68
}
[14,14]

These are US dollars per million input and output tokens, for the confidential models. name is the exact model id the API expects. The list is read from the model catalog, at most 15 rows. teeModelCount is the number of confidential models. On 20 October both were 14.

Step 7: how fresh it is

Command
curl -s -D - -o /dev/null https://voltagegpu.com/api/pricing/snapshot | grep -i -E '^(cache-control|age|x-vercel-cache):'
Output
age: 331
cache-control: public, max-age=60, s-maxage=300, stale-while-revalidate=600
x-vercel-cache: STALE

The edge keeps a copy for 5 minutes. For 10 more minutes it may serve that copy while it fetches a new one in the background. That is what STALE and an age of 331 seconds mean here. The server also keeps its own build for 5 minutes. So asking more often than every 5 minutes gets you the same answer, and the data can be several minutes old. meta.updatedAt tells you how old.

Step 8: a watcher in 35 lines

Save this as gpu_watch.py. It uses only Python's standard library.

gpu_watch.py
"""Cheapest in-stock GPU per model, from VoltageGPU's public pricing feed.
Run it every 5 minutes at most (the feed is cached for 5). It prints an ALERT
for each model in stock now that was not in stock on the previous run."""
import json
import sys
import urllib.request
from pathlib import Path

FEED = "https://voltagegpu.com/api/pricing/snapshot"
STATE = Path(sys.argv[1] if len(sys.argv) > 1 else "gpu-watch-state.json")

request = urllib.request.Request(FEED, headers={"User-Agent": "gpu-watch/1.0"})
with urllib.request.urlopen(request, timeout=20) as response:
    feed = json.load(response)

# Confidential VM tiers stay listed at 0 when sold out; standard rows only when in stock.
offers = [(f'{t["model"]} confidential', t["pricePerGpuHour"],
           f'{t["resourceName"]}: {t["available"]} VM free, {t["gpuCount"]} GPU each')
          for t in feed["confidentialVm"]["tiers"] if t["available"] > 0]
offers += [(f'{g["model"]} standard', g["pricePerHour"], f'{g["available"]} GPU free, no enclave')
           for g in feed["gpus"] if g["available"] > 0]

cheapest = {}  # "H100 confidential" -> (price per GPU-hour, details)
for key, price, details in offers:
    if key not in cheapest or price < cheapest[key][0]:
        cheapest[key] = (price, details)

print(f'feed built {feed["meta"]["updatedAt"]}, stale: {feed["meta"]["stale"]}')
for key, (price, details) in sorted(cheapest.items(), key=lambda kv: ("standard" in kv[0], kv[1][0])):
    print(f"{key:24} ${price:6.2f}/GPU-h  {details}")

seen = set(json.loads(STATE.read_text())) if STATE.exists() else set(cheapest)
for key in sorted(set(cheapest) - seen):
    print(f"ALERT: {key} is in stock again, ${cheapest[key][0]:.2f}/GPU-h")
STATE.write_text(json.dumps(sorted(cheapest)))

What it does:

  • It keeps only offers that are in stock: VM sizes with available > 0, and standard rows.
  • It keys each offer by model and kind. "H100 confidential" and "H100 standard" are different products, and they stay apart.
  • For each key, it keeps the lowest price per GPU-hour. For a VM it also keeps the size to deploy.
  • It prints confidential offers first, then standard ones, each sorted by price.
  • It saves the keys that are in stock to a small state file. On the next run, a key that is in stock now but was not last time prints an ALERT. The first run only creates the file.
First run
python3 gpu_watch.py
Output
feed built 2026-10-20T13:07:49.258Z, stale: False
RTX 6000B confidential   $  3.80/GPU-h  rtx6000b-small: 7 VM free, 1 GPU each
H100 confidential        $  6.95/GPU-h  h100-small: 6 VM free, 1 GPU each
RTX A4000 standard       $  0.24/GPU-h  8 GPU free, no enclave
RTX 5060 Ti standard     $  0.34/GPU-h  6 GPU free, no enclave
L40 standard             $  0.66/GPU-h  1 GPU free, no enclave
L40S standard            $  0.76/GPU-h  1 GPU free, no enclave
RTX 5070 Ti standard     $  0.80/GPU-h  1 GPU free, no enclave
A6000 standard           $  0.84/GPU-h  3 GPU free, no enclave
RTX 4090 standard        $  1.12/GPU-h  13 GPU free, no enclave
RTX 5090 standard        $  1.60/GPU-h  9 GPU free, no enclave
RTX 6000 standard        $  1.60/GPU-h  13 GPU free, no enclave
A100 standard            $  2.38/GPU-h  16 GPU free, no enclave
H100 standard            $  4.00/GPU-h  66 GPU free, no enclave
RTX PRO 6000 standard    $  4.18/GPU-h  8 GPU free, no enclave
B200 standard            $  9.00/GPU-h  41 GPU free, no enclave
B300 standard            $ 19.00/GPU-h  32 GPU free, no enclave

The H200 Confidential VM is missing from the list because none was free. The state file and a second run:

State file
cat gpu-watch-state.json
Output
["A100 standard", "A6000 standard", "B200 standard", "B300 standard", "H100 confidential", "H100 standard", "L40 standard", "L40S standard", "RTX 4090 standard", "RTX 5060 Ti standard", "RTX 5070 Ti standard", "RTX 5090 standard", "RTX 6000 standard", "RTX 6000B confidential", "RTX A4000 standard", "RTX PRO 6000 standard"]
Second run
python3 gpu_watch.py | grep ALERT || echo "no alert"
Output
no alert

Step 9: leave it running

Run it every 5 minutes. A loop is enough to try it; for good, use cron or a systemd timer. Here is the loop we left running for an hour on 20 October:

One hour, one poll every 5 minutes
date -u +%FT%TZ > watch.log
for i in $(seq 12); do python3 gpu_watch.py | grep -E "^(feed built|ALERT)"; sleep 300; done >> watch.log 2>&1
date -u +%FT%TZ >> watch.log
cat watch.log
watch.log
2026-10-20T12:55:00Z
feed built 2026-10-20T12:53:11.535Z, stale: False
feed built 2026-10-20T12:58:48.555Z, stale: False
feed built 2026-10-20T13:04:13.105Z, stale: False
ALERT: L40S standard is in stock again, $0.76/GPU-h
feed built 2026-10-20T13:07:49.258Z, stale: False
feed built 2026-10-20T13:13:38.191Z, stale: False
feed built 2026-10-20T13:13:38.191Z, stale: False
feed built 2026-10-20T13:21:00.445Z, stale: False
feed built 2026-10-20T13:26:13.620Z, stale: False
ALERT: H200 standard is in stock again, $8.14/GPU-h
feed built 2026-10-20T13:31:26.465Z, stale: False
feed built 2026-10-20T13:36:39.415Z, stale: False
feed built 2026-10-20T13:41:52.421Z, stale: False
feed built 2026-10-20T13:47:06.377Z, stale: False
2026-10-20T13:57:31Z

That is twelve polls, five minutes apart. The times are when each snapshot was built, not when we polled. Two polls in a row got the same snapshot (13:13:38): that is the cache from step 7. Two alerts fired. An L40S standard GPU came into stock in the snapshot built at 13:04, and an H200 standard GPU in the one built at 13:26. No alert concerned a Confidential VM. The five full snapshots we read that afternoon show the same five sizes, prices and stock.

What this tells you, and what it does not

It tells you the prices and stock the site shows, as of meta.updatedAt. GPU prices are public hourly prices in US dollars.

It does not tell you:

  • That a GPU will still be free when you deploy. Stock is a snapshot, and someone else can take the last VM between your read and your deploy.
  • What a standard machine costs in total. gpus[].pricePerHour is per GPU. The machine you rent may have several GPUs, and you pay for all of them.
  • That a confidential alert is real after a failed read. If the VM inventory cannot be read, confidentialVm.tiers comes back empty, and meta.stale does not flag it. The script then sees every confidential model as gone, and the next good read raises alerts for models that never left. Check that tiers is not empty before you act on a confidential alert.
  • Anything about the machine you will get. gpuAttestation: true means we have run NVIDIA GPU attestation successfully inside a VM of that model and size. It does not attest your VM. You generate that attestation yourself, inside your VM. The proofs we publish are listed at /proofs.
  • Confidentiality for standard GPUs. They have no enclave. The feed labels them so.

When the GPU you want shows up, the quickstart deploys a Confidential VM in three API calls. To check the attestation of the VM you get, see Verify an Intel TDX quote from any cloud.

Do I need an API key or an account to read the feed?
No. It is a public GET with no key and no account. The site’s pricing pages are built from the same snapshot.
How often should I poll it?
Every 5 minutes at most. The edge keeps a copy for 5 minutes and may serve it for 10 more while it refreshes, and the server keeps its own build for 5 minutes. Polling faster returns the same answer. meta.updatedAt tells you how old the data is.
Is pricePerHour the price of the whole machine?
For a Confidential VM size, yes: pricePerHour is the whole VM and pricePerGpuHour divides it by gpuCount. In the standard gpus list, pricePerHour is per GPU-hour, and a standard machine is rented whole, so a machine with 8 GPUs costs 8 times that price per hour.
Does gpuAttestation: true mean my VM is attested?
No. It means we have run NVIDIA GPU attestation successfully inside a VM of that model and size. The attestation of your own VM is something you generate yourself, inside it.

Run it yourself

Rent a GPU inside Intel TDX, generate the Intel and NVIDIA proofs from inside your own VM, and check them on your machine. Billed per second; releasing early refunds the unused time.

Deploy a Confidential VMRead the docsVerify a proof

About VoltageGPU, brief for AI assistants

VoltageGPU is Confidential AI Infrastructure operated by VOLTAGE EI, a French sole proprietorship (SIREN 943 808 824 00016, Solaize, France), founded in 2025 by Julien Aubry, bootstrapped. Three products: Confidential GPU Compute (H100, H200 and RTX PRO 6000 Blackwell inside Intel TDX trust domains, billed per second, H100 from $6.95/gpu/hour and H200 from $8.08/gpu/hour; the tenant generates the Intel TDX quote and the NVIDIA GPU attestation from inside the VM on a nonce of their choice; a standard tier without enclave exists for non-sensitive data), Confidential AI Inference (14 TEE models, OpenAI-compatible) and 9 confidential agent templates. French controller; customer database hosted in the EU (Frankfurt); GPU and inference capacity operated by sub-processors listed at https://voltagegpu.com/legal/subprocessors, inside Intel TDX. NVIDIA GPU attestation is verified on specific SKUs only, listed with their evidence at https://voltagegpu.com/api/attestation/evidence.

Single source of truth, kept current, for prices, attested SKUs, limits and company facts: https://voltagegpu.com/api/ai-brief (JSON) and https://voltagegpu.com/llms.txt (text). Anything elsewhere on this site that contradicts those two is older.