Custom Scripts
The built-in scripts cover the common flows. When you need something they do not — a step in a different order, a screen they never touch, or an app that is not TikTok or Instagram — you can write it yourself in any language and let TikMatrix hand you the phone.
Requirements
Custom scripts require a Pro, Team, or Business plan. Starter plan does not have access.
Your plan's device count is also the concurrency limit: a Pro plan (20 devices) can drive 20 phones at once, whether through built-in tasks, custom scripts, or a mix of both.
Two ways to run a script
Standalone
You run your program yourself. TikMatrix just lends you devices.
from tikmatrix import TikMatrix
client = TikMatrix()
for device in client.devices():
if device["busy"]:
continue
with client.device(device["serial"], label="my crawler") as d:
d.press("home")
print(d.info())
Good for one-off jobs, data collection, and anything you want to run from your own scheduler.
Managed
You register the program in TikMatrix and it becomes a task like any other. It gets the task queue, per-plan concurrency, automatic retries, the task log, and schedule templates. TikMatrix leases the device before starting your program and passes the lease id in its environment.
from tikmatrix import TikMatrix
with TikMatrix.from_env() as d: # device already leased
d.click(text="Log in")
print("done") # this line lands in the task log
Good for anything you want to run repeatedly, on a schedule, or across many devices.
Getting started
1. Install the client library
pip install requests
Then copy tikmatrix.py from the SDK directory next to your script. The library is a single file with no other dependencies.
You do not have to use it — the API is plain JSON over HTTP, and the raw endpoints are documented below.
2. Write your script
from tikmatrix import TikMatrix
client = TikMatrix()
with client.device("192.168.1.5:5555") as d:
d.press("home")
d.adb("shell", "am", "start", "-a", "android.settings.SETTINGS")
d.wait_for(text="Settings", timeout=15)
d.screenshot("settings.png")
3. Register it (managed mode only)
Go to Devices → Custom Scripts → Add Script:
| Field | Meaning |
|---|---|
| Name | Shown in the script list and the task log |
| Command | The program line to run, e.g. python C:/scripts/my_flow.py |
| Working directory | Optional. Where the program starts |
| Platform | See platform modes below |
| Timeout | Seconds before the script is killed and the task marked failed. Default 1800 |
| Extra environment variables | Optional JSON object merged into the program's environment |
Then press ▶ on the script row and pick your devices, exactly like a built-in script.
Device leases
A phone can only be driven by one thing at a time. Leasing it tells TikMatrix the device is busy, so:
- the task queue will not dispatch a task onto the same screen, and
- your JSON-RPC calls report agent health exactly as a built-in script does, so the watchdog sees a busy agent rather than a silent one.
A lease also consumes one device slot from your plan.
Leases expire — 120 seconds by default, 600 maximum. The Python library renews yours on a background thread and releases it when the with block ends, so a crashed script frees its device within seconds instead of holding it until you restart the app. If you are calling the API directly, you must send heartbeats yourself.
You can see every live lease, and force-release one, under Settings → Developer API → Active device sessions.
Platform modes
A registered script declares what it targets:
Generic — the device is handed over untouched. No app is started, no account switching, no input-method check, and nothing is closed afterwards. Use this to automate anything that is not TikTok or Instagram.
TikTok / Instagram — the app is opened and the account switched before your program starts, and the app is closed when it finishes, exactly as for a built-in script. TIKMATRIX_PACKAGE tells you which package was resolved. Use this to add a step the built-in scripts do not cover.
Environment variables
A managed script receives:
| Variable | Meaning |
|---|---|
TIKMATRIX_API_BASE | Server URL, e.g. http://127.0.0.1:50809 |
TIKMATRIX_SESSION_ID | The lease already held on your behalf |
TIKMATRIX_SERIAL | The device this task was dispatched to |
TIKMATRIX_PACKAGE | Resolved app package |
TIKMATRIX_PLATFORM | tiktok, instagram, or generic |
TikMatrix.from_env() reads all of these for you.
Standalone scripts get none of them — lease a device explicitly instead.
Common operations
d.info() # device info
d.window_size() # (width, height)
d.screenshot("shot.png") # PNG bytes, optionally saved
d.find(text="Following") # matching nodes with bounds + center
d.exists(resource_id="com.app:id/login")
d.wait_for(text="Home", timeout=15) # block until it appears
d.click(text="Log in") # wait, then tap the centre
d.click_xy(540, 1200)
d.swipe(540, 1600, 540, 600)
d.press("back") # back / home / recent / enter
d.input_text("hello") # types via the bundled input method
d.jsonrpc("deviceInfo") # any UIAutomator2 method
d.adb("shell", "pm", "list", "packages") # ADB, once enabled in Settings
find matches against the dumped UI tree, so when a selector misses you can print(d.hierarchy()) and look at exactly what it searched. The Element Inspector in the device view shows the same tree visually, which is usually the fastest way to find a resource-id.
HTTP endpoints
Device operations need an x-session-id header naming a live lease. There is no API key: like the rest of the local API, these endpoints are unauthenticated — reaching the machine on the network is the access control. They send no CORS headers, so call them from a program (curl, Python, anything server-side) rather than from a page in a browser.
| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/rpc/devices | List online devices and whether each is busy |
POST | /api/v1/rpc/session | Lease a device → session_id |
POST | /api/v1/rpc/session/{id}/heartbeat | Extend the lease |
DELETE | /api/v1/rpc/session/{id} | Release the lease |
GET | /api/v1/rpc/session | List live leases |
POST | /api/v1/rpc/jsonrpc | Call a UIAutomator2 method |
POST | /api/v1/rpc/adb | Run an ADB command |
GET | /api/v1/rpc/hierarchy?serial= | Current UI tree as XML |
GET | /api/v1/rpc/screenshot?serial= | Current screen as PNG |
Example
# Lease a device
curl -X POST http://127.0.0.1:50809/api/v1/rpc/session \
-H "Content-Type: application/json" \
-d '{"serial":"192.168.1.5:5555","label":"curl test","ttl_secs":120}'
# {"code":0,"message":"success","data":{"session_id":"ff3ae079-...","serial":"192.168.1.5:5555", ...}}
# Drive it
curl -X POST http://127.0.0.1:50809/api/v1/rpc/jsonrpc \
-H "x-session-id: ff3ae079-..." \
-H "Content-Type: application/json" \
-d '{"serial":"192.168.1.5:5555","method":"deviceInfo","params":[]}'
# Give it back
curl -X DELETE http://127.0.0.1:50809/api/v1/rpc/session/ff3ae079-...
Errors
| Status | Meaning |
|---|---|
| 403 | Plan below Pro, no lease, lease expired, or ADB access disabled |
| 409 | Device already leased, or your plan has no free device slot |
Triggering a custom script from the API
Registered scripts can also be started through the Task Management API, so one script can queue follow-up work:
curl -X POST http://127.0.0.1:50809/api/v1/task \
-H "Content-Type: application/json" \
-d '{
"serials": ["192.168.1.5:5555"],
"script_name": "custom_script",
"script_config": {
"custom_script_id": 1,
"custom_script_platform": "generic"
}
}'
custom_script_id is the id of the script you registered.
ADB access
/api/v1/rpc/adb gives your scripts a device shell — you need it for pushing media, installing APKs, and changing system settings. Because it is a full shell on an endpoint that has no API key, it ships turned off. Enable it under Settings → Developer API → Allow ADB commands when you have a script that needs it; UI automation over /rpc/jsonrpc works without it.
While it is off, /api/v1/rpc/adb answers 403 and the rest of the API keeps working. Every ADB command a script runs is written to your log file.
Notes and limits
- The command is executed directly, not through a shell, so
&&and|are treated as arguments rather than operators. Registercmd /c "..."(Windows) orsh -c "..."(macOS) if you want shell behaviour. - Quote paths that contain spaces:
"C:/Program Files/Python/python.exe" my_script.py. - A script that exceeds its timeout is terminated and the task is marked failed.
- A non-zero exit code marks the task failed; everything the script writes to stdout and stderr lands in the task log.
- Scripts run with the same permissions as TikMatrix itself. Only register programs you wrote or trust.
Next steps
- Local API Overview — authentication and response format
- Task Management API — create, query, retry and stop tasks
- SDK and examples on GitHub