gpu1.ai tracks live rental prices for the GPUs people actually train and serve models on — across Vast.ai's marketplace and RunPod — normalized per GPU, per hour, so an 8-GPU rig and a single card compare fairly.
The four GPUs people ask about most, at the snapshot below. Marketplace prices move constantly — the deploy guide further down wires this exact page to refresh itself hourly.
Full price comparison
Every price is per GPU per hour. Vast.ai figures come from live marketplace offers (cheapest and median); RunPod shows its current lowest available rate. “Best” is whichever provider wins that row — and that's where the Rent button points.
GPU
VRAM
Vast.ai from
Vast.ai median
RunPod from
Best
Inside the marketplace
The actual cheapest Vast.ai machines behind the headline numbers, sorted by per-GPU price. Watch the trade-off the headline hides: the rock-bottom offer sometimes sits on a host with a lower reliability score — the table note gives each GPU's “reliable floor”, the cheapest offer from a host rated 98% or better.
$/GPU/hr
Total $/hr
GPUs
Location
Reliability
Host CPU
RAM
Disk
Down / Up Mbps
What will your run cost?
Pick a workload — or dial in your own GPU, count, and hours — and see the bill on each provider at snapshot prices. Then check the rent-vs-buy line before you reach for a credit card.
Your run
The bill
Best case = the cheapest single offer at snapshot; it may be gone by the time you click. Typical = median of live offers — the safer planning number. Storage, egress, and your own time aren't included, because they're never included in anyone's headline price either.
The providers, honestly
Two very different animals. gpu1.ai links to both through affiliate programs — disclosed in plain sight, because a comparison site you can't trust is worth nothing.
Vast.ai
A marketplace of independent GPU hosts bidding for your job.
Cheapest headline prices anywhere — hosts compete, so a 4090 dips to about $0.38/GPU/hr and an H100 to $1.34.
Quality varies by host. Filter by the reliability score; the cheapest offer isn't always the one that finishes your training run.
Best for: batch jobs, fine-tuning, experiments — anything checkpointed that can survive a host hiccup.
How gpu1.ai earns: Vast.ai pays 3% of everything you spend, for the life of your account. Your price doesn't change — the cut comes out of their side.
A managed GPU cloud — Secure Cloud datacenters plus a Community Cloud.
Predictable availability. A fixed rate card instead of a bidding war; an H100 SXM is $2.69/hr at snapshot.
Per-second billing on pods, one-click templates, persistent volumes.
Best for: inference servers and long-running pods where “is it still up?” matters more than the last 20 cents.
How gpu1.ai earns: new accounts through this link get a $5–$500 credit bonus, and gpu1.ai earns credits too. A 12% cash affiliate tier unlocks after 25 paying referrals.
On the roadmap: Lambda, CoreWeave, Vultr, and DigitalOcean rate cards join as curated rows next. Fixed-price clouds don't expose live-offer APIs the way a marketplace does, so their published rates get verified by hand before they enter the table.
How this site makes money
No ads-first nonsense, no selling data. One engine: affiliate revenue from people who were going to rent a GPU anyway and found a cheaper one here.
# Illustrative — assumptions stated, not a promise
100 active referred users × $1,000/mo GPU spend = $100,000/mo flowing through the links
# Referrals stack: every month's new users add to every previous month's base.
# Later layers: sponsored placements (month 6+), a pricing-data API (year 2).
Host & deploy it: the full runbook
This page is the entire site — one static HTML file. In production it reads a prices.json file that a GitHub Action refreshes hourly from the two provider APIs; every refresh commits, and every commit auto-deploys on Cloudflare Pages. Total running cost: $0. Here is the exact path from this preview to gpu1.ai live.
GitHub Actions cronHourly: fetch_prices.py pulls both APIs, commits prices.json
→
Cloudflare PagesAuto-deploys every push to main — no build step
→
gpu1.aiCustom domain on Pages, HTTPS by default
Create the repo and add the files
Create a public repo named gpu1.ai under your GitHub account (same flow as mtls.ai — and make sure the “Meta Muse AI” GitHub App has this repo selected, the way you fixed it last time). The repo holds exactly four things:
index.html — this page. In the production copy the prices load from prices.json (step 3); everything else is identical.
scripts/fetch_prices.py — the fetcher in step 2.
.github/workflows/prices.yml — the hourly cron in step 2.
prices.json — generated by the script; you commit the first one by hand.
terminal
gh repo create gpu1.ai --public --clone
cd gpu1.ai
mkdir -p scripts .github/workflows
# drop index.html in the root, and the two files from step 2 in place
python3 scripts/fetch_prices.py # writes the first prices.json
git add -A && git commit -m "gpu1.ai v1: comparison table + price fetcher"
git push
Add the price fetcher and the hourly Action
Both APIs are public and need no keys. This is the production form of the spike script, extended with RunPod and per-GPU normalization — an 8×H100 rig's sticker price is divided by 8 so rows compare fairly. Two gotchas from the spike are baked in: Vast.ai's gpu_name uses spaces (“RTX 4090”, not RTX_4090), and extra query params like limit make its API return 400, so the plain query is the reliable form.
scripts/fetch_prices.py
#!/usr/bin/env python3
"""gpu1.ai price fetcher — writes prices.json next to index.html.
Sources (both public, no auth):
Vast.ai GET cloud.vast.ai/api/v0/bundles/?q=<json> (gpu_name uses SPACES)
RunPod POST api.runpod.io/graphql (gpuTypes.lowestPrice)
All prices normalized to USD per GPU per hour."""
import json, statistics, urllib.request, urllib.parse
from datetime import datetime, timezone
VAST = "https://cloud.vast.ai/api/v0/bundles/"
HEAD = {"User-Agent": "gpu1.ai/1.0"}
GPUS = ["B200", "H200", "H100 SXM", "RTX 5090", "RTX 4090", "RTX 3090",
"RTX 3090 Ti", "L40S", "RTX A6000", "A40", "L4"]
VRAM = {"B200": 180, "H200": 141, "H100 SXM": 80, "RTX 5090": 32, "RTX 4090": 24,
"RTX 3090": 24, "RTX 3090 Ti": 24, "L40S": 48, "RTX A6000": 48, "A40": 48, "L4": 24}
RUNPOD_NAME = {"H200": "H200 SXM", "A100 80GB": "A100 SXM", "A100 40GB": "A100 SXM 40GB"}
def vast_offers(name):
q = {"gpu_name": {"eq": name}, "rentable": {"eq": True}, "rented": {"eq": False}}
url = VAST + "?" + urllib.parse.urlencode({"q": json.dumps(q)}) # q only — extra params 400
with urllib.request.urlopen(urllib.request.Request(url, headers=HEAD), timeout=40) as r:
return json.load(r).get("offers", [])
def vast_row(offers):
per = sorted(o["dph_total"] / max(o.get("num_gpus", 1), 1) for o in offers if o.get("dph_total"))
if not per:
return None
best = min(offers, key=lambda o: o["dph_total"] / max(o.get("num_gpus", 1), 1))
rel = [o for o in offers if (o.get("reliability2") or 0) >= 0.98]
return {"min": round(per[0], 3), "median": round(statistics.median(per), 3),
"offers": len(offers), "loc": best.get("geolocation"),
"reliableMin": round(min(o["dph_total"] / max(o.get("num_gpus", 1), 1) for o in rel), 3) if rel else None}
def runpod_lowest():
body = json.dumps({"operationName": "GpuTypes", "query":
"query GpuTypes { gpuTypes { displayName lowestPrice(input:{gpuCount:1}) { uninterruptablePrice } } }"}).encode()
req = urllib.request.Request("https://api.runpod.io/graphql", data=body,
headers={**HEAD, "Content-Type": "application/json", "apollo-require-preflight": "true"})
with urllib.request.urlopen(req, timeout=40) as r:
types = json.load(r)["data"]["gpuTypes"]
return {t["displayName"]: (t.get("lowestPrice") or {}).get("uninterruptablePrice") for t in types}
def main():
rp = runpod_lowest()
rows = []
for name in GPUS:
v = vast_row(vast_offers(name))
if v:
rows.append({"gpu": name, "vram": VRAM[name], "vast": v,
"runpod": rp.get(RUNPOD_NAME.get(name, name))})
for disp, vram in [("A100 80GB", 80), ("A100 40GB", 40)]: # one API name, two cards — split by VRAM
offers = [o for o in vast_offers("A100 SXM4")
if abs(round((o.get("gpu_ram") or 0) / 1024) - vram) <= 4]
v = vast_row(offers)
if v:
rows.append({"gpu": disp, "vram": vram, "vast": v, "runpod": rp.get(RUNPOD_NAME[disp])})
out = {"fetchedAt": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), "rows": rows}
with open("prices.json", "w") as f:
json.dump(out, f, indent=1)
print(f"prices.json: {len(rows)} GPUs at {out['fetchedAt']}")
if __name__ == "__main__":
main()
.github/workflows/prices.yml
name: refresh-prices
on:
schedule:
- cron: "17 * * * *" # hourly, off the :00 rush
workflow_dispatch: # refresh by hand from the Actions tab
permissions:
contents: write # the bot commits prices.json back to main
jobs:
fetch:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: python3 scripts/fetch_prices.py
- run: |
git config user.name "gpu1-price-bot"
git config user.email "bot@gpu1.ai"
git add prices.json
git diff --cached --quiet || git commit -m "prices: hourly refresh"
git push
Wire index.html to prices.json
One change from this preview, which carries its snapshot inline: the production page fetches the JSON the Action keeps fresh, then renders the same tables. If the fetch fails, it falls back to the inline snapshot so the page never renders empty.
Build command: leave empty — there is no build step
Build output directory:/ (the repo root)
Hit Save and Deploy. From then on, every push to main — including the price bot's hourly commits — redeploys automatically in under a minute.
Attach the gpu1.ai domain
In the Pages project: Custom domains → Set up a custom domain → gpu1.ai. If the domain's DNS is already on Cloudflare, the record is created for you and HTTPS is automatic. You bought gpu1.ai at a registrar, so if its DNS isn't on Cloudflare yet, either point the domain's nameservers to Cloudflare first (free, and it's how mtls.ai is set up) or add the CNAME Pages gives you at your registrar — apex domains generally need Cloudflare DNS or CNAME flattening, so the nameserver route is the painless one. Then add www.gpu1.ai as a second custom domain and redirect it to the apex.
Verify the money plumbing
Before announcing anything, click every Rent button on the live site and confirm the URLs carry your codes — ref_id=751418 on Vast.ai (the dedicated, never-rented referral account, so earnings can cash out) and ref=aadmljc2 on RunPod. Check the Action ran green in the repo's Actions tab, and that gpu1.ai/prices.json shows a recent timestamp.
Launch checklist
Search Console: add gpu1.ai and submit the URL — the “cheapest H100” queries are the whole SEO game.
Announce it: a build-in-public X thread (live price drops are the hook), a @bube.builds carousel, and the link in both bios.
First deep-dive: publish “What it actually costs to fine-tune Llama 3 8B” using the calculator's numbers — that's the page other engineers link to.
Weekly price-drop post: the Action's commit history is a ready-made changelog; the biggest drop of the week becomes a recurring post and, later, the newsletter.
Milestone watch: at 25 paying RunPod referrals the 12% cash tier unlocks — that's the day RunPod stops being credit money.
Affiliate disclosure
The short version: links to Vast.ai and RunPod on this page are affiliate/referral links. If you rent a GPU through one, the provider pays gpu1.ai a cut — 3% of your spend on Vast.ai, for the life of your account; on RunPod, referral credits today and a 12% cash tier after 25 paying referrals. Your price is exactly the same as going direct — and on RunPod you get a $5–$500 credit bonus for coming through the link.
About the prices: every figure on this page was pulled from the providers' own public APIs (Vast.ai marketplace offers and RunPod's pricing endpoint) at the snapshot time shown — nothing is hand-typed and nothing is sponsored. Marketplace prices are live inventory: the cheapest offer can be rented by someone else a minute later, which is why the table shows medians alongside minimums. Sponsored placements, if they ever appear, will be labeled as such and will never change a price in the table.