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": "[email protected]",
# "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": "socks5h://u04c770:[email protected]:30000",
# "endpoint_ipv6": "socks5h://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… }, … ] }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 & OpenVPN)
Turn any proxy into a full-device VPN. Each config is a WireGuard (.conf) or OpenVPN (.ovpn) file that tunnels a whole device — phone, laptop, router — through that proxy's mobile carrier IP. A proxy can have 1 of each kind — one WireGuard and one OpenVPN config. Configs require a SOCKS5 + username/password proxy so the gateway can authenticate to it. Creating a config requires the VPN add-on on the device's order (403 vpn_addon_required without it) — existing configs stay listed, downloadable, and revocable.
config_text — the full file, containing the client's private key / certificate. It is not re-derivable; store it when you create it. You can re-download the exact same file any time from the /file endpoint, but a lost file can only be replaced by revoking and creating a new config.# 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…", … }
# ...or OpenVPN
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": "openvpn" }'
# List a proxy's configs (metadata + per-kind usage vs the 1-per-kind 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 / OpenVPN cert 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 as usual: wg-quick up ./mpx-9f2c1a0b.conf for WireGuard, or import the .ovpn into any OpenVPN client. All the device's traffic then exits through the phone's carrier IP, and rotating that proxy's IP rotates the VPN's exit too.
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", … }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)