API Docs

DiddySMS API: rent numbers, receive SMS codes

Last updated

Rent USA numbers and receive SMS verification codes programmatically.

Base URLhttps://api.diddysms.com/v1

HTTPS only, with TLS 1.2 or newer (TLS 1.3 preferred); TLS 1.0 and 1.1 are refused. Never call the API over plain http://: your API key would travel unencrypted before the redirect.

Authentication

All API requests require an API key passed in the Authorization header as a Bearer token.

curl https://api.diddysms.com/v1/balance \
  -H "Authorization: Bearer dsk_your_api_key_here"

API keys start with dsk_ and are 52 characters long. Keys are shown once when generated and cannot be retrieved later.

A missing, malformed, unknown or revoked key returns 401 with the error code UNAUTHORIZED. The message is one of "Missing API key", "Invalid API key format", "Invalid or revoked API key".

{
  "detail": {
    "error": {
      "code": "UNAUTHORIZED",
      "message": "Missing API key"
    }
  }
}

Getting your API key

  1. Create a DiddySMS account or sign in if you already have one.
  2. Open Account → API keys and click Generate API key.
  3. Copy the key immediately — it won't be shown again.
Note: Generating a new key automatically revokes any previous key on your account.

POST /v1/auth/api-key

Rotate your API key: get a new key and revoke the current one

Authenticate with your current key to receive a new one. Every previous key on the account is revoked at once, and the new key appears only in this response. Your first key is created in your account, as above.

Request body for rotating a key (optional)

NameTypeRequiredDescription
namestringNoLabel for the key, up to 100 characters (default: "default")
curl -X POST https://api.diddysms.com/v1/auth/api-key \
  -H "Authorization: Bearer dsk_your_api_key_here"

Response (201 Created)

{
  "api_key": "dsk_your_new_api_key_here",
  "prefix": "dsk_your_new",
  "name": "default",
  "message": "Store this key securely. It will not be shown again."
}

Quickstart

Rent a number and read its SMS code in three API calls, and cancel it with a fourth if you stop waiting.

1. List available services

curl https://api.diddysms.com/v1/services \
  -H "Authorization: Bearer dsk_your_api_key_here"
import requests

resp = requests.get(
    "https://api.diddysms.com/v1/services",
    headers={"Authorization": "Bearer dsk_your_api_key_here"},
)
services = resp.json()["services"]
const resp = await fetch("https://api.diddysms.com/v1/services", {
  headers: { Authorization: "Bearer dsk_your_api_key_here" },
});
const { services } = await resp.json();

2. Buy a number

curl -X POST https://api.diddysms.com/v1/orders \
  -H "Authorization: Bearer dsk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"service": "google"}'
resp = requests.post(
    "https://api.diddysms.com/v1/orders",
    headers={"Authorization": "Bearer dsk_your_api_key_here"},
    json={"service": "google"},
)
order = resp.json()["order"]
print(order["phone_number"])  # 2025551234
const resp = await fetch("https://api.diddysms.com/v1/orders", {
  method: "POST",
  headers: {
    Authorization: "Bearer dsk_your_api_key_here",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ service: "google" }),
});
const { order } = await resp.json();
console.log(order.phone_number); // 2025551234

3. Check for the SMS code

Poll every 3 seconds until sms_code is set. Stop when status is no longer active: an order with no code expires when its code window ends and its price is credited back to your balance. If you stop waiting before that, cancel it (step 4). A 429, 502, 503 or 504 is temporary (for example a short maintenance): wait and poll again. For unattended programs, see retries and maintenance.

curl https://api.diddysms.com/v1/orders/200 \
  -H "Authorization: Bearer dsk_your_api_key_here"

# Once the code has arrived:
# { "order": { "id": 200, "status": "completed", "sms_code": "123456", ... } }
import time

HEADERS = {"Authorization": "Bearer dsk_your_api_key_here"}
GIVE_UP_AFTER = 120  # seconds: your own limit (cancel works from 60 s)
started = time.monotonic()

while True:
    resp = requests.get(
        f"https://api.diddysms.com/v1/orders/{order['id']}", headers=HEADERS, timeout=30
    )
    if resp.status_code in (429, 502, 503, 504):
        # Temporary (for example a short maintenance): wait and poll again.
        time.sleep(int(resp.headers.get("Retry-After", 3)))
        continue
    data = resp.json()["order"]
    if data["sms_code"]:
        print(f"Code: {data['sms_code']}")
        break
    if data["status"] != "active":
        # Expired (no code in time) or cancelled; the price is credited back.
        print(f"No code: order {data['status']}")
        break
    if time.monotonic() - started > GIVE_UP_AFTER:
        # Stop waiting: cancel so the price comes back now (step 4).
        cancel = requests.post(
            f"https://api.diddysms.com/v1/orders/{order['id']}/cancel", headers=HEADERS
        )
        if cancel.status_code == 200:
            print("No code: order cancelled and credited back")
            break
        # 400: the code arrived first, the next poll returns it.
        # 503: the cancel is tried again on the next pass.
    time.sleep(3)
const headers = { Authorization: "Bearer dsk_your_api_key_here" };
const GIVE_UP_AFTER_MS = 120_000; // your own limit (cancel works from 60 s)

const poll = async (orderId) => {
  const started = Date.now();
  while (true) {
    const resp = await fetch(`https://api.diddysms.com/v1/orders/${orderId}`, { headers });
    if ([429, 502, 503, 504].includes(resp.status)) {
      // Temporary (for example a short maintenance): wait and poll again.
      const wait = Number(resp.headers.get("Retry-After")) || 3;
      await new Promise((r) => setTimeout(r, wait * 1000));
      continue;
    }
    const { order } = await resp.json();
    if (order.sms_code) return order.sms_code;
    // Expired (no code in time) or cancelled; the price is credited back.
    if (order.status !== "active") return null;
    if (Date.now() - started > GIVE_UP_AFTER_MS) {
      // Stop waiting: cancel so the price comes back now (step 4).
      const cancel = await fetch(
        `https://api.diddysms.com/v1/orders/${orderId}/cancel`,
        { method: "POST", headers }
      );
      if (cancel.status === 200) return null;
      // 400: the code arrived first, the next poll returns it.
      // 503: the cancel is tried again on the next pass.
    }
    await new Promise((r) => setTimeout(r, 3000));
  }
};

4. Cancel if you stop waiting

If your flow stops waiting for the code (the app never sent it, your user left, or you give up), cancel the order instead of leaving it to expire. You can cancel from 60 seconds after purchase. The price comes back to your balance at once, the order stops counting toward your 100 active rentals, and a code that arrives later can no longer complete it and charge you. If the answer is 400 ORDER_ERROR, the code arrived first: read sms_code from the order.

curl -X POST https://api.diddysms.com/v1/orders/200/cancel \
  -H "Authorization: Bearer dsk_your_api_key_here"

Rate Limits

Rate limits are applied per API key. Exceeding a limit returns 429 Too Many Requests.

Business rules: Maximum 100 concurrent active rentals per account. Orders cannot be cancelled within 60 seconds of purchase. An order that has not received a code expires when its code window ends and its price is credited back to your balance automatically.

Response Format

All responses are JSON. Successful responses return the requested data directly.

{
  "order": {
    "id": 200,
    "service": "google",
    "service_display_name": "Google",
    "phone_number": "2025551234",
    "status": "active",
    "price": 1.50,
    "carrier": null,
    "requested_carrier": null,
    "sms_code": null,
    "sms_message": null,
    "messages": [],
    "expires_at": "2026-02-26T22:49:00Z",
    "created_at": "2026-02-26T22:40:00Z",
    "completed_at": null
  }
}

Error responses use a consistent envelope with an error code and message. See the Errors reference for all codes.

{
  "detail": {
    "error": {
      "code": "INSUFFICIENT_BALANCE",
      "message": "Need $1.50, have $0.00"
    }
  }
}