# Snapshots

> Save a device's disk (apps, logins, settings) and boot new devices from it. Snapshots are pinned to the host that took them, have per-plan quotas, and can be lost with their host.

Source: https://akaphone-docs.pages.dev/concepts/snapshots/

A **snapshot** saves the disk of a running device: installed apps, their data and logins, system settings. You can then boot any number of new devices from it, each starting exactly where the snapshot left off. The original device is not affected.

Typical uses:

- Install your app and sign in to a test account once, then start every test run from that state.
- Prepare a device with a specific language, region or accessibility setting (see [Localisation testing](https://akaphone-docs.pages.dev/guides/localisation-testing/)).
- Keep a known-good state to compare against after a release.

## Take a snapshot

The device must be `running`.

```bash title="curl"
curl -X POST https://akaphone.abhgnx-417.workers.dev/v1/devices/dev_s6zr7mr4f2f1/snapshot \
  -H "Authorization: Bearer $AKAPHONE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "signed-in-v2.3"}'
```
```typescript title="TypeScript"
const snap = await fetch("https://akaphone.abhgnx-417.workers.dev/v1/devices/dev_s6zr7mr4f2f1/snapshot", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.AKAPHONE_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({ name: "signed-in-v2.3" }),
}).then((r) => r.json());
```
```python title="Python"
import os, requests

snap = requests.post(
    "https://akaphone.abhgnx-417.workers.dev/v1/devices/dev_s6zr7mr4f2f1/snapshot",
    headers={"Authorization": f"Bearer {os.environ['AKAPHONE_API_KEY']}"},
    json={"name": "signed-in-v2.3"},
).json()
```

The response is `202` with the snapshot in `pending` status. It becomes `ready` when the host has finished writing it; poll `GET /v1/snapshots` to see the change. In the MCP server, the tool is `device_snapshot`.

## Restore

Restoring creates a **new device** from the snapshot:

```bash
curl -X POST https://akaphone.abhgnx-417.workers.dev/v1/snapshots/snp_01m5vqsywgez2ntdv5fq42/restore \
  -H "Authorization: Bearer $AKAPHONE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "run-1842", "wait": true}'
```

This is the same as `POST /v1/devices` with `snapshot_id` set, and it accepts the same body (`name`, `profile`, `android`, `wait`). The snapshot must be `ready`. The new device counts toward your concurrency limit and bills like any other device.

## Pinned to one host

In this version a snapshot is stored on the disk of the host that took it, and every device restored from it is placed on **that same host**. `node_id` on the snapshot tells you which host it is. This has three consequences:

1. **Restores can queue.** If that host has no free slot, the new device waits in `queued` even when other hosts are idle. After 10 minutes it gives up with `error: "no_capacity"`.
2. **Snapshots can be lost.** If the host fails permanently, its snapshots move to status `lost` and cannot be restored (`409 conflict: Snapshot was lost with the host that stored it`). Devices created from a lost snapshot are not affected once they are running.
3. **Snapshots can be refused.** A host that is low on disk refuses new snapshots with `409 conflict` (*Device host is low on disk; try again later*).

Treat snapshots as a convenient cache, not a backup. Keep whatever you need to rebuild the state (APK, test account, setup task) so you can recreate a snapshot if it is lost. Moving snapshots to shared storage, so any host can restore them, is on the [roadmap](https://akaphone-docs.pages.dev/roadmap/).

## Statuses

| Status | Meaning |
|---|---|
| `pending` | Being written by the host. Counts toward your quota. |
| `ready` | Can be restored. Counts toward your quota. |
| `error` | The host could not write it. Does not count. |
| `lost` | Its host is gone. Cannot be restored. Does not count. |
| `deleted` | You deleted it. Not listed. |

## Quotas

| Plan | Snapshots you can hold (`pending` + `ready`) |
|---|---|
| Free | 1 |
| Dev | 5 |
| Team | 20 |

Taking one more returns `409 conflict` (*Snapshot quota reached*); delete one first with `DELETE /v1/snapshots/:id`. Separately, you can take at most **5 snapshots per hour** (`429 rate_limited`). Snapshot storage is not billed.

## The snapshot object

```json
{
  "id": "snp_01m5vqsywgez2ntdv5fq42",
  "device_id": "dev_s6zr7mr4f2f1",
  "node_id": "nod_01m5vqsywg84jv64rmmqf9",
  "name": "signed-in-v2.3",
  "size_bytes": 1873215488,
  "status": "ready",
  "created_at": "2026-10-26T09:20:44.000Z"
}
```

`size_bytes` is `null` until the snapshot is `ready`. Full endpoint reference: [Snapshots API](https://akaphone-docs.pages.dev/api/snapshots/).
