SimSms
EN

Language

English EN

More languages are on the way.

1 Select service 1,289 services in stock

Choose a service

2 Select country 145 countries

Choose a country

3 Network & price

Pick a service and a country — in either order. The price and the network appear here.

For developers

API documentation

Read your balance and the live price of any service in any country from your own code. One bearer key, JSON over HTTPS, no SDK to install.

Base address

https://simsms.com/api/v1
Create a key

Getting started #

Everything is JSON over HTTPS, authenticated with a single key you create yourself. No SDK to install, no contract to sign, no sandbox to request — a key and curl are enough.

  1. 1 Create a key Open the account menu, then API key, then Create a key. It is shown once: we store only its fingerprint, so it cannot be looked up again afterwards.
  2. 2 Send it as a bearer token Every request carries an Authorization header. There is no other way in — no key in the query string, no cookie, no session.
  3. 3 Read the answer Every response is a JSON object with an ok field. When ok is false, an error field says why in a stable machine-readable string.

Authentication #

One key per account. Sending it any other way than the header below is not supported — a key in a query string ends up in server logs, browser history and referrer headers, so it is refused rather than quietly accepted.

request
Authorization: Bearer sk_your_key_here

A key can spend money It reads the balance today, and it is the same key that will order numbers when ordering exists. Treat it like a password: out of source control, out of screenshots, replaced rather than shared. Replacing a key closes the old one immediately.

Conventions #

Base address https://simsms.com/api/v1
Transport HTTPS only. Plain HTTP is redirected, and the redirect does not carry your header.
Format JSON in, JSON out. Every response is an object, never a bare array.
Success HTTP 200 with "ok": true.
Failure A 4xx or 5xx code with "ok": false and a stable "error" string.
Money Numbers in US dollars, never strings, never cents. 0.84 means 84 cents.
Unknown fields New fields may be added to any response. Ignore what you do not know rather than failing on it.

Errors #

The error string is the part to branch on. The sentence next to it is for you, not for your code — it may be reworded; the string will not.

HTTP error When
401 missing_key No Authorization header, or it is not a bearer token.
401 bad_key The key is unknown, or it was closed or replaced.
400 unknown_service No service by that code. Codes come from the catalogue, not from the display name.
404 no_stock That service and country pair has no number in stock right now. It is a live figure — it can come back.
429 rate_limited Too many requests. A Retry-After header says how many seconds to wait.

Rate limits #

600 requests per minute, counted per account rather than per address. A key is meant to run from a server — one address — so a per-address limit would punish normal use and let a stolen key run freely somewhere else. Over the limit you get 429 and a Retry-After header in seconds.

Endpoints #

Account and catalogue reads. Each route lists every parameter it takes, every field it returns, and every error it can answer with — no undocumented behaviour.

Read your balance #

GET /api/v1/balance

What the account holds right now, and how many codes that buys at the cheapest price currently in the catalogue.

Response fields

Field Type Description
ok boolean Always true on a 200.
balance number Dollars available, rounded to the cent.
currency string Always "USD". Present so you never have to assume it.
codes_at_cheapest integer How many codes the balance buys at the cheapest price in the catalogue. A rough gauge, not a quote — null if the catalogue is empty.
cheapest_code number That cheapest price, so you can compute the gauge yourself against another figure.

Request

request
curl -s "https://simsms.com/api/v1/balance" \
  -H "Authorization: Bearer $SIMSMS_KEY"

Response

200 OK
{
  "ok": true,
  "balance": 42.5,
  "currency": "USD",
  "codes_at_cheapest": 425,
  "cheapest_code": 0.1
}

Can answer with missing_key bad_key rate_limited

Price one service in one country #

GET /api/v1/pricing?service={service}&country={country}

The live price and the live stock for a single pair. Both figures move — the price follows the cheapest network carrying that service in that country, and the stock is what the network reports right now.

Parameters

Parameter Description
service required Service code, for example telegram. Lower case, from the catalogue.
country optional Country code, for example england. Leave it out to get every country at once — see below.

Response fields

Field Type Description
ok boolean Always true on a 200.
service string The service code you asked for, echoed back.
country string The country code you asked for, echoed back.
price number Dollars for one code, at the cheapest network that has it.
stock integer Numbers reported available right now. It is a live figure and it moves.

Request

request
curl -s "https://simsms.com/api/v1/pricing?service=telegram&country=england" \
  -H "Authorization: Bearer $SIMSMS_KEY"

Response

200 OK
{
  "ok": true,
  "service": "telegram",
  "country": "england",
  "price": 0.84,
  "stock": 61213
}

Can answer with missing_key bad_key unknown_service no_stock rate_limited

Price one service everywhere #

GET /api/v1/pricing?service={service}

Drop the country and you get every country that carries the service. The order is the site's own: what actually delivers first, cheapest within that — not raw price, which would put a two-cent number nobody receives a code from at the top.

Parameters

Parameter Description
service required Service code, for example telegram.

Response fields

Field Type Description
ok boolean Always true on a 200.
service string The service code you asked for.
name string Its display name, for example "Telegram".
countries array One object per country, in the order described above.
countries[].country string Country code, to feed back into the pair call.
countries[].name string Its English name.
countries[].price number Dollars for one code.
countries[].stock integer Numbers available right now.

Request

request
curl -s "https://simsms.com/api/v1/pricing?service=telegram" \
  -H "Authorization: Bearer $SIMSMS_KEY"

Response

200 OK
{
  "ok": true,
  "service": "telegram",
  "name": "Telegram",
  "countries": [
    { "country": "england", "name": "United Kingdom", "price": 0.84, "stock": 61213 },
    { "country": "poland",  "name": "Poland",         "price": 0.91, "stock": 22140 }
  ]
}

Can answer with missing_key bad_key unknown_service rate_limited

Full examples #

Complete, runnable programs rather than one-line fragments — including the two things fragments always leave out: handling the error shape, and backing off on 429.

Shell #

Find the cheapest country for a service, then check the balance covers it.

cheapest.sh
#!/usr/bin/env bash
set -euo pipefail
: "${SIMSMS_KEY:?export your key first}"
API="https://simsms.com/api/v1"

auth=(-H "Authorization: Bearer $SIMSMS_KEY")

# The catalogue is already ordered: the first country is the one to take.
best=$(curl -sf "${API}/pricing?service=telegram" "${auth[@]}" \
        | jq -r ".countries[0] | \"\(.country) \(.price)\"")
country=${best% *}
price=${best#* }

balance=$(curl -sf "${API}/balance" "${auth[@]}" | jq -r .balance)

# ⚠️ Compare as numbers, not as strings: "9.5" > "10" is true in a string sort.
if awk "BEGIN{exit !($balance >= $price)}"; then
  echo "ok: $country at \$$price, balance \$$balance"
else
  echo "top up first: need \$$price, have \$$balance" >&2
  exit 1
fi

JavaScript #

The same thing in Node, with the error shape handled properly.

pricing.mjs
const API = "https://simsms.com/api/v1";
const key = process.env.SIMSMS_KEY;

async function call(path) {
  const r = await fetch(API + path, {
    headers: { Authorization: `Bearer ${key}` },
  });
  const body = await r.json();
  // A non-2xx always carries { ok:false, error }. Branch on `error`, never on
  // the sentence — the string is stable, the wording is not.
  if (!r.ok || !body.ok) {
    if (body.error === "rate_limited") {
      const wait = Number(r.headers.get("Retry-After") || 60);
      await new Promise((s) => setTimeout(s, wait * 1000));
      return call(path);
    }
    throw new Error(body.error ?? `http_${r.status}`);
  }
  return body;
}

const { countries } = await call("/pricing?service=telegram");
const { balance }   = await call("/balance");

const best = countries[0];
console.log(`${best.name}: $${best.price} (${best.stock} in stock)`);
console.log(balance >= best.price ? "balance covers it" : "top up first");

Python #

And in Python, with the same retry on 429.

pricing.py
import os, time, requests

API = "https://simsms.com/api/v1"
S = requests.Session()
S.headers["Authorization"] = f"Bearer {os.environ['SIMSMS_KEY']}"

def call(path):
    r = S.get(API + path, timeout=20)
    body = r.json()
    if not r.ok or not body.get("ok"):
        if body.get("error") == "rate_limited":
            time.sleep(int(r.headers.get("Retry-After", 60)))
            return call(path)
        raise RuntimeError(body.get("error", f"http_{r.status_code}"))
    return body

countries = call("/pricing?service=telegram")["countries"]
balance   = call("/balance")["balance"]

best = countries[0]
print(f"{best['name']}: ${best['price']} ({best['stock']} in stock)")
print("balance covers it" if balance >= best["price"] else "top up first")

Roadmap #

The API covers account and catalogue reads today. The ordering surface is next, and it will sit under the same base address and take the same key — nothing you build now will need rewriting.

Order a number Planned Planned. Ordering runs through the site today.
Poll for the code Planned Planned, alongside ordering.
Release a number Planned Planned, alongside ordering.
Rental over API Planned Planned.
Webhooks Planned Planned, once there are order events to deliver.

This page grows with them: the sections below stay where they are, and new ones are added under Endpoints.

Changelog #

  • First public version: keys, GET /balance, GET /pricing.