cloud phone API basics: getting started in 2026
cloud phone API basics: getting started in 2026
cloud phone API basics in 2026 are simpler than most people expect. once you understand four endpoints (auth, list, lock, unlock) you can build almost anything: a CI test runner, a multi-account farm controller, a customer support tool, an automation that takes a screenshot every hour. this guide walks you through the entire surface in plain language, with copy-pasteable code, so by the end of the page you have a working API call against a real Singapore Android device.
if you have not seen any of the broader integration patterns yet, the CI/CD pipeline guide shows what these primitives compose into. this article focuses on the smallest useful unit: one user, one device, one script.
the conceptual model
every cloud phone API has the same five concepts.
- a user (you, identified by an API key)
- a device (a real Android phone in a data center)
- a session (a time-bounded reservation of one device)
- an ADB endpoint (a host:port your
adbclient can connect to) - an event (something that happened: lock, unlock, crash, reboot)
most API calls either inspect the state of one of these or change it. that is the whole model.
getting your API key
log into the cloudf.one dashboard, go to settings, then API keys. click create, name the key (e.g. “local dev”), and copy the secret. it shows once. paste it into your env file.
export CLOUDFONE_TOKEN="cf_live_..."
export CLOUDFONE_API="https://api.cloudf.one/v1"
never commit this to git. for production systems, use a secret manager like AWS Secrets Manager, GCP Secret Manager, or HashiCorp Vault.
your first call: list devices
the simplest authenticated request is GET /devices. it returns the devices your account can see, with state, region, and tags.
curl -s -H "Authorization: Bearer $CLOUDFONE_TOKEN" \
"$CLOUDFONE_API/devices" | jq .
response looks like this.
{
"devices": [
{
"id": "dev_4f2a",
"model": "Samsung S22",
"region": "sg",
"state": "available",
"tags": ["pool-default"]
}
]
}
if you get a 401, double-check the bearer token. if you get a 403, your key probably lacks the read scope. recreate it with full access for now and tighten scopes once your code works.
locking a device
before you can use a device for testing or browsing, lock it. locking gives you exclusive access for a duration you specify, and it returns an ADB endpoint you can connect to.
import os, requests
API = os.environ["CLOUDFONE_API"]
TOKEN = os.environ["CLOUDFONE_TOKEN"]
H = {"Authorization": f"Bearer {TOKEN}"}
r = requests.post(f"{API}/devices/lock", headers=H, json={
"tag": "pool-default",
"duration": 600
}).json()
device_id = r["device_id"]
adb_endpoint = r["adb_endpoint"]
print(device_id, adb_endpoint)
duration is in seconds. 600 means 10 minutes. always set this to slightly less than your job timeout so your script unlocks before the lock auto-expires.
connecting via ADB
once you have the endpoint, plain adb works.
adb connect $ADB_ENDPOINT
adb -s $ADB_ENDPOINT shell getprop ro.product.model
adb -s $ADB_ENDPOINT install -r app-debug.apk
if you have never used ADB over network, the Android developer docs on adb-over-wifi cover the basics. the only difference here is that the device is in Singapore instead of on your desk.
taking a screenshot via API
you do not always need ADB. for one-shot tasks like “take a screenshot every hour”, the REST API is faster.
r = requests.post(f"{API}/devices/{device_id}/screenshot", headers=H).json()
print(r["url"]) # signed URL, valid 15 minutes
drop the URL into Slack, Discord, Notion, anywhere. the Slack notifications guide shows the full pattern.
unlocking when you are done
always call unlock. always.
requests.post(f"{API}/devices/{device_id}/unlock", headers=H)
put it in a finally block, or in your CI’s after_script equivalent. devices left locked cost money and block the next user.
try:
# ... your work ...
finally:
requests.post(f"{API}/devices/{device_id}/unlock", headers=H)
handling errors gracefully
four error codes you will see most often.
401 unauthorized: bad or expired token403 forbidden: token lacks the scope for that endpoint404 not found: device ID does not exist (or was archived)409 conflict: device is already locked by someone else, retry with a different tag
a simple retry wrapper covers the transient cases.
import time
def with_retry(fn, attempts=3, delay=2):
for i in range(attempts):
try:
return fn()
except requests.HTTPError as e:
if e.response.status_code in (409, 503) and i < attempts - 1:
time.sleep(delay * (2 ** i))
continue
raise
rate limits and pagination
cloudf.one rate limits at 60 requests per minute per token by default. heavy users get higher tiers. the X-RateLimit-Remaining header tells you where you stand. when it hits zero, the API returns 429 and a Retry-After header.
for endpoints that return lists (/devices, /sessions, /events), pagination is cursor-based.
url = f"{API}/devices?limit=50"
while url:
page = requests.get(url, headers=H).json()
for d in page["devices"]:
process(d)
url = page.get("next_url")
what to build next
once you have lock, unlock, and ADB working, you can build:
- a CI runner (see the GitLab CI guide)
- a webhook-driven automation (see the webhook patterns guide)
- a multi-account scheduler that locks one device per account each morning
- a screenshot bot that posts a daily collage to Slack
start with one of these. ship it small. then layer in the harder pieces (parallel jobs, RBAC, billing controls).
frequently asked questions
do I need any SDK or can I just use curl?
curl works for everything. cloudf.one publishes a Python SDK and a Node SDK for convenience but neither is required. anything that can speak HTTPS speaks the API.
what is the difference between locking by ID and locking by tag?
locking by ID gives you a specific device. locking by tag asks the platform to pick any available device matching that tag. tags are how you build pools.
how long can I hold a lock?
the default max is 24 hours. higher limits are available on enterprise plans. for normal CI testing, 10-30 minutes is right.
can I run multiple parallel calls with one API key?
yes, up to your rate limit. for high-throughput automation (more than 60 RPM) request a higher tier or rotate across multiple keys.
does the API support iOS devices?
cloudf.one is Android-only. iOS support is on the 2027 roadmap. for now you would need a separate provider for iOS.
ready to fire your first API call against a real Singapore phone? grab a cloudf.one trial token and run the code above as is.