Lovely Router文档
获取密钥
API 参考

手机端点

所有 /v1/phones 端点,涵盖云手机、应用、实时屏幕、直接控制和智能体运行,包括参数、限制、响应和错误。

云手机 API 位于 https://lovelyrouter.com/v1/phones 下。每个请求都需要用你的 API 密钥进行身份验证,格式为 Authorization: Bearer $LOVELY_API_KEY(身份验证)。除 APK 上传和截图外,请求体均为 JSON。手机在唤醒状态下每小时 $1.20,按分钟从你的预付费余额中扣费,参见手机的费用。

如需完整的入门演示,请从云手机概览开始。所有坐标都以截图的像素为单位,截图短边为 540 像素:竖屏时 540×1200,手机转为横屏时(游戏、视频)为 1200×540。截图的 width 和 height 会说明是哪一种。

手机对象#

属性

idstring

ph_ 后跟 20 个字符。

objectstring

始终为 phone。

statusstring

starting(约一分钟,安装启动应用时会更久)、ready、failed 或 deleted。

namestring | null

你设置的标签。

userstring | null

你自己的终端用户 id(如果设置了)。

metadataobject | null

你随手机存储的数据。

startup_appsarray

你在 apps 中列出的每个应用对应一项:source、status(pending、installed 或 failed)、packages(安装的包名)和 error。

created_atready_at

Unix 时间戳(秒)。

deletes_after_idle_atinteger | null

如果在此之前未被使用,手机将在这个时间被删除(最后一次活动后 1 小时)。

awake_secondsinteger

截至目前已计费的唤醒时长。

charge_usdnumber

手机截至目前的费用。

end_reasonstring | null

被删除的原因:deleted(由你删除)、idle、balance 或 failed。

errorstring | null

启动失败的原因。

创建手机#

POST/v1/phones

创建一台手机,并在其启动期间返回 201 和手机对象。之后用获取手机加上 wait 等待它就绪。余额中需要至少有 15 分钟的手机时长。每个账户最多同时拥有 3 台手机。

请求体参数

appsarray of strings

手机 ready 之前要安装的应用,最多 20 个:指向 .apk、.apks 或 .xapk 文件的 https:// 链接,或已上传 APK 的名称。安装失败不会导致手机启动失败,请查看 startup_apps。

namestring

标签,最多 80 个字符。

userstring

你的终端用户 id(最多 200 个字符),可用 GET /v1/phones?user= 列出该用户的手机。

metadataobject

最多 4 KB 的自定义数据。

waitinteger

挂起请求、等待手机就绪的秒数,最多 50。

cURL
curl https://lovelyrouter.com/v1/phones \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -H "content-type: application/json" \
  -d '{"apps": ["https://f-droid.org/F-Droid.apk"], "wait": 50}'
Response
{
  "id": "ph_2758eedebf08496f87e0",
  "object": "phone",
  "status": "starting",
  "name": null,
  "user": null,
  "metadata": null,
  "startup_apps": [{ "source": "https://f-droid.org/F-Droid.apk", "status": "pending" }],
  "created_at": 1793308400,
  "ready_at": null,
  "last_active_at": 1793308400,
  "deletes_after_idle_at": 1793312000,
  "awake_seconds": 0,
  "charge_usd": 0,
  "deleted_at": null,
  "end_reason": null,
  "error": null,
  "price_per_hour_usd": 1.20
}

获取手机#

GET/v1/phones/{id}

返回手机对象。带上 wait(秒,最多 50)时,处于 starting 的手机会挂起请求,直到就绪。

列出手机#

GET/v1/phones

返回 {"object": "list", "data": [phone, …]},最新的在前。user= 只保留某个终端用户的手机;all=1 会包含已删除的手机。

删除手机#

DELETE/v1/phones/{id}

删除手机及其中的应用和数据,停止其上的运行,并结算最后几分钟的费用。返回状态为 deleted 的手机对象。手机在 1 小时没有活动后也会被自动删除。

获取截图#

GET/v1/phones/{id}/screenshot

以 WebP 图片(image/webp,短边 540 像素)返回当前屏幕。带上 format=json 时,返回 {screenshot, width, height, app, elements},其中图片为 base64 编码。

cURL
curl https://lovelyrouter.com/v1/phones/ph_2758eedebf08496f87e0/screenshot \
  -H "Authorization: Bearer $LOVELY_API_KEY" -o screen.webp

列出元素#

GET/v1/phones/{id}/elements

屏幕上的内容,来自 Android 的视图树:{"app", "activity", "width", "height", "data": [element, …]}。每个元素包含 id、type(Button、EditText、TextView…)、text、desc(无障碍标签)、resource(视图 id)、x 和 y(中心点)、w 和 h,以及 flags(clickable、long-clickable、scrollable、editable、checked/unchecked、focused、selected、password、disabled)。不使用视图绘制的屏幕(游戏、部分画布)只会列出很少的元素,这时请使用截图。

发送操作#

POST/v1/phones/{id}/actions

在手机上执行一个操作,返回 {"ok": true}(或 ok: false 加 error)。加上 "screenshot": true 可在屏幕稳定后获得 screenshot(base64 WebP)。智能体运行正在操控手机时返回 409。

操作

{"action": "tap", "x", "y"}

点击一个点。

{"action": "long_press", "x", "y", "ms"?}

按住不放(默认 800 毫秒)。

{"action": "swipe", "x", "y", "to_x", "to_y", "ms"?}

从一个点拖动到另一个点(默认 300 毫秒)。

{"action": "scroll", "direction", "x"?, "y"?}

up、down、left 或 right;down 会显示下方的内容。在指定点滚动,或在屏幕中央滚动。

{"action": "type", "text", "clear"?, "submit"?}

在当前聚焦的输入框中输入,支持任何语言。clear 先清空内容;submit 输入后按 Enter。

{"action": "key", "key"}

back、home、enter、recents、delete、tab、escape、search、menu、power、volume_up、volume_down 或 wakeup。

{"action": "open_url", "url"}

打开一个链接:在 Chrome 中打开网页,或打开应用链接(market://、应用自己的 scheme)。

{"action": "launch", "package"}

打开一个已安装的应用,例如 org.fdroid.fdroid。

{"action": "stop_app", "package"}

强制停止一个应用。

{"action": "wait", "seconds"}

最多 60 秒。

{"action": "rotate", "orientation"}

旋转手机:portrait(竖屏)或 landscape(横屏)。锁定方向的应用(很多游戏)保持原方向。实时屏幕会跟着变化。

{"action": "shell", "command"}

在手机上执行一条 adb shell 命令;结果中包含 output。

cURL
curl https://lovelyrouter.com/v1/phones/ph_2758eedebf08496f87e0/actions \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -H "content-type: application/json" \
  -d '{"action": "type", "text": "天气 Tokyo", "submit": true}'
POST/v1/phones/{id}/screen

返回 {"url", "expires_at"}:一个显示手机实时屏幕的页面。拿到链接的任何人都可以观看手机,并用鼠标和键盘操控它,所以只把它分享给应该使用这台手机的人。该页面可以用 iframe 嵌入。

请求体参数

ttl_secondsinteger默认 3600

链接的有效时长:60 到 86,400。已经打开的屏幕会继续可用。

view_onlyboolean

以只读模式打开页面。这只是显示设置,不是权限:观看者可以把它关掉。

安装应用#

POST/v1/phones/{id}/apps

下载并安装一个应用,安装完成后返回:{"ok", "packages", "error"}(安装失败时返回 422)。安装已存在的应用会将其更新。

请求体参数

urlstring

指向 .apk、.apks 或 .xapk 文件的 https:// 链接。

apkstring

或者一个已上传 APK 的名称。

GET /v1/phones/{id}/apps 列出已安装应用的包名,DELETE /v1/phones/{id}/apps/{package} 卸载其中一个。

上传 APK#

PUT/v1/phones/apks/{name}

存储一个 APK,之后可以按 name(字母、数字、_、.、-,最多 80 个字符)在任何手机上安装。把文件作为请求体发送,最大 100 MB;更大的文件(最大 500 MB)请分片上传。.apk、.apks 和 .xapk 都可以;安装 .xapk 时,其中的游戏数据(OBB 文件)会复制到手机上。上传到同一名称会替换原文件。返回 201 和 {name, bytes, sha256}。每个账户最多保存 50 个,超过 1 GB 的存储会计费。

GET /v1/phones/apks 列出已上传的 APK,DELETE /v1/phones/apks/{name} 删除其中一个。

cURL
curl -X PUT https://lovelyrouter.com/v1/phones/apks/myapp.apk \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  --data-binary @myapp.apk

分片上传大 APK#

POST/v1/phones/apks/{name}/uploads

超过 100 MB(最大 500 MB)的文件要分片上传:先告知文件大小,逐个发送分片,最后完成。结果与上传 APK 相同:文件以 name 保存,并替换同名文件。

Body parameters

bytesinteger必填

文件大小(字节),最大 524,288,000(500 MB)。

返回 201 和 {id, part_size, parts, parts_done, expires_at}。然后:

  1. 对从 1 到 parts 的每个分片 n,调用 PUT /v1/phones/apks/{name}/uploads/{id}/{n},请求体是文件从 (n-1) × part_size 开始的字节。除最后一个分片外,每个分片都是 part_size 字节(50 MB),最后一个是剩余部分。分片可以按任意顺序或同时发送,重新发送的分片会替换之前的。
  2. POST /v1/phones/apks/{name}/uploads/{id}/complete 合并分片,返回 201 和 {name, bytes, sha256};如果还缺分片,返回 409 并列出缺少的分片。

GET /v1/phones/apks/{name}/uploads/{id} 显示已收到哪些分片(parts_done),对它调用 DELETE 会放弃这次上传。24 小时内未完成的上传会被丢弃。

存储#

上传的 APK 前 1 GB 免费存储。超出部分每 GB 每月 $0.30,每小时从余额中扣除。不再需要的 APK 可以用 DELETE /v1/phones/apks/{name} 删除。上传需要余额大于零。

cURL
# 120 MB 的文件:3 个分片
curl https://lovelyrouter.com/v1/phones/apks/game.xapk/uploads \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -H "content-type: application/json" \
  -d "{\"bytes\": $(stat -c %s game.xapk)}"

split -b 50M -d -a 2 game.xapk part.
for n in 1 2 3; do
  curl -X PUT https://lovelyrouter.com/v1/phones/apks/game.xapk/uploads/upl_.../$n \
    -H "Authorization: Bearer $LOVELY_API_KEY" \
    --data-binary @part.0$((n-1))
done

curl -X POST https://lovelyrouter.com/v1/phones/apks/game.xapk/uploads/upl_.../complete \
  -H "Authorization: Bearer $LOVELY_API_KEY"

运行对象#

属性

idstring

prun_ 后跟 20 个字符。

objectstring

始终为 phone.run。

phone_idstring

运行所在的手机。

statusstring

queued(手机正在启动)、running、waiting(它向你提了问题)、succeeded、failed 或 stopped(被你停止,或达到 max_steps)。

goalmodel

你发送的内容。

stepsinteger

截至目前的步数。

questionstring | null

处于 waiting 时:智能体需要你提供什么。

answerstring | null

完成后的结果。

errorstring | null

失败或停止的原因。

llm_usdnumber

模型 token 的费用(运行结束时设置)。

tracearray

带上 trace=1 时:每一步的 step、note(智能体看到了什么、为什么这样操作)、action、args、app、ok、error 和 ms;如果操作后屏幕完全没变,还会有 unchanged: true。

启动运行#

POST/v1/phones/runs

创建一台手机并在上面启动智能体,或者用 phone_id 在你已有的某台手机上启动。返回 202 和运行对象。一台手机同一时间只运行一个目标:如果该手机上已有活动的运行,返回 409。

请求体参数

goalstring必填

要做什么,用平实的语言描述,并写明任务需要的所有值。最多 10,000 个字符。

phone_idstring

在这台手机上运行,而不是新建一台。POST /v1/phones/{id}/runs 效果相同。

appsname

用于新手机,同创建手机。

modestring默认 smart

smart:由模型决定每一步(每步几秒钟)。fast:由快速决策模型在不到一秒内选出几乎每一步(它总会行动,适合游戏),model 只用来写答案或向你提问。见快速模式。

planboolean默认 true

仅限快速模式:一个教练模型在快速模型旁边运行(费用已包含在手机价格中)。它为目标写一份简短的策略(游戏的话,写明优先走哪些步)和一条当前局面的提示,每隔几步或运行看起来卡住时会重写这两部分。快速模型从不等它:每一步都使用最新的版本。运行对象的 strategy、situation 和 sidecar_updates 字段会显示这些内容。

modelstring默认 gemini-3.8-flash

GET /v1/models 中的任意对话模型。其 token 按正常价格计费。

max_stepsinteger默认 40

1 到 200。达到上限的运行以 stopped 结束,error 为 reached the step limit (N)。

waitinteger

挂起请求、等待运行结束或提问的秒数,最多 50。

获取运行#

GET/v1/phones/runs/{id}

返回运行对象。wait(最多 50 秒)会挂起请求,直到运行结束或向你提问;trace=1 会附上每一步;加上 after_step=N 时,运行超过 N 步也会立即返回。GET /v1/phones/runs?phone_id= 列出运行,最新的在前。

回复运行#

POST/v1/phones/runs/{id}/reply

用 {"answer": "…"} 回答运行的 question,运行随即继续。运行不在等待状态时返回 409。

停止运行#

POST/v1/phones/runs/{id}/stop

在当前步骤完成后停止运行。手机会保留。

错误#

| 状态码 | 何时出现 | | - | - | | 400 | 某个字段缺失或有误,消息会说明是哪个。 | | 402 | 余额不足以启动手机,或已用完。 | | 404 | 你的账户中没有这台手机、这个运行或这个 APK。 | | 409 | 手机仍在启动中,或正忙于某个运行。 | | 410 | 手机已被删除。 | | 413 | APK 太大,无法一次发送(超过 100 MB 请分片上传),或超过 500 MB。 | | 422 | 应用未能安装,error 会说明原因。 | | 429 | 你已经有 3 台手机,或已上传 50 个 APK。 | | 503 | 目前所有手机都在使用中,请几分钟后再试。 |