AndroidNexus API
The AndroidNexus API provides programmatic access to manage your Android device fleet. Use it to automate device management, integrate with your existing tools, and build custom solutions.
Base URL
https://api.androidnexus.ai/api/v1
Build your integration against staging first. It carries its own enterprises and devices and behaves exactly like production:
https://staging-api.androidnexus.ai/api/v1
Building a customer-facing UI on this API? Start with Build your own frontend, which covers holding the key server-side, enrollment, embedded remote control and webhooks.
Authentication
Create an API key in the AndroidNexus console and send it as a bearer token on every request:
curl https://api.androidnexus.ai/api/v1/devices \
-H "Authorization: Bearer nxk_..."
Organization owners and admins create and revoke API keys under Settings. Store a key in a secret manager and rotate it by creating a new key and deleting the old one.
Every API key carries full administrator authority over your enterprise, whoever created it. It can read and change every device, policy and private app, start remote control, and factory reset hardware, and the scopes shown when a key is created do not narrow what it can reach.
Keep it on a server you control. Never ship it to a browser or a mobile app. See Roles and Permissions.
Every endpoint in this reference accepts an API key. API keys, users and team members are managed in the console by a signed-in person; those endpoints refuse an API key with 403 unauthorized_principal_kind.
Response Format
Every response shares one envelope.
Success Response
{
"status": "success",
"data": {},
"metadata": {
"timestamp": "2026-01-01T00:00:00Z",
"request_id": "..."
}
}
Error Response
{
"status": "error",
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request format",
"details": {}
},
"metadata": {
"timestamp": "2026-01-01T00:00:00Z",
"request_id": "..."
}
}
Include the request_id when you contact support about a failed request.
Rate limits
Requests are bounded in two places, and a refusal from either is an HTTP 429
carrying Retry-After in whole seconds. Honour that header and back off;
retrying sooner is refused again.
| Bound | Applies to | Budget |
|---|---|---|
| Per API key | every authenticated request your key makes | 1000 a minute |
| Per organization | starting remote-control sessions, across every key and every console operator | 120 a minute |
| Per address | sign-in and token routes, at our network edge | 100 a minute |
| Per address | the rest of the API, at our network edge | 3000 a minute |
The first two belong to your credential and follow it whatever address you call from. The last two belong to the address you call from, so every integration sharing one egress address shares them.
The two kinds of refusal do not look alike, and your client has to handle both. A limit we apply answers with the ordinary error envelope:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 37
{
"status": "error",
"error": {
"code": "api_key_rate_limited",
"message": "API key request budget exceeded"
},
"metadata": { "timestamp": "...", "request_id": "..." }
}
error.code is api_key_rate_limited or remote_control_rate_limited
depending on which budget you spent.
A limit applied at the edge never reaches our API, so it carries no envelope,
no request_id and no error.code. It is plain text:
HTTP/2 429
Content-Type: text/plain; charset=UTF-8
Retry-After: 51
Server: cloudflare
error code: 1015
So read the status code and Retry-After first, and only then try to parse
the body. A client that assumes every response is JSON throws on this one.
Server: cloudflare and the absence of status in the body are what tell the
two apart if you need to.
Device agent traffic (heartbeats, telemetry, pending-command polls, compliance reports and bug-report uploads) is not bounded by the per-address limits, so a large fleet behind one corporate address is not throttled by its own size.
Pagination
List endpoints accept these parameters:
| Parameter | Default | Description |
|---|---|---|
page | 1 | Page number (starts at 1) |
per_page | varies | Items per page (max 100) |
Paginated responses report page, per_page, total and total_pages.
Quick Start Examples
List Devices
curl "https://api.androidnexus.ai/api/v1/devices?page=1&per_page=20" \
-H "Authorization: Bearer $ANDROIDNEXUS_API_KEY"
Get Device Details
curl "https://api.androidnexus.ai/api/v1/devices/$DEVICE_ID" \
-H "Authorization: Bearer $ANDROIDNEXUS_API_KEY"
Send a Lock Command
curl -X POST "https://api.androidnexus.ai/api/v1/devices/$DEVICE_ID/commands" \
-H "Authorization: Bearer $ANDROIDNEXUS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"command_type": "lock_device"}'
SDK Examples
Python
import os
import requests
BASE_URL = "https://api.androidnexus.ai/api/v1"
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['ANDROIDNEXUS_API_KEY']}"
devices = session.get(f"{BASE_URL}/devices").json()
session.post(
f"{BASE_URL}/devices/{device_id}/commands",
json={"command_type": "lock_device"},
)
JavaScript
const BASE_URL = 'https://api.androidnexus.ai/api/v1';
const headers = {
Authorization: `Bearer ${process.env.ANDROIDNEXUS_API_KEY}`,
'Content-Type': 'application/json',
};
async function listDevices() {
const response = await fetch(`${BASE_URL}/devices`, { headers });
return response.json();
}
async function sendCommand(deviceId, commandType) {
const response = await fetch(`${BASE_URL}/devices/${deviceId}/commands`, {
method: 'POST',
headers,
body: JSON.stringify({ command_type: commandType }),
});
return response.json();
}
Command Types
POST /devices/{deviceId}/commands runs the quick actions the console offers on a device:
command_type | Payload | Effect |
|---|---|---|
lock_device | none | Lock the device screen |
restart_device | none | Restart the device |
factory_reset | none | Factory reset the device and remove it from management |
remove_work_profile | none | Remove the work profile (work profile and COPE devices) |
amapi_issue_command | {"type": "RESET_PASSWORD", "newPassword": "...", "resetPasswordFlags": [...]} | Reset the device password; both fields are optional |
amapi_issue_command | {"type": "CLEAR_APP_DATA", "clearAppsDataParams": {"packageNames": ["..."]}} | Clear the named apps' data |
amapi_issue_command | {"type": "START_LOST_MODE", "startLostModeParams": {...}} | Start lost mode with the message, phone number, email and address to show |
amapi_issue_command | {"type": "STOP_LOST_MODE"} | Stop lost mode |
An optional priority (low, normal, high, urgent) can accompany any command.
Need Help?
- API Reference: Browse the complete API documentation in this section
- Support: Contact [email protected]