Plug an agent into the pond.
The whole GPU spot market — 1,700+ live offers from ten provider feeds — behind one MCP endpoint and one JSON API. Reading it needs no key, no signup, no scraping: an agent can price the market, watch for a number, and pick the cheapest machine in one call. Renting has honest boundaries: execution runs on your provider key, or on a resting limit order your own keyholder daemon fills — your key rides one pass-through call and is never stored, and we hold no custody.
MCP, natively.
The endpoint speaks MCP streamable HTTP, stateless, at https://compute.pangle.online/mcp/.
| tool | what it answers |
|---|---|
| search_offers | ranked live offers — gpu, country (ISO-2, comma list, or EU), region, offer_class, max_price_per_gpu_hr, min_vram_gb, min_gpu_count (8 = full nodes), limit |
| get_offer | one offer by id |
| market_stats | totals, idle share, per-provider freshness and health |
| price_history | the tape for one GPU: per-provider min/median per GPU-hour over time |
| idle_history | the idle-capacity index series |
| best_value | perf-per-dollar — FP16 spec TFLOPS per dollar-hour at each model's best live price (spec-sheet basis, disclosed; unrated models named) |
| provider_reliability | how dependable each provider's feed has been (poll success), plus their own machine-reliability figure where reported |
| price_position | rent-now-or-wait context: today's best price as a percentile of its own trailing 7/30-day range — descriptive, never a forecast |
| spot_spread | the live on-demand vs interruptible discount per model; pass a model for its history |
| create_watch | arm a price tripwire; fires under your line, re-arms 2% above it |
| watch_status | a watch's state, recent events, best matching offer now |
| delete_watch | disarm and remove a watch |
| rent_offer | place a rental on YOUR provider key (Vast or RunPod) — dry-run by default, live only with confirm=true; the key passes through one call and is never stored |
| rent_best | one-shot: search the live book and place on the best match ("cheapest H100 under $2/hr, go"); walks past at most 3 already-gone offers, price cap absolute |
| rental_status | live state, uptime and estimated spend of an instance you placed; shows the budget guard if armed |
| destroy_rental | tear down an instance you placed, same pass-through key |
| register_account | optional metering account — bearer token shown once; pass as account_token on rent calls to build a ledger |
| account_usage | your metered ledger: placements, hours, estimated USD, platform fee (0 today) |
Claude Code — one line:
claude mcp add --transport http compute-wick https://compute.pangle.online/mcp/
Claude Desktop — recent versions connect to remote servers directly; add under mcpServers in claude_desktop_config.json. Older builds can bridge with mcp-remote:
{ "mcpServers": {
"compute-wick": { "url": "https://compute.pangle.online/mcp/" }
} }
// or, over stdio:
{ "mcpServers": {
"compute-wick": { "command": "npx",
"args": ["mcp-remote", "https://compute.pangle.online/mcp/"] }
} }
Python (the mcp SDK):
from mcp import ClientSession from mcp.client.streamable_http import streamable_http_client async with streamable_http_client("https://compute.pangle.online/mcp/") as (r, w): async with ClientSession(r, w) as session: await session.initialize() offers = await session.call_tool("search_offers", {"gpu": "H100", "limit": 5}) # mcp SDK 1.x: streamablehttp_client, unpacks (r, w, _)
LangChain / LangGraph (via langchain-mcp-adapters):
from langchain_mcp_adapters.client import MultiServerMCPClient
client = MultiServerMCPClient({
"compute": {"transport": "streamable_http",
"url": "https://compute.pangle.online/mcp/"}})
tools = await client.get_tools() # hand these to any agent
Plain JSON, if you'd rather.
Every MCP tool has a REST twin. No auth on any of them.
| endpoint | returns |
|---|---|
| GET /api/specs | the workload-lens spec table: dense FP16 tensor TFLOPS per model — spec-sheet ceilings, not benchmarks; absent model = unrated |
| GET /api/offers | ranked offers; filters gpu, country (ISO-2, comma list, or EU), region, offer_class, provider, max_price, min_vram, min_gpu_count, per_model (1 = each model’s cheapest row), limit |
| GET /api/gpus | GPU models with counts, floors, idle tallies |
| GET /api/stats | market snapshot and provider health |
| GET /api/history | price tape per GPU (gpu=, offer_class=, hours=) |
| GET /api/idle-history | idle-capacity index series (hours=) |
| GET /api/value | perf-per-dollar board (offer_class=, min_vram=, limit=) |
| GET /api/reliability | provider feed dependability over the trailing window (days=) |
| GET /api/timing | price percentile vs trailing range for one model (gpu=, offer_class=) |
| GET /api/spread | on-demand vs interruptible discount per model (gpu= adds history) |
| POST /api/watches | arm a tripwire; returns private feed + status URLs |
| POST /api/rentals | rent an offer with your own provider key; dry_run=true by default returns the exact call it would make |
| POST /api/rentals/best | one-shot search+place with the same body plus search filters |
| POST /api/rentals/{provider}/{id}/status | live state + estimated spend (body: your api_key — keys never ride GET) |
| DELETE /api/rentals/{provider}/{id} | destroy an instance you placed (body: your api_key) |
| POST /api/account | optional metering account; GET /api/account/usage (Authorization: Bearer) reads the ledger |
curl 'https://compute.pangle.online/api/offers?gpu=4090&max_price=0.30'
curl -X POST https://compute.pangle.online/api/watches \
-H 'Content-Type: application/json' \
-d '{"gpu": "H100", "max_price_per_gpu_hr": 1.50,
"offer_class": "interruptible",
"webhook_url": "https://your-agent.example/hook"}'
When a watch trips, your webhook receives {watch_id, gpu, line, best, ts} where best is the full offer that crossed your line — including the link to rent it. Webhooks must resolve to public address space; watches re-arm when the price climbs 2% back over the line. Watches expire 30 days after creation (expires_at on every watch response) — re-create one to keep it standing.
Renting through the broker. Bring your own Vast or RunPod API key. Every placement is re-quoted live with your key before money moves, refused if the live price exceeds your max_price_per_gpu_hr, and guarded by an optional idempotency_key so a retried request returns the first receipt instead of a second machine. Dry-run is the default; a live placement requires confirm: true.
The budget cap. Pass auto_destroy_budget_usd and this station watches your rental every five minutes and destroys it when estimated spend (price × uptime, conservative) reaches the cap — the safety net for unattended agents. Plain about the mechanics: arming the cap keeps your key in process memory only, never disk; if the station restarts, the guard is gone while your rental keeps running, and every receipt that arms one says exactly that. Poll rental_status as your own backstop.
Limit orders & the keyholder.
A watch tells you the price crossed your line. A limit order goes further: the station cuts a signed fill ticket naming the exact offer, and whoever holds your provider key executes it. The station never holds the key — that is the whole design.
curl -X POST https://compute.pangle.online/api/orders \
-H 'Content-Type: application/json' \
-d '{"gpu": "RTX 4090", "max_price_per_gpu_hr": 0.02,
"offer_class": "interruptible",
"auto_destroy_budget_usd": 1.0}'
The response carries order_secret — shown once, it authenticates ticket reads and fills for this order only, and can rent nothing by itself. Three ways to execute when the ticket is cut:
1. The keyholder sidecar (recommended): a ~180-line open-source stdlib-Python daemon you run on your own machine — it long-polls the order, verifies the ticket's HMAC signature, and fires the fill with your key from your side. Every safety property is readable in one sitting (the whole station is open source: github.com/aitools420/compute-wick, AGPL), and it dry-runs by default:
# fetch it, read it, run it — stdlib only, no installs curl -sO https://compute.pangle.online/sidecar/keyholder.py ORDER_ID=<id> ORDER_SECRET=<secret> PROVIDER_KEY=<your vast/runpod key> \ python3 keyholder.py # dry-runs until you add CONFIRM=1
A real pass, replayed. The loop above, run against the live book on 2026‑08‑24 (dry-run; the order's line sat above the market, so the ticket cut on the next check):
keyholder: watching order 7dd9e5ee… (dry-run only — set CONFIRM=1) keyholder: ticket — RTX 4090 on vast at $0.1215/GPU/hr (line $0.2000) keyholder: receipt: { "dry_run": true, "would_place": { "method": "PUT", "url": "https://console.vast.ai/api/v0/asks/46655716/", … }, "live_price_final_per_gpu_hr": 0.121481, "fee_bps": 0, "note": "no rental was placed; repeat with dry_run=false and confirm=true to execute" } keyholder: dry-run complete (order stays armed); set CONFIRM=1 for real fills.
2. Your own agent: long-poll POST /api/orders/{id}/ticket ({order_secret, wait: 55}), then POST /api/orders/{id}/fill with {order_secret, api_key, confirm: true}. MCP tools create_limit_order / limit_order_status / fill_limit_order wrap the same rail.
3. A human with curl, if you like your fills artisanal.
Plain about the mechanics: triggers are checked on the station's poll cadence (every ~5 minutes against the keyed feeds; keyless catalogs refresh half-hourly — not tick-by-tick); a ticket lives ~4 minutes then the order re-arms; fills re-quote the live price and refuse above your line; the fill is idempotent per order, so a retry can never rent a second machine; orders expire after 30 days.
Standing orders — compute that survives.
Interruptible machines die; that is why they are cheap. A standing order is a limit order that refuses to: add "standing": true and after a live fill the keyholder keeps watching the machine, reporting what it sees every ~45 seconds. Two consecutive dead observations and the order re-arms — the station cuts a fresh ticket against the current best offer on the book, which may be a different host or a different provider entirely. No single provider can move you off itself; a layer that sees the whole market can. Every death, gap and refill lands on the order's event chain, receipts included.
curl -X POST https://compute.pangle.online/api/orders \
-H 'Content-Type: application/json' \
-d '{"gpu": "RTX 4090", "max_price_per_gpu_hr": 0.25,
"offer_class": "interruptible", "standing": true,
"max_refills": 5, "cooloff_seconds": 120,
"auto_destroy_budget_usd": 5.0}'
Plain about the limits, and why each one exists:
We re-provision the machine, not the work. GPU memory cannot be checkpointed portably across hosts, so resume comes from your own checkpoints: write them to your bucket every few minutes, restore on boot (CHECKPOINT_URL rides every refill). Right tool for checkpointable training and stateless inference; wrong tool for sticky in-memory state.
Refills need your sidecar alive. It is both the witness and the executor — if it is down the station cannot see the death and could not place the replacement anyway, so silence gets you a heartbeats-lost webhook, never a guess. Run it under docker --restart=always or systemd.
Death is confirmed, not assumed. Two consecutive dead observations (~90 seconds), then a cooloff before the refill so a flapping host cannot spend you in a loop. Detection is minutes, not milliseconds. Every refill re-quotes live and holds your original line — if nothing on the book is at or under it, the order says starved and watches on.
Cancelling stops supervision, not the machine. The station holds no key and cannot destroy anything; end a live machine yourself or let its auto_destroy_budget_usd guard do it.
The zombie watchdog. Forgotten machines are how this industry quietly profits; a standing order can refuse that too. Add spend_alert_usd or age_alert_hours and the station webhooks you once per machine when it outlives your line — "up 6.2h ≈ $4.23 — still yours?" The number is an estimate (uptime × price; the provider's bill is authoritative, and the alert says so), alerts fire only while heartbeats flow, and nothing is ever killed on an inference — auto_destroy_budget_usd, a cap you set, remains the only hard stop.
The probe & the trust tape.
The oldest lie in this market is the gap between advertised and delivered: the throttled card, the gimped PCIe slot, the "dedicated" bandwidth that isn't. The probe is a one-minute, stdlib-only script you run on the machine you rented — it reads what the box actually is (GPU identity, VRAM, PCIe link, power limit, disk, network), prints your receipt, and posts the reading to the public trust tape:
curl -sL https://compute.pangle.online/sidecar/probe.py | ADVERTISED="RTX 4090" python3 -
Plain about how the tape earns belief, and what it costs you (nothing):
We never rent the fleet; you already did. Readings come from renters on machines they are paying for anyway — the reading costs you sixty seconds and answers "did I get what I paid for" on the spot. NO_REPORT=1 keeps your receipt private; the tape survives on the renters who don't mind.
Tiers of belief, labeled. A reading signed with a live order's secret (ORDER_ID/ORDER_SECRET) counts as verified — the submitter provably holds an order on this book, and faking that at scale means renting at scale. Anything else is anonymous and says so. No aggregate wears the verified badge under 5 verified readings — a young tape reads accruing, never dressed up. GET /api/trust.
What it can't see. The probe reads hardware facts, not your workload, keys or data — the source is the one file you just read. Coverage follows liquidity: heavily-rented hosts get measured; the long tail shows no readings rather than a guessed score.
The badge.
A live SVG anyone can hotlink — the cheapest listing for a model, refreshed every five minutes, straight off the book. Paste it in a README, a blog, a dashboard:
# markdown  # any model works: ?gpu=4090, ?gpu=A100 · add &offer_class=interruptible for the idle floor
Ground rules.
Honest data. A provider that stops answering is recorded as down and its rows are flagged stale — never silently reused. Every price passes through one fee seam; fee_bps is currently 0 and the field says so on every response.
Referral disclosure. Outbound provider links may carry referral tags. The renter's price is unchanged; that margin is how a zero-fee index pays for itself.
Courtesy. The money feeds (Vast, RunPod) refresh about every five minutes and the keyless catalogs every thirty, so polling faster than that buys you nothing. For "tell me when", use a watch instead of a loop.