Agent-readable docs index: /llms.txt. Full docs in one file: /llms-full.txt. Download /docs.zip to grep all markdown files locally.

Read the text on screen with OCR

/v1/leases/{lease}/textGET
On-device text recognition (Vision, accurate level, English plus an optional language) of a fresh frame. Boxes use a top-left origin in the same space as the screenshot and tap (screen points by default, see scale). About 0.1 s for the frame plus 0.05-0.25 s for OCR.
Authorizationoptional
bearerAuth?Bearer <token>
Required on the tunnel. An API token of the account, from ghosthand tunnel url --new-token. The unix socket needs no token
Query Parameters
scale?number
Coordinate space of images and points, as a fraction of the native screen pixels. Default: screen points (1/2 on @2x iPhones, 1/3 on @3x), e.g. 414x896 on an iPhone XR instead of 828x1792. Points keep UI text readable for vision models at about a third of the tokens, and stay under every model's resize limit, so coordinates the model reads from the image are the ones to send. Use 1 for native pixels.
Example: 1Min: > 0Max: 1
settle?boolean
Wait until two frames 150 ms apart match (up to 2 s) before OCR.
Default: false
language?string
OCR language, a BCP 47 tag (e.g. it-IT). English en-US is always included.
Example: "it-IT"Min length: 2
Response
200 · Recognized text lines, top to bottom
width *integer
Screenshot width in the request's scale space
height *integer
Screenshot height in the request's scale space
lines *object[]
Recognized lines, top to bottom
Show item properties
text *string
x *integer
Left edge, in the request's scale space
y *integer
Top edge, in the request's scale space
width *integer
height *integer
confidence *number
OCR confidence, 0...1
Min: 0Max: 1
Default · Error (RFC 9457 problem details). `code` is stable; switch on it, not on `title` or `detail`.
code *"invalid-request" | "unauthorized" | "not-found" | "device-not-found" | "no-device" | "multiple-devices" | "permission-denied" | "bluetooth-unavailable" | "bluetooth-disconnected" | "no-frame" | "device-locked" | "lock-failed" | "pairing-timeout" | "pointer-not-found" | "lock-state-unavailable" | "lockdown-unavailable" | "unlock-failed" | "mute-unconfirmed" | "text-not-found" | "wait-text-timeout" | "cancelled" | "lease-invalid" | "device-leased" | "recording-active" | "no-recording" | "recording-failed" | "daemon-busy" | "tunnel-offline" | "login-required" | "subscription-required" | "unsupported-in-mode" | "xctest-unavailable" | "task-failed" | "internal-error"
Stable error code. Switch on it; title and detail are for people.
title *string
Short summary of the code, the same for every error with this code
status *integer
HTTP status of the error. In stream mode the response status stays 200.
detail *string
What happened and what to do next. Agents should read it.
Request example
curl -X GET "https://ghosthand.dev/macs/studio/v1/leases/42/text" \ -H "Authorization: Bearer <token>"
Response example
{ "width": 414, "height": 896, "lines": [ { "text": "Settings", "x": 20, "y": 96, "width": 124, "height": 34, "confidence": 1 }, { "text": "General", "x": 66, "y": 433, "width": 64, "height": 20, "confidence": 1 }, { "text": "Accessibility", "x": 66, "y": 477, "width": 102, "height": 20, "confidence": 0.98 } ] }