DocsAPI

.md

Phones

All pages

You allocate phones to your workspace, in the console or over the API, and keep them until you release them. Each is paid for from your balance, monthly, or hourly billed per minute while it runs; see Billing. A phone that is running and READY takes actions and runs.

Allocating phones

To allocate, pick a profileId from GET /v1/catalog and ask for 1 to 50 phones of it. You get all of them or none. plan is monthly or payg, and payg when left out. Your balance must cover the new phones, or the request answers 402 payment-required.

terminal
curl https://api.distilled.cx/v1/phones \
  -H "Authorization: Bearer $DISTILLED_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: first-phones-0001" \
  -d "{ \"profileId\": \"$PROFILE_ID\", \"count\": 2, \"plan\": \"monthly\" }"
Phone
{
  "id": "phone-1",
  "status": "assigned",
  "plan": "monthly",
  "paidThrough": "2026-10-23T09:41:12.000Z",
  "profileId": "<a profileId from GET /v1/catalog>",
  "androidVersion": 16,
  "phase": "READY",
  "running": true,
  "stoppedForBalance": false,
  "region": "us",
  "exit": { "kind": "distilled", "country": "US" },
  "createdAt": "2026-09-23T09:41:12.000Z"
}
idstring
Use this everywhere the API asks for a phone.
statusstring
assigned, or stopped when it has been switched off.
profileIdstring
The device the phone presents: maker, codename, Android version and build.
androidVersioninteger
13, 14, 15 or 16.
phasestring | null
Where the phone is in starting up. READY means it can be used.
runningboolean
Whether the phone is on right now.
stoppedForBalanceboolean
Stopped because the balance ran out. It starts again once you top up.
regionstring | null
Where the phone runs.
exitobject
Where its traffic leaves: distilled for our network, or proxy with the proxy's id. See Proxies.
createdAtstring
When the phone was made, in ISO 8601.

Endpoints

GET
/v1/catalogphones:read
The devices you can allocate, the regions with free slots, and a phone's monthly price in US cents.
GET
/v1/phonesphones:read
Every phone in the workspace.
POST
/v1/phonesphones:write
Allocates { profileId, count, region? } and answers 201 with { phones }. Without region, any region with room. Send an Idempotency-Key to make a retry safe. Answers 503 when there are not that many free slots, and allocates none.
GET
/v1/phones/{id}phones:read
One phone.
DELETE
/v1/phones/{id}phones:write
Releases the phone: it is removed and its data wiped at once, and it stops costing.
POST
Switches the phone off. Its apps and data are kept.
POST
Switches it on again. Answers 402 while the balance is used up.
POST
Recreates the phone's container. Apps, data and identity stay.
POST
Gives the phone a new device identity. Send { "wipe": true } to delete its apps and data as well.
GET
Where the phone's GPS says it is: the point it is asked to be at (location), whether that is still on its way (pending), the fix its Android last acknowledged (applied), and what last went wrong (error).
PUT
Puts the GPS back near the exit with { "mode": "exit" }, or pins it to a point with { "mode": "manual", "latitude": 37.7749, "longitude": -122.4194 }. Answers the location as it now stands.

Stopping, starting and restarting answer 409 while a run or a lease holds the phone. When your balance runs out, running phones stop and start again once you top up; a phone still stopped for balance after a week is removed with its data.

Location

Each phone also has a place. Unless you set one, its GPS sits near wherever its traffic exits, and follows the exit when it moves or drifts. A manual point holds however its exits move, and survives restarts until it is put back on automatic. A fresh fix reaches the phone on its next sync, usually within about a minute, and apps still need the location permission they always did to read it.

Updated . Something missing or unclear? Tell us and we will fix the page. The API also describes itself in OpenAPI.