Coronium Mobile Proxies
REST API v3

Coronium Mobile Proxy API

One REST API for stock, buying, rotation, renewals and webhooks, described in an OpenAPI spec, with a TypeScript SDK, a CLI and an MCP server built on the same routes.

Every line you manage through it is one physical device with its own SIM, rented to one customer, with HTTP and SOCKS5 on the same host and login.

Routes on this page were checked against the live spec and with real calls on 30 October 2026.

Terminal
# No token: the public catalog and live stock
curl -s https://www.coronium.io/api/catalog
curl -s https://api.coronium.io/api/v3/tariffs/available

# With a token: your account and your lines
curl -s -H "Authorization: Bearer $CORONIUM_API_TOKEN" https://api.coronium.io/api/v3/account
curl -s -H "Authorization: Bearer $CORONIUM_API_TOKEN" https://api.coronium.io/api/v3/account/proxies

Base URL and reference

Base URL
https://api.coronium.io/api/v3
Auth header
Authorization: Bearer YOUR_CORONIUM_API_TOKEN
Where the token lives
dashboard.coronium.io, Account, API Access

Every route in the spec has an operationId, so client generators produce readable method names. The spec version moves with the API; read it from the document rather than pinning a number.

Authentication

Create a key in dashboard.coronium.io, under Account, API Access, and send it as Authorization: Bearer YOUR_CORONIUM_API_TOKEN. Dashboard API keys are issued for 365 days. Opening the page again keeps the current key; regenerating it revokes the previous one.

The spec also accepts the key as an auth_token query parameter for older clients. Prefer the header: a key in a URL ends up in logs and browser history. GET /account is the quickest way to test a key.

Errors come back as JSON with error, code, suggested_action, retryable and request_id. Branch on the HTTP status first and the code second, and quote the request_id when you write to support. Unknown paths answer 404 with JSON; treat any redirect from the API as a route that does not exist.

What a call without a token returns
HTTP/2 401
{
  "error": "No auth token",
  "code": "auth_required",
  "suggested_action": "provide_bearer_token",
  "retryable": false,
  "request_id": "req_..."
}

Read the catalog without a token

Two public sources, both live. /api/catalog on this site returns countries, carriers, plans and stock in one JSON document. On the API, /tariffs/available, /free-modems and /countries need no token either.

What /api/catalog holds

observedAt, source
When the data was read, and whether it is live.
countries[].code, name, pageUrl
ISO country code, display name and the country page on this site.
countries[].stockState
in_stock or out_of_stock for the country as a whole.
carriers[].name, is5g, stockState
Each carrier, whether it is a 5G entry, and its own stock state.
carriers[].features
Whether lines in stock carry an OpenVPN profile or the p0f OS fingerprint setting.
carriers[].plans[]
tariffId, period (day, week or month), priceUsd and inStock for each plan.
volumeDiscount, buyUrl, apiBase, docs
The volume discount tiers, where to buy, the API base and the agent manual.

Public reads are limited per IP and per path. Each answer reports the budget in its rate-limit headers (X-RateLimit-Limit first), and a refused call gets HTTP 429 with Retry-After. Stock moves minute to minute, so read it again right before you quote a customer.

catalog.mjs
// Node 18+ (global fetch). Lists countries and carriers in stock.
const res = await fetch("https://www.coronium.io/api/catalog");
const catalog = await res.json();

const inStock = catalog.countries.filter((country) => country.stockState === "in_stock");
for (const country of inStock) {
  const carriers = country.carriers
    .filter((carrier) => carrier.stockState === "in_stock")
    .map((carrier) => carrier.name);
  console.log(`${country.name}: ${carriers.join(", ")}`);
}

The calls most integrations need

All paths are relative to https://api.coronium.io/api/v3. The full list, with request and response schemas, is in the spec.

Plans with live stock
GET /tariffs/available
No token. Only plans you can buy now. Each row carries features.p0f.count and features.openvpn.count.
Stock per region
GET /free-modems
No token. Rows of country, carrier, region, count and p0f_count.
Countries, carriers, regions
GET /countries
No token. The lookup table behind the ids in the other calls.
Your account and balances
GET /account
Token. Also the quickest way to test a token.
Your lines
GET /account/proxies
Token. ip_address, http_port, socks_port, proxy_login, proxy_password, expiry and the restart token.
Buy with account credit
POST /payment/buy-with-account-credit
Token. tariff_id, modemCount and max_total_usd, with an Idempotency-Key header.
Rotate the IP
POST /modems/{modem_id}/restart
Token. Read rotated, not the status code. Some lines answer 202 with pending: true.
Rotation status
GET /modems/{modem_id}/rotation-status
Token. Poll after a 202 until the state is done or failed.
Rotation link
GET /modems/rotate-modem-by-token/{token}
No token. Rotates on every GET, link previews included. Answers 200 only when the IP really changed.
Rotation timer
PUT /modems/{modem_id}/set-rotation-interval
Token. rotation_interval in seconds: 0 turns it off, otherwise at least 60.
Replace a line
POST /modems/{modem_id}/replace
Token. Irreversible: new device, new credentials. 2 per rolling 24 hours by default.
Quote a renewal
POST /payment/renewal-quote
Token. Read-only. Send its total_usd as max_total_usd on the renew call.
Renew with account credit
POST /payment/renew-with-account-credit
Token. modems with modem_id and days, plus max_total_usd.
Payment status
GET /payments/{payment_id}/status
Token. Poll after a 202 or a 503 payment_delivery_unconfirmed. Never order again.

Rotate a line

Read rotated in the answer, not only the status code: rotated: false means the carrier kept the address. Lines that rotate asynchronously answer 202 with pending: true; poll the status and do not resubmit.

Terminal
curl -s -X POST -H "Authorization: Bearer $CORONIUM_API_TOKEN" \
  https://api.coronium.io/api/v3/modems/MODEM_ID/restart

# If the answer was 202 with pending: true, poll:
curl -s -H "Authorization: Bearer $CORONIUM_API_TOKEN" \
  https://api.coronium.io/api/v3/modems/MODEM_ID/rotation-status

# A timer instead: every 300 seconds (0 turns it off)
curl -s -X PUT -H "Authorization: Bearer $CORONIUM_API_TOKEN" \
  -H "Content-Type: application/json" -d '{"rotation_interval": 300}' \
  https://api.coronium.io/api/v3/modems/MODEM_ID/set-rotation-interval

Renew for a quoted total

The quote is computed by the same code that charges, and costs nothing. Send its total_usd as max_total_usd, so the charge can never exceed what you showed your customer.

Terminal
# 1. Ask what 30 more days cost (no charge)
curl -s -X POST https://api.coronium.io/api/v3/payment/renewal-quote \
  -H "Authorization: Bearer $CORONIUM_API_TOKEN" -H "Content-Type: application/json" \
  -d '{"modems": [{"modem_id": "MODEM_ID", "days": 30}]}'

# 2. Renew for that total and no more, with a new key for this order
ORDER_KEY="$(uuidgen)"
curl -s -X POST https://api.coronium.io/api/v3/payment/renew-with-account-credit \
  -H "Authorization: Bearer $CORONIUM_API_TOKEN" -H "Idempotency-Key: $ORDER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"modems": [{"modem_id": "MODEM_ID", "days": 30}], "max_total_usd": QUOTED_TOTAL}'

Paying through the API

Payment: card, cryptocurrency or account balance in the dashboard; through the API, account credit, the account's BTC balance or a card the person saved in the dashboard. The API spends what is already there; it has no route to add money, so a person tops up in the dashboard.

Send an Idempotency-Key on every buy and renew, and resend the same key with the same body on a retry: the first successful answer is replayed instead of a second order. Send the approved total as max_total_usd; if the live total is higher, the call refuses with price_changed and charges nothing.

To try a carrier before a longer plan, buy its 24-hour plan on one line. It is the same dedicated line, and you can renew it for 7 or 30 days where the carrier offers them.

Buy one line with account credit
ORDER_KEY="$(uuidgen)"   # one new key per approved order; reuse it on retries

curl -s -X POST https://api.coronium.io/api/v3/payment/buy-with-account-credit \
  -H "Authorization: Bearer $CORONIUM_API_TOKEN" \
  -H "Idempotency-Key: $ORDER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tariff_id": "TARIFF_ID", "modemCount": 1, "max_total_usd": APPROVED_TOTAL}'

Answers to plan for

200
Delivered. data lists the lines with their connection fields; charged_usd shows what was debited.
202 with paymentId
Paid and still being set up. Poll the payment status; do not order again.
409 price_changed
The total is above max_total_usd. Nothing was charged; quote holds the current total.
409 no_stock
Not enough lines for that plan. Nothing was charged. Pick another carrier or region.
429 payment_in_progress
Another payment on the account is running. Wait Retry-After (5 seconds) and resend with the same key.
400 insufficient_balance
Not enough account credit. Nothing was charged; a person adds funds in the dashboard.
503 with refunded: true
Nothing could be set up and the charge is already back on the balance. A new order is safe.

Webhooks

Set an HTTPS endpoint with PUT /account/webhook and send yourself a test with POST /account/webhook/test. Setting a webhook also turns on automatic replacement of dead lines, which gives those lines a new host and new credentials, so your handler should update what it stores.

Events: proxy.purchased, proxy.purchase_failed, proxy.renewed, proxy.expired, modem.replaced and modem.dead, plus webhook.test for a test. There is no expiry warning event; read expires_at from GET /account/proxies instead.

Every delivery carries X-Coronium-Signature: sha256=<hex> and X-Coronium-Event-Id for de-duplication. Verify the signature over the raw body before you parse the JSON; coronium-sdk exports verifyWebhookSignature for it.

Terminal
curl -s -X PUT https://api.coronium.io/api/v3/account/webhook \
  -H "Authorization: Bearer $CORONIUM_API_TOKEN" -H "Content-Type: application/json" \
  -d '{"webhook_url": "https://your-app.example/coronium"}'

curl -s -X POST -H "Authorization: Bearer $CORONIUM_API_TOKEN" https://api.coronium.io/api/v3/account/webhook/test

SDK, CLI and MCP server

All three wrap the same v3 routes, need Node 20 or newer, and read the public catalog without a token.

TypeScript SDK

coronium-sdk 1.0.0, no runtime dependencies. Methods that charge money are disabled unless the client is created with allowSpending: true.

coronium-sdk on npm
Node
npm install coronium-sdk

// The public catalog needs no token
import { Coronium } from "coronium-sdk";

const coronium = new Coronium();
const plans = await coronium.catalog.availableTariffs({ countryCode: "US" });
console.log(plans.map((plan) => `${plan.carrier_name} ${plan.period}`));

// Your lines need a token
const mine = new Coronium({ token: process.env.CORONIUM_API_TOKEN });
console.log(await mine.proxies.list());

CLI

coronium-cli 1.0.0. Catalog commands work without signing in; --json makes the output easy to pipe.

coronium-cli on npm
Terminal
npm install -g coronium-cli

npx -y coronium-cli tariffs --country US
coronium login --token YOUR_CORONIUM_API_TOKEN
coronium proxy list --json
coronium proxy rotate LINE_ID --wait

MCP server

coronium-proxy-mcp 2.3.0 for AI clients, local over stdio or hosted with OAuth. Spending is off by default. Profiles and host configs are on the MCP server page.

Terminal
# Local server, stdio (Claude Code shown; any MCP host works)
claude mcp add coronium -e CORONIUM_API_TOKEN=YOUR_CORONIUM_API_TOKEN \
  -e CORONIUM_TOOL_PROFILE=directory -- npx -y coronium-proxy-mcp@2.3.0

# Hosted server, OAuth sign-in, never spends
claude mcp add --transport http coronium-remote https://api.coronium.io/mcp

Limits to design around

Public catalog reads
120 requests per minute per IP per path
Rotation link
30 requests per minute per IP
Rotations of one line
At least 60 seconds apart
Replacements
2 per rolling 24 hours by default
Support tickets
1 per minute, 5 per hour
Long calls (rotate, replace, test, purchase)
Allow 180 seconds
Idempotency-Key
8 to 128 characters; replays for 24 hours

Reselling with the API

Resellers use the same API: buy and renew from a prepaid balance, tag each line with your own customer id in its metadata, and hand the connection details to your customers under your brand. Webhooks tell your system when a line is replaced or dies.

The coronium-reseller-kit on GitHub is an MIT-licensed starting point: a Next.js operator dashboard, a webhook receiver and a read-only check of your key, balances, stock and webhook settings. How the reseller programme works is on the reseller page.

Questions

Is there a public API for Coronium mobile proxies?

Yes. The REST API v3 lives at https://api.coronium.io/api/v3, with an OpenAPI spec at https://api.coronium.io/openapi.json and an interactive reference at https://api.coronium.io/api-docs. Catalog and stock routes need no token; everything about your account and lines does.

Where do I get an API token?

In dashboard.coronium.io, Account, API Access. Send it as Authorization: Bearer on protected calls. Dashboard API keys are issued for 365 days, and regenerating the key in the dashboard revokes the old one.

Can I read prices and stock without an account?

Yes. https://www.coronium.io/api/catalog returns countries, carriers, plans and stock as JSON, and GET /tariffs/available on the API returns the plans you can buy now. Public reads are limited to 120 requests per minute per IP per path.

How do I pay for lines through the API?

From money already on the account: account credit, the account's BTC balance, or a card the person saved in the dashboard. The API cannot add funds. Send an Idempotency-Key and the approved total as max_total_usd on every buy and renew, and test with the 24-hour plan first.

How do I rotate a line from code?

POST /modems/{modem_id}/restart with your token, or one GET on the line's rotation link, which needs no token. Leave at least 60 seconds between rotations. For a fixed rhythm, set a timer with PUT /modems/{modem_id}/set-rotation-interval.

Can I resell Coronium lines through the API?

Yes. Resellers buy and renew through the same API from a prepaid balance and hand the connection details to their own customers. The MIT-licensed reseller kit on GitHub has a Next.js operator dashboard, a webhook receiver and a read-only integration check to start from.

How we checked

  • Every route on this page was matched against the live spec at https://api.coronium.io/openapi.json on 30 October 2026.
  • The public calls returned HTTP 200, and the protected calls, sent without a token, returned HTTP 401 with the error body shown above.
  • The Node catalog script, the SDK catalog call and the CLI tariffs command were run against production the same day.
  • Calls that need a token or spend money are shown with placeholders. No real token, line or price appears in this page's source.