> ## 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.

# Listing, inspecting and stopping

> Every filter on /rentals, what the rental object contains, how to connect, and how to stop cleanly.

## Listing your rentals

```bash theme={null}
curl -s -G https://api.gpuoutlet.ai/v1/rentals \
  -H "Authorization: Bearer $GPUOUTLET_API_KEY" \
  -d active=true \
  -d limit=50
```

Newest first, cursor-paginated. Requires `rentals:read`.

<Warning>
  This returns **every rental on the account** — ones you started in the dashboard,
  ones another key started, all of them. A key scopes access to the account, not to
  its own rentals. See [why](/api/keys#keys-scope-to-the-account-not-to-themselves).
</Warning>

### Filters

<ParamField query="status" type="string">
  `provisioning` · `running` · `stopping` · `stopped` · `failed`

  **Repeatable.** Omit for all statuses.

  ```
  /rentals?status=running&status=provisioning
  ```
</ParamField>

<ParamField query="active" type="'true' | 'false'">
  Shorthand for `provisioning` + `running` + `stopping` — everything currently
  live, which is also everything currently occupying a
  [concurrency slot](/api/spend-limits#concurrent-rentals).

  This is the filter a "what am I paying for right now" screen wants.
</ParamField>

<ParamField query="created_after" type="string">
  ISO 8601 timestamp. Only rentals created after it.

  ```
  /rentals?created_after=2026-07-01T00:00:00Z
  ```
</ParamField>

<ParamField query="cursor" type="string">
  Opaque; from `meta.next_cursor`.
</ParamField>

<ParamField query="limit" type="integer" default="50">
  1–200.
</ParamField>

<Note>
  `active=true` and an explicit `status` list overlap. If you pass both, you are
  asking two questions at once — prefer one or the other so the query says what you
  mean.
</Note>

## One rental by id

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

This is the endpoint to poll while a rental comes up.

```json theme={null}
{
  "id": "rnt_8h2k4m",
  "name": "sweep-run-14",
  "status": "running",
  "offer_id": "off_3kd91mz7",
  "gpu_model": "h100-sxm-80gb",
  "gpu_name": "NVIDIA H100 SXM",
  "vram_gb": 80,
  "gpu_count": 2,
  "instance_class": "standard",
  "service_tier": "on_demand",
  "region": "us-east-1",
  "region_name": "US East",
  "billing": {
    "currency": "usd",
    "price_per_hour_cents": 498,
    "accrued_cents": 1743,
    "hold_cents": 498
  },
  "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
  },
  "termination_reason": null,
  "failure_code": null,
  "created_at": "2026-07-29T14:02:11.000Z",
  "started_at": "2026-07-29T14:02:47.000Z",
  "stopped_at": null
}
```

`404` covers both "no such rental" and "belongs to another account", reported
identically — distinguishing them would confirm the existence of someone else's
resource.

### Connecting

`access` is `null` until the status is `running`, and **every member is
independently nullable**: an offer may expose SSH but no Jupyter, or a web
terminal but no service port. Check before you use one.

```python theme={null}
access = rental.get("access") or {}

if ssh := access.get("ssh"):
    subprocess.run(["ssh", "-p", str(ssh["port"]), f"{ssh['username']}@{ssh['host']}"])
elif jup := access.get("jupyter"):
    webbrowser.open(jup["url"])
else:
    raise RuntimeError("this offer exposes no interface we can use")
```

<ResponseField name="access.ssh" type="object | null">
  `host`, `port`, `username`.
</ResponseField>

<ResponseField name="access.jupyter" type="object | null">
  `url`.
</ResponseField>

<ResponseField name="access.service" type="object | null">
  `url` — the port your template exposes, if it exposes one.
</ResponseField>

<ResponseField name="access.web_terminal" type="object | null">
  `url`, `username`, `password`.
</ResponseField>

<Note>
  Credentials come back as **fields**, never baked into a URL. A
  `https://user:pass@host/` would end up in your logs, your browser history and
  every `Referer` header the page emits. Keep them out of URLs on your side too.
</Note>

## Stopping

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

Requires `rentals:write`. No body, no idempotency key needed — the call is
idempotent by nature: stopping an already-stopping or already-stopped rental
returns the rental as it is, not an error. Retry it freely.

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

<Warning>
  **`stopping` is not `stopped`.** Teardown is asynchronous. Poll `GET
    /rentals/{id}` until the status is `stopped` before you treat
  `billing.accrued_cents` as the final charge, and before starting a replacement if
  you are near the [concurrency ceiling](/api/spend-limits#concurrent-rentals) — a
  `stopping` rental still holds its slot.
</Warning>

```python theme={null}
session.post(f"{API}/rentals/{rid}/stop")

while (r := get_rental(rid))["status"] == "stopping":
    time.sleep(2)

print("final charge:", r["billing"]["accrued_cents"], "cents")
```

## Common queries

<AccordionGroup>
  <Accordion title="What am I paying for right now?" icon="dollar-sign">
    ```
    /rentals?active=true
    ```

    Sum `billing.accrued_cents` for spend so far; sum `billing.price_per_hour_cents`
    for the current burn rate.
  </Accordion>

  <Accordion title="Stop everything" icon="power-off">
    ```python theme={null}
    for r in list_all("/rentals?active=true"):
        session.post(f"{API}/rentals/{r['id']}/stop")
    ```

    Walk the full cursor — do not stop at the first page. See
    [Pagination](/api/pagination).
  </Accordion>

  <Accordion title="What failed this week, and why?" icon="triangle-exclamation">
    ```
    /rentals?status=failed&created_after=2026-07-22T00:00:00Z
    ```

    Then group by `failure_code`. A run of `provisioning_failed` on one region is
    worth a support ticket; a scatter across regions is usually bad luck.
  </Accordion>

  <Accordion title="Which spot rentals got reclaimed?" icon="bolt">
    ```
    /rentals?status=stopped
    ```

    Then filter on `termination_reason == "preempted"`. If the ratio is high, the
    [spot trade-off](/api/rental-billing#spot-and-preemption) may not be paying for
    itself.
  </Accordion>

  <Accordion title="Why did this one stop at 03:14?" icon="clock-rotate-left">
    ```
    /rentals/rnt_8h2k4m/events
    ```

    The [event history](/api/rental-lifecycle#the-event-history), oldest first, with
    timestamps — rather than diffing objects you polled.
  </Accordion>
</AccordionGroup>
