> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gpuoutlet.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# API quickstart

> From no key to a running GPU you can SSH into, in five steps.

This walks the whole path once: mint a key, find capacity, price it, rent it,
connect, stop it. Every response below is real, trimmed only for length.

<Steps>
  <Step title="Mint a key">
    In the dashboard, go to **Settings → API keys → Create key**. Give it a name you
    will recognise in six months, tick the scopes it needs — for this walkthrough,
    all three — and copy the key. It is shown once.

    ```bash theme={null}
    export GPUOUTLET_API_KEY="gpk_live_…"
    ```

    Confirm it works:

    ```bash theme={null}
    curl -s https://api.gpuoutlet.ai/v1/me \
      -H "Authorization: Bearer $GPUOUTLET_API_KEY"
    ```

    ```json theme={null}
    { "key_id": "key_7f3a2b1c", "scopes": ["catalog:read", "rentals:read", "rentals:write"], "mode": "live" }
    ```
  </Step>

  <Step title="Find capacity">
    Ask for H100s that are free right now, cheapest first:

    ```bash theme={null}
    curl -s -G https://api.gpuoutlet.ai/v1/offers \
      -H "Authorization: Bearer $GPUOUTLET_API_KEY" \
      -d gpu_family=h100-80gb \
      -d available=true \
      -d sort=price_asc \
      -d limit=3
    ```

    ```json theme={null}
    {
      "data": [
        {
          "id": "off_3kd91mz7",
          "gpu_model": "h100-sxm-80gb",
          "gpu_name": "NVIDIA H100 SXM",
          "gpu_family": "h100-80gb",
          "vram_gb": 80,
          "max_gpus": 8,
          "instance_class": "standard",
          "region": "us-east-1",
          "region_name": "US East",
          "currency": "usd",
          "price_per_hour_cents": 249,
          "availability": { "kind": "count", "count": 6 },
          "service_tiers": ["on_demand", "spot"]
        }
      ],
      "meta": { "next_cursor": "eyJvIjoiMjQ5Iiwi…" }
    }
    ```

    Every filter is documented in [Browsing the catalog](/api/offers).
  </Step>

  <Step title="Price it">
    A quote tells you what the rental costs per hour at the size you want, and
    whether it can start right now.

    ```bash theme={null}
    curl -s https://api.gpuoutlet.ai/v1/rental-quotes \
      -H "Authorization: Bearer $GPUOUTLET_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "offer_id": "off_3kd91mz7", "gpu_count": 2, "hours": 6 }'
    ```

    ```json theme={null}
    {
      "offer_id": "off_3kd91mz7",
      "gpu_count": 2,
      "service_tier": "on_demand",
      "currency": "usd",
      "price_per_hour_cents": 498,
      "hold_cents": 498,
      "estimated_total_cents": 2988,
      "available": true,
      "sufficient_balance": true
    }
    ```

    `price_per_hour_cents` is for the **whole rental**, not per GPU. A quote reserves
    nothing — see [Quotes](/api/renting#quote-first).
  </Step>

  <Step title="Rent it">
    Creating a rental spends money, so `Idempotency-Key` is required. Generate one
    per intent and reuse it verbatim on every retry of that same intent.

    ```bash theme={null}
    curl -s https://api.gpuoutlet.ai/v1/rentals \
      -H "Authorization: Bearer $GPUOUTLET_API_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: $(uuidgen)" \
      -d '{ "offer_id": "off_3kd91mz7", "gpu_count": 2, "name": "sweep-run-14" }'
    ```

    ```json theme={null}
    {
      "id": "rnt_8h2k4m",
      "status": "provisioning",
      "name": "sweep-run-14",
      "gpu_count": 2,
      "billing": { "currency": "usd", "price_per_hour_cents": 498, "accrued_cents": 0, "hold_cents": 498 },
      "access": null,
      "created_at": "2026-07-29T14:02:11.000Z"
    }
    ```

    <Warning>
      `provisioning` means the machine does not exist yet. `access` is `null` and
      nothing is reachable. Do not treat a `201` as "ready".
    </Warning>
  </Step>

  <Step title="Wait for it, then connect">
    Poll the rental until it is `running` — usually well under a minute.

    ```bash theme={null}
    curl -s https://api.gpuoutlet.ai/v1/rentals/rnt_8h2k4m \
      -H "Authorization: Bearer $GPUOUTLET_API_KEY"
    ```

    ```json theme={null}
    {
      "id": "rnt_8h2k4m",
      "status": "running",
      "access": {
        "ssh": { "host": "ssh.us-east-1.gpuoutlet.ai", "port": 20041, "username": "root" },
        "jupyter": { "url": "https://rnt-8h2k4m.gpuoutlet.ai/lab" },
        "web_terminal": null,
        "service": null
      },
      "billing": { "currency": "usd", "price_per_hour_cents": 498, "accrued_cents": 12, "hold_cents": 498 },
      "started_at": "2026-07-29T14:02:47.000Z"
    }
    ```

    ```bash theme={null}
    ssh -p 20041 root@ssh.us-east-1.gpuoutlet.ai
    ```

    Poll about once every two to three seconds. Every member of `access` is
    independently nullable — an offer may expose SSH but no Jupyter — so check
    before you use one.
  </Step>

  <Step title="Stop it">
    ```bash theme={null}
    curl -s -X POST https://api.gpuoutlet.ai/v1/rentals/rnt_8h2k4m/stop \
      -H "Authorization: Bearer $GPUOUTLET_API_KEY"
    ```

    ```json theme={null}
    { "id": "rnt_8h2k4m", "status": "stopping", "billing": { "accrued_cents": 1743, "hold_cents": 498 } }
    ```

    <Warning>
      `stopping` is not `stopped`. Teardown is asynchronous and the final charge lands
      when the machine is actually gone. Poll until `stopped` before you trust
      `accrued_cents` as final, or before starting a replacement if you are near the
      concurrency ceiling.
    </Warning>
  </Step>
</Steps>

## The same thing in one script

<CodeGroup>
  ```python Python theme={null}
  import os, time, uuid, requests

  API = "https://api.gpuoutlet.ai/v1"
  S = requests.Session()
  S.headers["Authorization"] = f"Bearer {os.environ['GPUOUTLET_API_KEY']}"

  offers = S.get(f"{API}/offers", params={
      "gpu_family": "h100-80gb", "available": "true", "sort": "price_asc", "limit": 1
  }).json()["data"]
  if not offers:
      raise SystemExit("no H100 capacity right now")

  rental = S.post(f"{API}/rentals",
      headers={"Idempotency-Key": str(uuid.uuid4())},
      json={"offer_id": offers[0]["id"], "gpu_count": 1, "name": "quickstart"},
  ).json()

  # Poll until it is usable — or until it gives up. Both are terminal for us.
  while rental["status"] == "provisioning":
      time.sleep(3)
      rental = S.get(f"{API}/rentals/{rental['id']}").json()

  if rental["status"] == "failed":
      raise SystemExit(f"failed: {rental['failure_code']}")

  print("ssh:", rental["access"]["ssh"])
  S.post(f"{API}/rentals/{rental['id']}/stop")
  ```

  ```javascript Node theme={null}
  const API = 'https://api.gpuoutlet.ai/v1';
  const auth = { Authorization: `Bearer ${process.env.GPUOUTLET_API_KEY}` };
  const get = (p) => fetch(`${API}${p}`, { headers: auth }).then((r) => r.json());

  const { data: offers } = await get(
    '/offers?gpu_family=h100-80gb&available=true&sort=price_asc&limit=1',
  );
  if (!offers.length) throw new Error('no H100 capacity right now');

  let rental = await fetch(`${API}/rentals`, {
    method: 'POST',
    headers: { ...auth, 'Content-Type': 'application/json', 'Idempotency-Key': crypto.randomUUID() },
    body: JSON.stringify({ offer_id: offers[0].id, gpu_count: 1, name: 'quickstart' }),
  }).then((r) => r.json());

  while (rental.status === 'provisioning') {
    await new Promise((r) => setTimeout(r, 3000));
    rental = await get(`/rentals/${rental.id}`);
  }
  if (rental.status === 'failed') throw new Error(`failed: ${rental.failure_code}`);

  console.log('ssh:', rental.access.ssh);
  await fetch(`${API}/rentals/${rental.id}/stop`, { method: 'POST', headers: auth });
  ```
</CodeGroup>

<Note>
  Both scripts loop only while the status is `provisioning`, and treat everything
  else as an exit condition. Looping on `!== 'running'` instead would spin forever
  on a `failed` rental — the single most common bug in a first integration.
</Note>

## Next

<CardGroup cols={2}>
  <Card title="Every catalog filter" icon="filter" href="/api/offers" />

  <Card title="Rental lifecycle in detail" icon="arrows-rotate" href="/api/rental-lifecycle" />

  <Card title="Spend limits and safety rails" icon="shield" href="/api/spend-limits" />

  <Card title="Error reference" icon="triangle-exclamation" href="/api/errors" />
</CardGroup>
