Observe
Read the screen as numbered elements or compact text, and take screenshots with their scale.
Both endpoints read the phone's screen, so the phone must be ready, and both count as activity that keeps the phone from idling, unless the key is Read-only. Observe the screen explains every field, which elements are listed, and the text form.
Observe the screen
GET /v1/phones/{id}/observe needs the phones:read scope.
Reads the screen and stores its element list as a snapshot for 15 minutes, so that actions can target elements by ref together with the snapshot_id.
| Parameter | Type | Default | Rules |
|---|---|---|---|
screenshot | string | none | none, jpeg or png. Adds a screenshot to the JSON. |
max_width | integer | The screen's width | From 160 to 2000. The widest the screenshot may be, in pixels. |
format | string | json | json, or text for the text form alone. |
curl "https://phonebox.dev/v1/phones/ph_7kx2m6q4v3ta/observe?screenshot=jpeg&max_width=720" \
-H "Authorization: Bearer $PHONEBOX_API_KEY"The JSON answer is an observation. This one is shortened: data holds the image in base64.
{
"snapshot_id": "snp_4f2k7m3q6z3a",
"taken_at": "2026-09-29T10:01:05.662Z",
"app": { "package": "com.android.launcher3", "activity": null },
"keyboard": { "visible": false, "focused_editable": false },
"screen": { "width": 1080, "height": 2400 },
"elements": [
{
"ref": 1, "type": "EditText", "text": "Search apps", "desc": null, "id": "com.android.launcher3:id/search",
"bounds": [60, 200, 1020, 300], "center": [540, 250], "state": ["clickable", "editable"]
},
{
"ref": 2, "type": "TextView", "text": "Chrome", "desc": null, "id": "com.android.launcher3:id/icon",
"bounds": [100, 1800, 300, 2000], "center": [200, 1900], "state": ["clickable"]
}
],
"truncated": false,
"text": "app com.android.launcher3\nkeyboard hidden\nscreen 1080x2400\n[1] EditText \"Search apps\" #search clickable editable (540,250)\n[2] TextView \"Chrome\" #icon clickable (200,1900)",
"screenshot": { "format": "jpeg", "width": 720, "height": 1600, "scale": 0.666667, "data": "/9j/4AAQSkZJRgABAQAAAQABAAD…" }
}screenshot is present only when you ask for one. Element coordinates are always device pixels, and screenshot.scale is the image's width divided by the screen's. The whole answer stays under 4 MB, so Phonebox shrinks a screenshot further when it has to.
With format=text, the answer is text/plain; charset=utf-8, holding the text field alone, and its X-Snapshot-Id header carries the snapshot ID. The text form never includes a screenshot.
| Error | When |
|---|---|
400 validation_failed | A parameter is outside its range or given twice. |
The readiness and phone-service errors in Common errors apply too.
Take a screenshot
GET /v1/phones/{id}/screenshot needs the phones:read scope.
Returns the screen as an image, not JSON.
| Parameter | Type | Default | Rules |
|---|---|---|---|
format | string | png | png or jpeg. |
max_width | integer | The screen's width | From 160 to 2000. The widest the image may be, in pixels. |
quality | integer | 70 | From 30 to 95. JPEG quality. A PNG ignores it. |
curl -o screen.jpg "https://phonebox.dev/v1/phones/ph_7kx2m6q4v3ta/screenshot?format=jpeg&max_width=720" \
-H "Authorization: Bearer $PHONEBOX_API_KEY"The answer is image/png or image/jpeg, always under 4 MB. Phonebox makes the image narrower when it would be larger. Three headers describe it:
| Header | Meaning |
|---|---|
X-Screen-Width | The screen's width in device pixels. |
X-Screen-Height | The screen's height in device pixels. |
X-Screen-Scale | The image's width divided by the screen's, to six decimal places. Divide a point on the image by it to get device pixels. |
| Error | When |
|---|---|
400 validation_failed | A parameter is outside its range or given twice. |
The readiness and phone-service errors in Common errors apply too.