API
API reference
A complete, endpoint-by-endpoint reference for the mobileproxy.app REST API. Every path, field, and example here mirrors the live OpenAPI document the API is built from. Base URL https://mobileproxy.app/api/v1.
Authentication & base URL
Every endpoint lives under https://mobileproxy.app/api/v1 and requires a bearer token. Create one in the dashboard under For developers → API tokens, or with POST /tokens while signed in. The secret is shown once — store it. Send it on every request:
Authorization: Bearer mpx_live_…Tokens starting mpx_live_ are your account tokens. The /device/* endpoints are called by the Android app itself and authenticate with a per-phone device token (mpxdev_…) issued at pairing — you normally never call them.
Errors are JSON with a human message and a machine code:
{ "error": "No such device.", "code": "not_found" }| Field | Type | Description |
|---|---|---|
| 401 | unauthorized | Token missing, malformed, or revoked. |
| 403 | forbidden | Token valid but not permitted. |
| 404 | not_found | Resource doesn't exist on this account. |
| 502 | control_plane_unavailable | Gateway/control plane briefly unreachable — retry. |
Account
GET /me — who am I? Returns the account plus live counts of subscriptions and API tokens.
curl https://mobileproxy.app/api/v1/me \
-H "Authorization: Bearer mpx_live_…"
# → { "id": "u_8c31f0", "email": "you@example.com",
# "created_at": "2026-06-28T14:02:11.000Z",
# "subscriptions": 3, "api_tokens": 2,
# "plan": "beta", "balance": "0.00" }Subscriptions
A subscription (id sub_…) is the durable slot you own: its friendly name, settings, and proxies persist even as the phone under it changes. Its device is the phone currently powering it (or null when unpaired), and is swappable — a swap keeps every proxy endpoint.
List subscriptions — each with its paired device merged with live gateway status (online = the proxy tunnel is up):
curl https://mobileproxy.app/api/v1/subscriptions \
-H "Authorization: Bearer mpx_live_…"
# → { "subscriptions": [ {
# "id": "sub_760c1fc8ddab", "name": "Paris SFR",
# "status": "active", "wifi_split": true, "created_at": "…",
# "device": { "id": "phone_ac862619408e", "model": "Galaxy S8",
# "hardware_id": "352091081234567", "hardware_type": "imei",
# "status": "online", "exit_ip": "77.136.66.63",
# "carrier": "SFR", "last_seen": "…", "paired_at": "…" } } ] }Create a subscription (unpaired), then pair or swap the phone. POST /subscriptions/{id}/pair returns a PIN valid ~3 minutes; entered in the Android app it binds the phone. On a slot that already has a device this is a swap (mode: "swap") — the old phone is retired and every proxy keeps its host:port and credentials:
curl -X POST https://mobileproxy.app/api/v1/subscriptions \
-H "Authorization: Bearer mpx_live_…" \
-H "Content-Type: application/json" -d '{ "name": "Paris SFR" }'
# → { "id": "sub_760c1fc8ddab", "status": "unpaired", "device": null, … }
# Pair (or swap) a phone into it
curl -X POST https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/pair \
-H "Authorization: Bearer mpx_live_…"
# → { "subscription_id": "sub_760c1fc8ddab", "mode": "new",
# "pairing": { "pin": "483-119-702", "expires_at": "…" }, … }Get, update, or cancel one subscription. POST changes the name, a freeform description (max 200 chars, null clears it), the wifi_split preference (queued to the paired phone, applied on its next poll ~25 s), or shared_speed_kbps — how the device's speed is divided between its proxies. -1 (recommended) shares it automatically: the control plane measures the device's real speed from live throughput (~3 s rebalance), proxies busy at the same time converge to equal shares of it, and a proxy working alone gets full speed. A value > 0 splits that fixed device total the same way instead. Per-proxy speed_limit_kbps values are kept but not enforced while either is on; 0 turns sharing off. DELETE tears down its device and proxies, releases the gateway ports, and deletes the slot's history — irreversible:
# Fetch one subscription
curl https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID \
-H "Authorization: Bearer mpx_live_…"
# Rename / describe / set Wi-Fi split / share the device's speed equally (auto)
curl -X POST https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID \
-H "Authorization: Bearer mpx_live_…" \
-H "Content-Type: application/json" \
-d '{ "name": "Paris SFR", "description": "Client X — farm slot 2", "wifi_split": true, "shared_speed_kbps": -1 }'
# Cancel (irreversible)
curl -X DELETE https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID \
-H "Authorization: Bearer mpx_live_…"
# → { "deleted": true, "id": "sub_760c1fc8ddab" }Proxies
A proxy is one single-protocol endpoint (SOCKS5 or HTTP) on a device, with its own auth, source-IP whitelist, speed limit, and domain rules. How many a device can serve at once is a plan entitlement (10 on Standard, 20 on Pro) — mint one per customer or tool, and revoke any one without touching the rest. A create past your plan's cap returns 409 proxy_limit_reached.
Create a proxy — mints a fresh username/password; the password is returned in the clear once, here. Pick protocol (socks5 or http) and optionally pass traffic_limit_bytes to give it a hard traffic cap from the start:
curl -X POST https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/proxies \
-H "Authorization: Bearer mpx_live_…" \
-H "Content-Type: application/json" \
-d '{ "protocol": "socks5", "traffic_limit_bytes": 5368709120 }'
# → { "id": "acc_5f734b9270e8", "subscription_id": "sub_760c1fc8ddab",
# "protocol": "socks5",
# "host": "173.249.36.102", "host_v6": "2a02:c207:2319:6018::1",
# "port": 30000,
# "username": "u04c770", "password": "k7Qp2mXe9wZ4",
# "endpoint": "socks5://u04c770:k7Qp2mXe9wZ4@173.249.36.102:30000",
# "endpoint_ipv6": "socks5://u04c770:k7Qp2mXe9wZ4@[2a02:c207:2319:6018::1]:30000",
# "ip_whitelist": [], "speed_limit_kbps": 0,
# "domain_mode": "none", "domain_rules": [],
# "status": "active", "traffic_limit_bytes": 5368709120, "bytes_used": 0,
# "over_limit": false, "created_at": "…" }endpoint is the IPv4 URI, endpoint_ipv6 the same port over IPv6 (the v6 literal is bracketed, per URL syntax). The source-IP whitelist accepts IPv4 and IPv6 addresses and CIDRs, and destinations may be IPv6 too: a SOCKS5 CONNECT to an IPv6 literal or a AAAA-only domain egresses over the phone's carrier IPv6 when the SIM has one.Edit a proxy's policy — auth mode, IP whitelist, speed limit, and domain rules, applied live to the gateway (protocol is fixed — delete and recreate instead):
curl -X PATCH \
https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/proxies/PROXY_ID \
-H "Authorization: Bearer mpx_live_…" \
-H "Content-Type: application/json" \
-d '{ "speed_limit_kbps": 2000,
"domain_mode": "whitelist", "domain_rules": ["api.example.com"] }'List proxies — on one device, or every proxy across the whole account:
# One device
curl https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/proxies \
-H "Authorization: Bearer mpx_live_…"
# Account-wide
curl https://mobileproxy.app/api/v1/proxies \
-H "Authorization: Bearer mpx_live_…"
# → { "proxies": [ { …Proxy… }, … ] }Working proxies with their exit IPs — the account-wide listing carries each proxy's current exit_ip, the device's live status, and a working verdict (device online, no billing or manual hold, not over its traffic cap; not_working_reason says why not). Filter with ?working=true, and add ?unique_ip=true to keep one proxy per distinct exit IP — two SIMs behind the same carrier NAT are also flagged via duplicate_ip:
# Working proxies only, one per distinct exit IP — a paste-ready pool
curl "https://mobileproxy.app/api/v1/proxies?working=true&unique_ip=true" \
-H "Authorization: Bearer mpx_live_…"
# → { "proxies": [ {
# "endpoint": "socks5://user:pass@173.249.36.102:30012",
# "exit_ip": "77.136.66.44", "device_status": "online",
# "working": true, "not_working_reason": null,
# "duplicate_ip": false, … }, … ] }Revoke a proxy — stops that one listener immediately; every other proxy on the device keeps working:
curl -X DELETE \
https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/proxies/PROXY_ID \
-H "Authorization: Bearer mpx_live_…"
# → { "deleted": true, "id": "acc_5f734b9270e8" }Per-proxy usage limits & expiry
Give any proxy a usage limit — ideal when you resell access and meter each customer to a data allowance. Set or change it any time (0 clears it), on creation, via PATCH, or with the dedicated endpoint below. When bytes_used reaches the cap the proxy is blocked: it keeps its port and credentials but refuses every request, and over_limit turns true. Raise the limit above the used amount — or clear it — and the proxy resumes immediately, same endpoint, no re-configuration. Listing proxies also reports each one's traffic_limit_bytes, live bytes_used, and over_limit.
curl -X POST \
https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/proxies/PROXY_ID/limit \
-H "Authorization: Bearer mpx_live_…" \
-H "Content-Type: application/json" \
-d '{ "traffic_limit_bytes": 10737418240 }' # 10 GB — or 0 to remove
# → { "proxy_id": "acc_5f734b9270e8", "subscription_id": "sub_760c1fc8ddab",
# "traffic_limit_bytes": 10737418240, "over_limit": false }Time-based expiry — give a proxy an expires_at instant (ISO 8601, in the future) at creation or via PATCH; null clears a pending expiry. At that moment the proxy is retired — permanently, unlike the reversible usage-limit block. To renew a customer, create a fresh proxy (new port and credentials). Perfect for selling day/week passes:
# A proxy that stops serving in 7 days
curl -X POST https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/proxies \
-H "Authorization: Bearer mpx_live_…" \
-H "Content-Type: application/json" \
-d '{ "protocol": "socks5", "expires_at": "2026-07-13T09:00:00Z" }'
# Extend or clear it later
curl -X PATCH \
https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/proxies/PROXY_ID \
-H "Authorization: Bearer mpx_live_…" \
-H "Content-Type: application/json" \
-d '{ "expires_at": "2026-07-20T09:00:00Z" }' # or null to removeVPN configs (WireGuard)
Turn a SOCKS5 username/password proxy into a full-device WireGuard tunnel. Each proxy can carry one WireGuard config; creation requires the VPN add-on. Legacy OpenVPN rows remain downloadable and revocable, but new OpenVPN configs cannot be provisioned.
# Create a WireGuard config on a proxy
curl -X POST \
https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/proxies/PROXY_ID/vpn-configs \
-H "Authorization: Bearer mpx_live_…" \
-H "Content-Type: application/json" \
-d '{ "kind": "wireguard", "name": "My laptop" }'
# → { "id": "vpn_9f2c1a0b7e", "kind": "wireguard", "client_ip": "10.77.0.2",
# "config_text": "[Interface]\nPrivateKey = …\n[Peer]\n…", … }
# List a proxy's configs (metadata + usage vs the one-config cap)
curl https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/proxies/PROXY_ID/vpn-configs \
-H "Authorization: Bearer mpx_live_…"
# Re-download the file straight to disk
curl -OJ \
https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/proxies/PROXY_ID/vpn-configs/vpn_9f2c1a0b7e/file \
-H "Authorization: Bearer mpx_live_…"
# Revoke (drops the WireGuard peer and kills any live session)
curl -X DELETE \
https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/proxies/PROXY_ID/vpn-configs/vpn_9f2c1a0b7e \
-H "Authorization: Bearer mpx_live_…"Use the file with wg-quick up ./mpx-9f2c1a0b.conf or import it into a WireGuard client. Rotating the proxy also rotates the VPN exit.
IP rotation
Rotation asks the carrier for a fresh IP. It's per-device — every proxy on the device gets the new IP — queued to the phone and applied within one poll cycle (~25 s).
exit_ip on the device or from /ip-history.Rotate now — queue a one-off rotation:
curl -X POST https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/change-ip \
-H "Authorization: Bearer mpx_live_…"
# → { "status": "rotating", "subscription_id": "sub_760c1fc8ddab",
# "command_id": "cmd_a41b",
# "note": "IP rotation on mobile networks is best-effort; …" }Unique-IP rotation — pass unique: true to keep rotating until the exit IP is one not seen in the last window_min minutes of this subscription's IP history (default 60, max 1440), retrying up to max_attempts times (default 3, max 5). Each attempt costs a full rotation cycle (~30–90 s); poll the GET variant for progress. Carriers can legitimately re-issue an IP, so exhausted is a possible honest outcome:
curl -X POST https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/change-ip \
-H "Authorization: Bearer mpx_live_…" \
-H "Content-Type: application/json" \
-d '{ "unique": true, "window_min": 60, "max_attempts": 3 }'
# → { "status": "rotating", "request_id": "rr_18d2c4", "command_id": "cmd_a41b",
# "unique": { "window_min": 60, "max_attempts": 3 }, … }
# Poll for the outcome (active | succeeded | exhausted | superseded | expired)
curl https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/change-ip \
-H "Authorization: Bearer mpx_live_…"
# → { "request_id": "rr_18d2c4", "status": "succeeded",
# "attempts_used": 2, "attempts_max": 3, "window_min": 60,
# "started_ip": "77.136.66.63", "result_ip": "77.136.67.101", … }Bulk rotation — rotate every paired device in one call by passing all: true, or a chosen set via subscription_ids (one or the other is required, so a malformed request can never rotate your whole fleet by accident). Takes the same unique options as the per-subscription endpoint, and each subscription reports its own outcome — one held or unpaired slot never fails the batch. Requires the operator role per subscription:
# Rotate every paired device at once (explicit opt-in)…
curl -X POST https://mobileproxy.app/api/v1/proxies/rotate \
-H "Authorization: Bearer mpx_live_…" \
-H "Content-Type: application/json" \
-d '{ "all": true }'
# …or a chosen set, with the unique-IP options
curl -X POST https://mobileproxy.app/api/v1/proxies/rotate \
-H "Authorization: Bearer mpx_live_…" \
-H "Content-Type: application/json" \
-d '{ "subscription_ids": ["sub_760c1fc8ddab"], "unique": true }'
# → { "results": [ { "subscription_id": "sub_760c1fc8ddab",
# "status": "rotating", "command_id": "…", "request_id": "rr_18d2c4" } ],
# "rotating": 1, "skipped": 0, … }Rotation links — a tokenized URL that rotates this device's IP when fetched, with no auth header (the secret is the token). Paste it into any tool that supports a "rotation URL". List, create, and revoke them:
# List rotation links
curl https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/rotation-links \
-H "Authorization: Bearer mpx_live_…"
# → { "rotation_links": [ { "token": "rk_9f2c81d4e7",
# "url": "https://mobileproxy.app/rotate/rk_9f2c81d4e7", "created_at": "…" } ] }
# Create one
curl -X POST https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/rotation-links \
-H "Authorization: Bearer mpx_live_…"
# → { "token": "rk_9f2c81d4e7", "url": "https://mobileproxy.app/rotate/rk_9f2c81d4e7" }
# Revoke one
curl -X DELETE https://mobileproxy.app/api/v1/rotation-links/ROTATION_TOKEN \
-H "Authorization: Bearer mpx_live_…"
# → { "deleted": true, "token": "rk_9f2c81d4e7" }Scheduled (timed) rotation — set an automatic-rotation interval in minutes (0 turns it off; max 1440). It persists as rotation_interval_min on the device and pushes a set_rotation_interval command the phone applies on its next poll, so it re-dials on that cadence on its own:
# Read the current settings
curl https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/rotation \
-H "Authorization: Bearer mpx_live_…"
# → { "subscription_id": "sub_760c1fc8ddab", "interval_minutes": 0,
# "airplane_seconds": 5, "stale_ip_alert_minutes": 0 }
# Rotate automatically every 15 minutes
curl -X POST https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/rotation \
-H "Authorization: Bearer mpx_live_…" \
-H "Content-Type: application/json" \
-d '{ "interval_minutes": 15 }'
# → { "status": "set", "subscription_id": "sub_760c1fc8ddab",
# "interval_minutes": 15, "airplane_seconds": 5,
# "stale_ip_alert_minutes": 0, "command_id": "cmd_a41b" }Airplane timeout — how long the phone holds Airplane mode ON during each IP change (airplane_seconds, 3–60, default 5). Some carriers only release the old lease after a longer hold; raise this if rotations keep returning the same IP. Applies to scheduled, dashboard, and API rotations alike:
curl -X POST https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/rotation \
-H "Authorization: Bearer mpx_live_…" \
-H "Content-Type: application/json" \
-d '{ "airplane_seconds": 20 }'
# → { "status": "set", "subscription_id": "sub_760c1fc8ddab",
# "interval_minutes": 15, "airplane_seconds": 20,
# "stale_ip_alert_minutes": 0, "command_id": "cmd_b52c" }Custom DNS — the resolvers the phone uses for proxied domain lookups (up to 3 IP literals; the phone applies the first and re-dials its tunnel). An empty list restores the carrier's default DNS:
# Read
curl https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/dns \
-H "Authorization: Bearer mpx_live_…"
# → { "subscription_id": "sub_760c1fc8ddab", "dns_servers": [] }
# Set (and later send [] to go back to carrier DNS)
curl -X POST https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/dns \
-H "Authorization: Bearer mpx_live_…" \
-H "Content-Type: application/json" \
-d '{ "dns_servers": ["1.1.1.1", "8.8.8.8"] }'
# → { "status": "set", "subscription_id": "sub_760c1fc8ddab",
# "dns_servers": ["1.1.1.1", "8.8.8.8"], "command_id": "cmd_c63d" }Monitoring
Exit-IP history — recent exit IPs reported by the phone, deduplicated (a new row only when the IP actually changed). The reliable way to verify a rotation landed:
curl https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/ip-history \
-H "Authorization: Bearer mpx_live_…"
# → { "subscription_id": "sub_760c1fc8ddab", "ip_history": [
# { "ip": "77.136.66.63", "carrier": "SFR", "ts": "…" }, … ] }Optional query parameters: from/to (ISO 8601) bound the window, limit caps the entries (default 50, max 500), and group=day returns per-day change counts instead — pass tz as your UTC offset in minutes so days are bucketed in your timezone. This is what powers the dashboard's IP-history calendar:
# Entries for one day
curl "https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/ip-history?from=2026-07-04T22:00:00Z&to=2026-07-05T22:00:00Z&limit=500" \
-H "Authorization: Bearer mpx_live_…"
# Per-day counts for a month (tz = minutes east of UTC, e.g. 120 for CEST)
curl "https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/ip-history?group=day&tz=120&from=2026-06-30T22:00:00Z&to=2026-07-31T22:00:00Z" \
-H "Authorization: Bearer mpx_live_…"
# → { "subscription_id": "sub_760c1fc8ddab", "days": [
# { "day": "2026-07-03", "changes": 9 }, { "day": "2026-07-05", "changes": 2 } ] }IP alerts — the server watches your fleet (~every 60 s) and opens an alert when something needs attention: stale_ip when a device is online but its exit IP hasn't changed for longer than the subscription's threshold (set stale_ip_alert_minutes on /rotation; 0 = off, 2–10080), and duplicate_ip when two of your devices report the same exit IP (carrier NAT can hand two SIMs one address — rotate one to separate them). Alerts resolve automatically when the condition clears; they also show in the dashboard's alert bell:
# Turn on stale-IP alerts: warn after 60 min without an IP change
curl -X POST https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/rotation \
-H "Authorization: Bearer mpx_live_…" \
-H "Content-Type: application/json" \
-d '{ "stale_ip_alert_minutes": 60 }'
# Poll open alerts (feed this to your own monitoring)
curl https://mobileproxy.app/api/v1/alerts \
-H "Authorization: Bearer mpx_live_…"
# → { "alerts": [
# { "id": "42", "sub_id": "sub_760c1fc8ddab", "sub_name": "Galaxy S8",
# "kind": "stale_ip",
# "message": "IP unchanged for 75 min (alert threshold 60 min) — still 77.136.66.44",
# "detail": { "ip": "77.136.66.44", "age_min": 75, "threshold_min": 60 },
# "created_at": "…", "resolved_at": null, "seen_at": null },
# { "id": "41", "sub_id": "sub_9d8e7f6a5b4c", "sub_name": "Galaxy A32",
# "kind": "duplicate_ip",
# "message": "Exit IP 77.136.67.33 is also used by “Galaxy S8” — rotate one device to separate them",
# "detail": { "ip": "77.136.67.33",
# "shared_with": [ { "sub_id": "sub_760c1fc8ddab", "name": "Galaxy S8" } ] },
# "created_at": "…", "resolved_at": null, "seen_at": null } ],
# "unseen": 2 }
# Include resolved history; filter to one subscription
curl "https://mobileproxy.app/api/v1/alerts?status=all&subscription=SUBSCRIPTION_ID" \
-H "Authorization: Bearer mpx_live_…"
# Reset the dashboard's unseen badge (never resolves anything)
curl -X POST https://mobileproxy.app/api/v1/alerts/seen \
-H "Authorization: Bearer mpx_live_…"
# → { "status": "ok", "marked": 2 }Traffic for a device — bytes counted at the gateway, split per proxy. Handy for billing customers you resell to:
curl https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/traffic \
-H "Authorization: Bearer mpx_live_…"
# → { "subscription_id": "sub_760c1fc8ddab",
# "bytes_sent": 10485760, "bytes_recv": 262144000, "bytes_total": 272629760,
# "per_proxy": [ { "proxy_id": "acc_5f734b9270e8",
# "bytes_sent": 10485760, "bytes_recv": 262144000 } ] }Daily traffic breakdown — per-day sent/received bytes, from minute-resolution samples kept on the web tier, so the series survives device swaps and proxy retirement. data_since marks the oldest sample — days before it have no data, which is not the same as zero traffic. Pass tz (your UTC offset in minutes) so days are bucketed in your timezone; windows up to 92 days:
curl "https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/traffic/by-day?tz=120" \
-H "Authorization: Bearer mpx_live_…"
# → { "subscription_id": "sub_760c1fc8ddab", "tz": 120, "data_since": "…",
# "days": [ { "day": "2026-07-06", "bytes_sent": 10485760,
# "bytes_recv": 262144000, "bytes_total": 272629760 } ],
# "total": { "bytes_sent": 10485760, "bytes_recv": 262144000,
# "bytes_total": 272629760 } }Daily uptime — per-day up/down seconds folded from the device's heartbeat record (unified across device swaps). It measures device heartbeat liveness (~25 s cadence, ~90 s gap threshold), not end-to-end tunnel usability. current is the state right now; uptime_pct covers the observed part of each day:
curl "https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/uptime?tz=120" \
-H "Authorization: Bearer mpx_live_…"
# → { "subscription_id": "sub_760c1fc8ddab", "tz": 120, "current": "up",
# "data_since": "…",
# "days": [ { "day": "2026-07-06", "up_seconds": 31547,
# "down_seconds": 253, "uptime_pct": 99.2 } ] }Account-wide usage — a roll-up across every device:
curl https://mobileproxy.app/api/v1/usage/summary \
-H "Authorization: Bearer mpx_live_…"
# → { "subscriptions": 3, "proxies": 5,
# "bytes_sent": 31457280, "bytes_recv": 786432000, "bytes_total": 817889280 }SMS / OTP
Received SMS — inbound messages on this phone's SIM, newest first. Useful for reading one-time verification codes (OTPs) on phones you own. It relies on the app's optional SMS-receive permission; if that was declined on the device, the list stays empty:
curl https://mobileproxy.app/api/v1/subscriptions/SUBSCRIPTION_ID/sms \
-H "Authorization: Bearer mpx_live_…"
# → { "subscription_id": "sub_760c1fc8ddab", "messages": [
# { "id": "1042", "sender": "+15550101",
# "body": "Your verification code is 314159",
# "received_at": "2026-07-05T14:12:00.000Z" }, … ] }Servers
Gateway locations — the locations your proxies are served from:
curl https://mobileproxy.app/api/v1/servers/locations \
-H "Authorization: Bearer mpx_live_…"
# → { "locations": [ { "id": "eu-de-1", "host": "173.249.36.102",
# "region": "EU · Germany (Frankfurt)", "country": "DE",
# "protocols": ["socks5"], "pool": "mobile" } ] }API tokens
List, create, and revoke API tokens. A newly created token's secret is shown once — store it right away; it is never retrievable again. Revoking takes effect immediately:
# List (metadata only — never the secret)
curl https://mobileproxy.app/api/v1/tokens \
-H "Authorization: Bearer mpx_live_…"
# → { "tokens": [ { "id": "tk_51ad", "name": "storefront",
# "prefix": "mpx_live_9f2c", "created_at": "…", "last_used_at": "…" } ] }
# Create (secret shown once)
curl -X POST https://mobileproxy.app/api/v1/tokens \
-H "Authorization: Bearer mpx_live_…" \
-H "Content-Type: application/json" \
-d '{ "name": "storefront" }'
# → { "id": "tk_51ad", "name": "storefront",
# "token": "mpx_live_9f2c81d4e7…", "prefix": "mpx_live_9f2c",
# "scope_subscription_id": null, "readonly": false,
# "note": "Store this token now — it will not be shown again." }
# Revoke
curl -X DELETE https://mobileproxy.app/api/v1/tokens/TOKEN_ID \
-H "Authorization: Bearer mpx_live_…"
# → { "deleted": true, "id": "tk_51ad" }Scoped & read-only keys
Delegate access without sharing your account. Pass subscription_id to scope a token to a single subscription, and readonly: true to limit it to GET requests. A scoped token can reach only that subscription's own endpoints (plus /me); a read-only token is refused on any write. Both are restrictions layered on top of your account — a scoped key can never reach another account's data.
# A read-only key for ONE subscription — safe to hand a teammate or a monitor
curl -X POST https://mobileproxy.app/api/v1/tokens \
-H "Authorization: Bearer mpx_live_…" \
-H "Content-Type: application/json" \
-d '{ "name": "customer-42",
"subscription_id": "sub_760c1fc8ddab",
"readonly": true }'
# → { …, "scope_subscription_id": "sub_760c1fc8ddab", "readonly": true }
# With that key: a GET on its own subscription works…
curl https://mobileproxy.app/api/v1/subscriptions/sub_760c1fc8ddab/traffic \
-H "Authorization: Bearer mpx_live_scoped…" # 200
# …but another subscription, the account-wide list, or any write is refused.
curl -X POST https://mobileproxy.app/api/v1/subscriptions/sub_760c1fc8ddab/change-ip \
-H "Authorization: Bearer mpx_live_scoped…" # 401 (read-only)