Virtual Number API for SMS Verification

The same private virtual numbers over a REST API: buy a number, receive SMS codes, check your balance and stream events. Bearer auth, OpenAPI spec included.

Buy virtual phone numbers and one-time SMS codes, rent proxies, receive verification codes, check your balance and stream realtime events. Authenticate every request with a personal Bearer API key (Authorization: Bearer vpn_xxx) created in the account dashboard. Money values are integer USD cents. A lane that is not selling answers 501 not_implemented or not_enabled — feature-detect on it rather than treating it as an outage.

Base URL and authentication

All endpoints are relative to https://api.sms-activate.app/v1.

Authenticate with a bearer token: send Authorization: Bearer <your-api-key> on every request. Endpoints under /me require it; the catalog endpoints do not.

Endpoints (26)

GET /countries

List available countries with availability and price range

GET /numbers

List live numbers for a country

Per-number priceCents is populated only for an authenticated principal; anonymous callers get availability and the price range.

  • country (query, string, required) - ISO-2 country code (uppercase).
  • type (query, string, optional)
  • limit (query, integer, optional)
  • offset (query, integer, optional)

GET /numbers/search

Search numbers by pattern

  • country (query, string, required)
  • pattern (query, string, required)

GET /me

Get the authenticated profile

GET /me/balance

Get account balance

GET /me/transactions

List transactions (cursor-paginated)

  • limit (query, integer, optional)
  • cursor (query, string, optional)

GET, POST /me/numbers

GET: List your numbers

POST: Purchase a number

Send an Idempotency-Key header so retries never double-charge. Returns 201 with the number plus chargedCents and balanceCents.

  • Idempotency-Key (header, string, optional) - UUID; replays return the original result.

GET, PATCH, DELETE /me/numbers/{id}

GET: Get a number

  • id (path, string, required)

PATCH: Update a number

  • id (path, string, required)

DELETE: Release a number

  • id (path, string, required)

GET /me/numbers/{id}/sms

Inbound SMS for a number (cursor-paginated)

  • id (path, string, required)
  • limit (query, integer, optional)
  • cursor (query, string, optional)

GET /me/numbers/{id}/calls

Call logs for a number (cursor-paginated)

  • id (path, string, required)
  • limit (query, integer, optional)
  • cursor (query, string, optional)

GET /me/sms

Unified inbound SMS feed across your numbers (cursor-paginated)

  • limit (query, integer, optional)
  • cursor (query, string, optional)

GET /me/events

Server-Sent Events stream (25s heartbeat)

Emits inbound SMS, balance changes and number events. Connect with an EventSource-style client and the Authorization header.

GET, POST /me/watches

GET: List your number watches

Availability watches (digit pattern or exact number) with optional auto-buy.

POST: Create a watch

First watch is free; each additional active watch is billed monthly (debited from balance).

PATCH, DELETE /me/watches/{id}

PATCH: Pause/resume a watch, toggle auto-buy, or change the cap

  • id (path, string, required)

DELETE: Delete a watch

  • id (path, string, required)

GET /public/activations/catalog

List countries and services that can deliver a one-time code

  • country (query, string, optional) - ISO-2 country code.
  • service (query, string, optional) - Service slug the code is for.

POST /public/activations/quote

Price a one-time code before buying it

POST /public/activations/order

Buy a one-time code

Charges the account balance. Refusals are ANSWERS and arrive as the error envelope: insufficient_balance (409), no_numbers (409), price_exceeded (409), fx_unavailable (503), provider_failure (502). Treat none of them as an outage.

  • Idempotency-Key (header, string, required) - A stable key per intent. A retry with the same key replays the first answer instead of charging twice; a key built from a clock or a random is worse than none, because it looks like protection.

GET /public/activations

List your one-time code orders

  • limit (query, integer, optional)
  • cursor (query, string, optional)

GET, DELETE /public/activations/{id}

GET: Read one order, and the code once it has arrived

  • id (path, string, required)

DELETE: Cancel an order and get the money back

Money-safe by refusal: once a code has arrived, or once the no-SMS window has passed, this answers 409 wait_sms BEFORE anything upstream is touched. A cancel that can no longer refund is refused rather than performed.

  • id (path, string, required)

GET /public/activations/{id}/sms

Poll one order for its code

Poll every 5-15 seconds with a timeout. The code also appears on the order itself.

  • id (path, string, required)

GET /public/proxies/catalog

List priced bandwidth bundles

  • country (query, string, optional)
  • gateId (query, string, optional)

GET /public/proxies/gates

List gates and the bundle sizes each sells

GET /public/proxies/countries

List countries proxies can be placed in

POST /public/proxies/quote

Price one published offer

Takes the `key` from the catalog and nothing else that could move the price. Never send a price of your own.

GET, POST /public/proxies

GET: List your proxies

Never carries `connection`: credentials are returned on order and on GET by id only.

POST: Buy a bandwidth bundle

Charges the account balance and answers with the credentials. `insufficient_balance` is 402 on this lane and 409 on the product lanes; branch on the code.

  • Idempotency-Key (header, string, required) - A stable key per intent. A retry with the same key replays the first answer instead of charging twice; a key built from a clock or a random is worse than none, because it looks like protection.

GET /public/proxies/{id}

Read one proxy, with its credentials

  • id (path, integer, required)

Response objects

The API returns 12 object types: Error, Activation, ProxyOffer, ProxyGate, ProxyConnection, Proxy, Country, Number, Watch, Sms, SmsFeed, Me.