AkaPhoneDocs
REST API

Devices API

Create, list, inspect and stop cloud Android devices; take screenshots, send taps and text, install apps, snapshot, and mint stream tokens.

A device is a cloud Android phone. Read Devices for the lifecycle and billing rules. All examples assume your key is in AKAPHONE_API_KEY.

The device object#

JSON
{
  "id": "dev_s6zr7mr4f2f1",
  "name": "checkout-test",
  "status": "running",
  "profile": "pixel_8",
  "android": "14",
  "region": "eu-central",
  "node_id": "nod_01m5vqsywg84jv64rmmqf9",
  "snapshot_id": null,
  "created_at": "2026-10-26T09:14:02.000Z",
  "ready_at": "2026-10-26T09:14:09.000Z",
  "terminated_at": null,
  "last_activity_at": "2026-10-26T09:16:40.000Z",
  "billed_minutes": 3,
  "error": null
}
Field Type Description
id string dev_ and 12 characters.
name string or null Your label.
status string queued, creating, running, stopping, terminated, error or lost.
profile string Screen profile, default pixel_8.
android string Android version, "14".
region string Where it runs, eu-central.
node_id string or null The host it was placed on; null while queued.
snapshot_id string or null The snapshot it was restored from.
created_at string When you created it.
ready_at string or null When it became running. Billing starts here.
terminated_at string or null When it ended.
last_activity_at string Last input, task, install or viewer activity. Used for the 30-minute idle stop.
billed_minutes integer Credits charged for this device so far.
error string or null Why it failed, e.g. no_capacity.

List profiles#

GET/v1/profiles

The device profiles you can pass as profile. No authentication needed; cacheable for 5 minutes.

curl https://akaphone.abhgnx-417.workers.dev/v1/profiles

Response 200 OK

JSON
[
  { "id": "pixel_8", "name": "Pixel 8", "width": 720, "height": 1560, "dpi": 320, "android": "14" },
  { "id": "pixel_tablet", "name": "Pixel Tablet", "width": 1280, "height": 800, "dpi": 240, "android": "14" },
  { "id": "small_phone", "name": "Small phone", "width": 540, "height": 960, "dpi": 240, "android": "14" }
]

width and height are the device pixels that input coordinates use. pixel_8 is the default.

Create a device#

POST/v1/devices

Queues a new device and returns it. With wait, the request is held until the device is out of queued and creating, for at most 90 seconds.

Body field Type Default Description
name string null Up to 64 characters.
profile string "pixel_8" pixel_8, pixel_tablet or small_phone; see list profiles.
android string "14" One or two digits.
snapshot_id string null Boot from this ready snapshot. Same as restore.
region string first region Must be an available region; today only eu-central.
wait boolean false Hold the request until the device is running (max 90 s).

The query parameters ?wait=true&timeout=<seconds> do the same as wait; timeout is capped at 90.

curl -X POST https://akaphone.abhgnx-417.workers.dev/v1/devices \
  -H "Authorization: Bearer $AKAPHONE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "checkout-test", "wait": true}'

Response 201 Created with the device. Without wait its status is usually queued. With wait it is normally running; if boot failed it is error. If the 90 seconds ran out while the device was still queued or creating, the response carries the header X-Wait-Timed-Out: 1: poll get a device until it is running.

HTTP
HTTP/1.1 201 Created
Content-Type: application/json
X-Wait-Timed-Out: 1

{"id": "dev_s6zr7mr4f2f1", "status": "creating", "…": "…"}
Error When
402 insufficient_credits Balance below 1 credit.
409 concurrency_limit You already have your plan's maximum of queued, creating and running devices.
404 not_found snapshot_id does not exist.
409 conflict The snapshot is lost, pending or error.
400 invalid_request A field is invalid, the region is unknown, or the profile is not one of pixel_8, pixel_tablet, small_phone.
429 rate_limited More than 10 device creations in a minute.

List devices#

GET/v1/devices

Returns a JSON array of devices, newest first. By default only active devices (queued, creating, running, stopping).

Query Description
status=all Include finished devices (the 200 most recent). all=true is accepted too.
curl "https://akaphone.abhgnx-417.workers.dev/v1/devices?status=all" \
  -H "Authorization: Bearer $AKAPHONE_API_KEY"

Response 200 OK

JSON
[
  { "id": "dev_s6zr7mr4f2f1", "name": "checkout-test", "status": "running", "profile": "pixel_8", "android": "14", "region": "eu-central", "node_id": "nod_01m5vqsywg84jv64rmmqf9", "snapshot_id": null, "created_at": "2026-10-26T09:14:02.000Z", "ready_at": "2026-10-26T09:14:09.000Z", "terminated_at": null, "last_activity_at": "2026-10-26T09:16:40.000Z", "billed_minutes": 3, "error": null },
  { "id": "dev_ynxe87v87zx0", "name": null, "status": "terminated", "profile": "pixel_8", "android": "14", "region": "eu-central", "node_id": "nod_01m5vqsywg84jv64rmmqf9", "snapshot_id": null, "created_at": "2026-10-25T17:02:11.000Z", "ready_at": "2026-10-25T17:02:19.000Z", "terminated_at": "2026-10-25T17:21:40.000Z", "last_activity_at": "2026-10-25T17:21:40.000Z", "billed_minutes": 20, "error": null }
]

Get a device#

GET/v1/devices/:id
curl https://akaphone.abhgnx-417.workers.dev/v1/devices/dev_s6zr7mr4f2f1 \
  -H "Authorization: Bearer $AKAPHONE_API_KEY"

Response 200 OK with the device. 404 not_found if it does not exist or is not yours.

Stop a device#

DELETE/v1/devices/:id

Stops the device and its billing. Idempotent: stopping a device that has already ended returns it unchanged.

curl -X DELETE https://akaphone.abhgnx-417.workers.dev/v1/devices/dev_s6zr7mr4f2f1 \
  -H "Authorization: Bearer $AKAPHONE_API_KEY"

Response 202 Accepted with the device: stopping if it was creating or running (it becomes terminated shortly after), or terminated if it was still queued.

JSON
{ "id": "dev_s6zr7mr4f2f1", "name": "checkout-test", "status": "stopping", "profile": "pixel_8", "android": "14", "region": "eu-central", "node_id": "nod_01m5vqsywg84jv64rmmqf9", "snapshot_id": null, "created_at": "2026-10-26T09:14:02.000Z", "ready_at": "2026-10-26T09:14:09.000Z", "terminated_at": null, "last_activity_at": "2026-10-26T09:31:15.000Z", "billed_minutes": 18, "error": null }

Take a screenshot#

GET/v1/devices/:id/screenshot

Returns the current screen as image/jpeg. If someone is watching the live stream you get the latest frame; otherwise the host captures one.

curl https://akaphone.abhgnx-417.workers.dev/v1/devices/dev_s6zr7mr4f2f1/screenshot \
  -H "Authorization: Bearer $AKAPHONE_API_KEY" \
  -o screen.jpg

Response 200 OK, Content-Type: image/jpeg. Frames are downscaled by the host (540 px wide by default).

Error When
409 conflict The device is not running.
504 internal No frame arrived within 8 seconds. Retry.

Send input#

POST/v1/devices/:id/input

Sends one tap, swipe, text or key event. Coordinates are in device pixels (720×1560 for pixel_8).

type Fields
tap x, y: integers 0–10000
swipe x1, y1, x2, y2: integers 0–10000; ms: 1–10000, default 300
text text: 1–500 characters
key key: HOME, BACK, RECENTS, ENTER, DEL or TAB
curl -X POST https://akaphone.abhgnx-417.workers.dev/v1/devices/dev_s6zr7mr4f2f1/input \
  -H "Authorization: Bearer $AKAPHONE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type": "tap", "x": 360, "y": 780}'

Response 202 Accepted

JSON
{ "ok": true }

The event is sent to the device; there is no confirmation that it had an effect. Take a screenshot to check. For many events in a row, the stream socket is faster than one HTTP request each.

Error When
400 invalid_request Unknown type, missing field, or value out of range.
409 conflict The device is not running, or its host is not connected.

Install an app#

POST/v1/devices/:id/apps

Installs a completed upload on the device.

Body field Type Description
upload_id string An upload in status uploaded.
curl -X POST https://akaphone.abhgnx-417.workers.dev/v1/devices/dev_s6zr7mr4f2f1/apps \
  -H "Authorization: Bearer $AKAPHONE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"upload_id": "upl_01m5vqsywgw57haqkjnexz"}'

Response 202 Accepted

JSON
{ "install_id": "ins_01m5vqsywg99189k5xj7f5" }

The host downloads and installs the APK in the background (adb install -r -g). Poll get an install for the outcome, or watch for the app.installed event on the live stream. See Install an APK.

Error When
404 not_found Unknown upload.
409 conflict The upload is not complete, the device is not running, or its host is not connected.

Get an install#

GET/v1/devices/:id/apps/:install_id

The status of an install started with install an app. Poll it every few seconds until status is no longer pending.

curl https://akaphone.abhgnx-417.workers.dev/v1/devices/dev_s6zr7mr4f2f1/apps/ins_01m5vqsywg99189k5xj7f5 \
  -H "Authorization: Bearer $AKAPHONE_API_KEY"

Response 200 OK

JSON
{
  "install_id": "ins_01m5vqsywg99189k5xj7f5",
  "status": "installed",
  "package": "com.example.shop",
  "created_at": "2026-10-26T09:20:00.000Z",
  "finished_at": "2026-10-26T09:20:06.000Z"
}
Field Description
status pending, installed or failed.
package The installed package name, when it could be determined. Omitted otherwise.
error Why it failed, e.g. INSTALL_FAILED_NO_MATCHING_ABIS or timeout. Omitted on success.
finished_at When it ended. Omitted while pending.

An install still pending after 15 minutes becomes failed with error: "timeout". Install records are kept for 30 days. 404 not_found for an unknown device or install id.

Snapshot a device#

POST/v1/devices/:id/snapshot

Saves the device's disk as a snapshot.

Body field Type Description
name string Optional, up to 64 characters.
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"}'

Response 202 Accepted

JSON
{
  "id": "snp_01m5vqsywgez2ntdv5fq42",
  "device_id": "dev_s6zr7mr4f2f1",
  "node_id": "nod_01m5vqsywg84jv64rmmqf9",
  "name": "signed-in-v2.3",
  "size_bytes": null,
  "status": "pending",
  "created_at": "2026-10-26T09:20:44.000Z"
}
Error When
409 conflict The device is not running; your snapshot quota is reached; the host is low on disk or not connected.
429 rate_limited More than 5 snapshots in an hour.

Create a stream token#

POST/v1/devices/:id/stream-token

Returns a single-use token, valid for 5 minutes, that lets a browser open this device's live stream without your API key. Call it from your server and hand the token to the page.

curl -X POST https://akaphone.abhgnx-417.workers.dev/v1/devices/dev_s6zr7mr4f2f1/stream-token \
  -H "Authorization: Bearer $AKAPHONE_API_KEY"

Response 200 OK

JSON
{ "token": "pfs_Jq3w0kV9mXr2YbT6nLc8Hd1sPfAe4uZo" }

The token is bound to this device. It works for one connection; reconnecting needs a new token.