Skip to main content
POST
/
v1
/
rentals
curl -X POST https://gpuoutlet.ai/v1/rentals \
  -H "Content-Type: application/json" \
  --cookie "sid=$SID" \
  -d '{"familyId":"rtx-4090","gpuCount":1,"templateId":"pytorch","name":"my-finetune"}'
{
  "id": "rent_01H...",
  "status": "provisioning",
  "host": null,
  "port": null,
  "pricePerHourCents": 65,
  "startedAt": "2026-06-07T08:14:22Z"
}
familyId
string
required
GPU family id (rtx-4090, h100-80gb, etc.).
gpuCount
number
required
1, 2, 4, or 8.
templateId
string
Optional, defaults to ubuntu-24-04.
region
string
Optional. ISO country code or broader region.
name
string
Optional human label for your bookkeeping, max 64 chars. Pre-fill with e.g. alice/exp-42.
autoStopAfterHours
number
Optional. Force stop after N hours regardless. Caps your wallet burn.
id
string
Stable rental ID (rent_…)
status
string
provisioning initially
host
string | null
SSH host once provisioning finishes
port
number | null
SSH port once ready
pricePerHourCents
number
Locked rate for this rental
startedAt
string
ISO 8601 UTC
curl -X POST https://gpuoutlet.ai/v1/rentals \
  -H "Content-Type: application/json" \
  --cookie "sid=$SID" \
  -d '{"familyId":"rtx-4090","gpuCount":1,"templateId":"pytorch","name":"my-finetune"}'
{
  "id": "rent_01H...",
  "status": "provisioning",
  "host": null,
  "port": null,
  "pricePerHourCents": 65,
  "startedAt": "2026-06-07T08:14:22Z"
}

Minimum balance check

To launch a rental, your wallet’s available must be at least 1 hour of cost. So a 0.65/hrrentalneeds0.65/hr rental needs 0.65 minimum, an H100 needs $2.80. The reserved amount is held against the wallet for the first hour; after, metering takes over.

Polling for ready

Poll GET /v1/rentals/:id every 2 seconds until status === 'running' and host is populated. Typical: 20–60 seconds.
async function waitUntilReady(id: string): Promise<{ host: string; port: number }> {
  for (let i = 0; i < 60; i++) {
    const r = await fetch(`/v1/rentals/${id}`, { credentials: 'include' });
    const j = await r.json();
    if (j.status === 'running' && j.host) return { host: j.host, port: j.port };
    if (j.status === 'failed') throw new Error(`Provision failed: ${j.error}`);
    await new Promise((r) => setTimeout(r, 2000));
  }
  throw new Error('Timed out waiting for pod to provision.');
}

What happens server-side

  1. Validate request shape
  2. Take a row-level lock on the user’s wallet
  3. Verify available balance ≥ 1 hour of cost
  4. Reserve the hour’s cost (move from available → reserved)
  5. Pick the cheapest matching listing
  6. Send allocation request to the upstream provider
  7. Create a rental row with status='provisioning'
  8. Release the wallet lock
  9. Return 200
Provider-side provisioning happens asynchronously. Our worker polls the provider every 2 seconds and flips status='running' once the pod’s SSH daemon answers.