Offers
An offer is a rentable slice of a machine: 1, 2, 4 or 8 GPUs, priced for the whole slice. These endpoints are what the Rent GPUs page is built on. See Offers and slices for the concept.
List offers
curl -s "$SUPERHEAT_API/v1/offers?gpu_model=H100%20SXM&num_gpus=2&sort=price_asc&limit=50" \
-H "Authorization: Bearer $SUPERHEAT_KEY"
GET /v1/offers returns {"items": [...], "next_cursor": ...}. Filtering, sorting and paging all happen on the server. With no query parameters you get every available offer, cheapest first, up to the unpaged ceiling described under Paging.
Filters
| Parameter | Type | Default | Matches |
|---|---|---|---|
gpu_model | string | none | Exact GPU model, for example H100 SXM or H200 SXM. Case- and space-sensitive. |
gpu_family | string | none | An architecture family: Hopper, Ampere, Ada, Blackwell or Other. The family is worked out on the server from the normalized model name: in this cell's fleet the H800 and H20 count as Hopper, the A800 as Ampere and the L20 as Ada. The families in the catalog right now, with their counts, are in the catalog summary. A family with nothing listed returns an empty page, not an error |
q | string, up to 100 characters | none | Free-text search: a case-insensitive substring of the GPU model, the hostname and the region, taken together. % and _ are matched literally, not as wildcards |
num_gpus | integer | none | Slice size: 1, 2, 4 or 8 |
region | string | none | The full region code shown on an offer, or a country code such as CN. Exact, case-sensitive match |
min_vram_gb | integer | none | Offers with at least this much VRAM per GPU |
max_price_per_hour_micro | integer | none | Offers at or below this hourly price for the whole slice, in millionths of a yuan (人民币): ¥3.00 is 3000000 |
include_rented | boolean | false | When true, sold-out offers (available_units of 0) are returned alongside the ones with units free. Retired offers are never returned |
sort | enum | price_asc | See below |
limit | integer, 1 to 200 | none | Page size. See Paging |
cursor | string | none | The next_cursor of the previous page |
Filters combine with AND. An unknown sort value, a limit outside 1 to 200, or a number too large for its field is rejected with 422.
Sort values
| Value | Orders by |
|---|---|
price_asc | Hourly slice price, cheapest first |
price_desc | Hourly slice price, most expensive first |
tflops_desc | Slice throughput, highest first. Offers with no TFLOPS figure come last |
vram_desc | VRAM per GPU, highest first |
Offers that tie on the sort value are ordered by their id, so the order is stable from one request to the next.
Paging
Pass limit to page. Each response carries next_cursor; send it back as cursor, with the same sort, to get the next page. On the last page next_cursor is null.
curl -s "$SUPERHEAT_API/v1/offers?sort=price_asc&limit=100&cursor=$NEXT_CURSOR" \
-H "Authorization: Bearer $SUPERHEAT_KEY"
- The cursor is a position in the sort order, not a snapshot: it holds the last offer's sort value and id. An offer listed while you page lands where it sorts, and never shifts the pages after it. Filters may change between pages.
- A cursor only works with the sort it came from. A cursor from another sort, or one that cannot be parsed, is rejected with
422 INVALID_CURSOR; passnext_cursorback exactly as you received it. When one is rejected, start again without a cursor. - Offers with no TFLOPS figure stay at the end of a
tflops_descwalk, across pages.
A request without limit returns the whole matching catalog, as it always has, up to 1,000 offers. Past that it returns the first 1,000, a next_cursor, and the response header X-Result-Truncated: true, so a script that does not page is told that its list is short. Page with limit if you might ever read more than 1,000 offers.
Response fields
| Field | Type | Meaning |
|---|---|---|
id | uuid | Pass this as offer_id when you deploy |
gpu_model | string | GPU model in the slice |
num_gpus | integer | GPUs in the slice |
available_units | integer | How many of this shape the machine can serve right now. 0 means sold out, not retired |
capacity_units | integer | The ceiling: how many it could serve if the machine were idle |
vram_gb | integer | VRAM per GPU |
tflops | float | Throughput for the whole slice |
cuda_version | string | CUDA version the host provides |
vm_capable | boolean | Whether vm launch-mode templates can run here |
disk_quota_capable | boolean | Whether the host enforces a per-instance disk quota. See below |
price_per_hour_micro | integer | Price for the whole slice, per hour |
storage_price_per_gb_hour_micro | integer | Disk price, per GB per hour |
max_duration_hours | integer or null | Rental time cap, when the offer has one |
status | string | Always available on anything the API returns. See below |
machine | object | The host the slice sits on |
status is listed-versus-retired, not free-versus-taken. The only other value is inactive, meaning the offer is retired — the machine is gone, its GPU model changed, or no pricing rule matches it any more — and retired offers are filtered out of the list and answer 404 on the detail endpoint. Sold out is available_units of 0. There is no rented status: a 2-GPU machine sells its 1x shape twice, so one shape can be rented and still available at the same time.
disk_quota_capable says whether the machine can hold you to the disk_gb you asked for. You are billed for that figure either way. It is true only where the host has put Docker's storage on XFS with project quotas, which is an opt-in step for the machine owner, so expect false to be common. On a false machine, everyone renting that box shares one volume and a co-tenant who writes more than they bought can fill it under you; disk_gb is a cap the catalog sold, not a share held for you. On a true machine it is a quota enforced per instance. The console shows this as guaranteed versus shared on the offer card.
The machine object carries hostname, region, country_code, cpu_model, cpu_cores, ram_gb, disk_type, disk_gb, net_up_mbps, net_down_mbps, pcie_gen, pcie_width, reliability, verified and next_maintenance.
region is the location the operator entered for the host, and country_code is taken from its first two letters; when the operator entered no location they are UNKNOWN and XX. See Regions.
machine.disk_gb is the whole volume, and it is an upper bound on the disk_gb you ask for at deploy time, not a promise you can get it: 50 GB is reserved for the host itself, and everything already committed by other renters on that machine comes off the rest. Asking for more than is left returns 409 MACHINE_DISK_EXHAUSTED with the figure that was actually available.
machine.reliability is null when nothing has ever been measured for that host, which is not the same claim as 0.0. A measured value is a fraction between 0 and 1, stored to four decimals — the console shows it as a percentage with two. machine.next_maintenance is when the host has scheduled the machine to go down, or null. It is the start of the earliest window still to come or still running, so it can be in the past. Treat it as a warning and not a guarantee — the host can book a window a minute after you rent the box, and a window sooner than about 75 minutes away makes a deploy fail with 409 MACHINE_IN_MAINTENANCE.
Prices are set by Superheat, not by machine owners, and the price on the offer is snapshotted onto the instance when you rent it. A later catalog price change does not move the rate you are paying.
Catalog summary
curl -s "$SUPERHEAT_API/v1/offers/summary" \
-H "Authorization: Bearer $SUPERHEAT_KEY"
GET /v1/offers/summary counts the whole listed catalog, not one filtered page. The marketplace uses it for its filter options and its headline.
| Field | Meaning |
|---|---|
offers | Listed offers, sold-out ones included |
available_offers | Offers with at least one free unit right now |
machines | Distinct machines listing at least one offer |
available_gpus | Free GPUs. For each machine this counts its largest free slice, never the sum of its slice sizes: an 8-GPU machine lists its 1×, 2×, 4× and 8× shapes on the same cards |
min_price_per_hour_micro, max_price_per_hour_micro | Cheapest and dearest slice price, or null when nothing is listed |
models | One entry per GPU model: gpu_model, gpu_family, offers, available_offers, machines, available_gpus and its price range |
families | One entry per gpu_family, with offers and available_offers. Other comes last. These are the values gpu_family accepts |
regions | One entry per country_code, with offers and available_offers. These are the values region accepts |
generated_at | When the counts were taken |
source | snapshot or live, see below |
The counts come from a snapshot that is rebuilt after each catalog refresh (source: snapshot), so they can trail a deploy by up to one refresh. When there is no recent snapshot — none from the last five minutes, or the last three refresh intervals if those are longer, because the refresh is turned off or rebuilding the snapshot failed — the endpoint counts the catalog for that request instead and says source: live. The list endpoint is always live. Use the summary for options and headline figures, and re-read available_units on the offer before you deploy.
Get one offer
curl -s "$SUPERHEAT_API/v1/offers/5d2f9c31-8b64-4c0e-9a77-2e0f1b6a4c11" \
-H "Authorization: Bearer $SUPERHEAT_KEY"
Returns a single offer in the shape above, sold out or not. Unknown and retired offers return 404 OFFER_NOT_FOUND.
Re-read available_units before deploying, not status. The last free unit can be taken between your list call and your create call, and POST /v1/instances will answer 409 OFFER_UNAVAILABLE.
Catalog freshness
curl -s "$SUPERHEAT_API/v1/catalog/status" \
-H "Authorization: Bearer $SUPERHEAT_KEY"
{
"last_synced_at": "2026-07-24T11:04:12.907Z",
"last_error": null,
"sync_enabled": true,
"unpriced_gpu_models": []
}
| Field | Type | Meaning |
|---|---|---|
last_synced_at | timestamp or null | When the catalog last refreshed successfully |
last_error | string or null | The last refresh failure, if there was one |
sync_enabled | boolean | Whether the catalog refreshes automatically at all |
unpriced_gpu_models | array of strings | GPU models the last refresh withheld because Superheat has no price for them |
A failed refresh leaves the previous catalog in place rather than emptying it, so an old last_synced_at means the offers you are reading may no longer be rentable. The marketplace shows a staleness banner in that case; in a script, treat a stale timestamp as a reason to expect 409 OFFER_UNAVAILABLE and retry with the next offer.
A model in unpriced_gpu_models produces no offer at all rather than an offer at a made-up price, so from /v1/offers alone that hardware is indistinguishable from hardware nobody is hosting. This field is the only way to tell the two apart: if the model you are looking for is listed here, the capacity exists and is not rentable yet, and polling /v1/offers for it will never succeed.