API Docs

API Error Codes

Last updated

All error responses follow a consistent format with an error code and human-readable message.

Error Format

{
  "detail": {
    "error": {
      "code": "ERROR_CODE",
      "message": "Human-readable description of what went wrong"
    }
  }
}

Authentication Errors

A missing, malformed, unknown or revoked API key returns 401 with the code UNAUTHORIZED. The message is one of "Missing API key", "Invalid API key format", "Invalid or revoked API key". Make sure your key starts with dsk_ and is passed as a Bearer token.

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

Rate-Limit Errors

A request over its rate limit returns 429 with a different body: a plain error string and no error code. A 429 that carries MAX_RENTALS means the concurrent rental limit instead.

{
  "error": "Rate limit exceeded: 60 per 1 minute"
}

Validation Errors

A request with missing or malformed fields returns 422 with the standard FastAPI body, where detail is a list of problems.

{
  "detail": [
    {
      "type": "missing",
      "loc": ["body", "service"],
      "msg": "Field required",
      "input": {}
    }
  ]
}

Retries and Maintenance

Programs that buy numbers unattended should handle short outages, such as a maintenance, without losing orders or buying twice.

  • Retry these: 429 with a rate-limit body, 502, 503, 504, 520 to 524, and a network error or timeout. They are temporary: the same request works again shortly.
  • Wait before each retry: If the response has a Retry-After header, wait that many seconds. Otherwise wait longer each time (2, 4, 8, 16, 32, then 60 seconds) and add up to one random second, so your clients do not all retry at the same instant. Give up after about 10 minutes and alert yourself.
  • Do not retry these unchanged: 400, 401, 402, 403, 404, 409, 422 and 429 MAX_RENTALS. Repeating the same request gives the same answer: fix the cause first (top up, pick another service, wait for an order to finish).
  • A failed buy may still have gone through: After a timeout, a network error or a 502, 504 or 52x on POST /v1/orders or a re-rent, the order may exist. Before buying again, list your newest orders (GET /v1/orders) and look for one for that service created in the last minutes. A 503 means no order was kept: MAINTENANCE stopped the request before anything happened, and after SERVICE_UNAVAILABLE any charge is credited back automatically.
  • Maintenance: 503 MAINTENANCE usually pauses only new purchases and re-rents, for about a minute: reading orders, cancelling and code delivery keep working, except a few seconds that can answer 502 while the API restarts. Rarely the whole API is paused for a few minutes. Your balance and open orders are safe either way, and codes that arrive meanwhile are on the order when you read it again.
  • Keep your orders through an outage: Store each order id as soon as you get it, and keep polling with these waits instead of dropping the order. An order with no code still expires and is credited back on its own.
  • Set a client timeout: At least 30 seconds per request, and 60 seconds for buys and re-rents, which wait for a number.

During a maintenance the API answers 503 with a Retry-After header (seconds) and this body:

{
  "detail": {
    "error": {
      "code": "MAINTENANCE",
      "message": "New purchases are paused for a short maintenance. Numbers you already rented still receive their codes. Your balance is safe. Please try again in a few minutes."
    }
  }
}

A retry helper in Python that follows these rules:

import random
import time
from datetime import datetime

import requests

API = "https://api.diddysms.com/v1"
HEADERS = {"Authorization": "Bearer dsk_your_api_key_here"}
TEMPORARY = {429, 502, 503, 504, 520, 521, 522, 523, 524}


def is_temporary(resp):
    if resp is None:  # network error or timeout
        return True
    if resp.status_code == 429 and "MAX_RENTALS" in resp.text:
        return False  # not a rate limit: an order has to finish first
    return resp.status_code in TEMPORARY


def wait(resp, attempt):
    """Retry-After when the API sends it, else 2, 4, 8 ... 60 s, plus jitter."""
    header = resp.headers.get("Retry-After") if resp is not None else None
    delay = int(header) if header and header.isdigit() else min(60, 2 ** (attempt + 1))
    time.sleep(delay + random.random())


def request(method, path, **kwargs):
    """Reads, cancel and complete: safe to repeat."""
    for attempt in range(12):
        try:
            resp = requests.request(
                method, f"{API}{path}", headers=HEADERS, timeout=30, **kwargs
            )
        except requests.RequestException:
            resp = None
        if not is_temporary(resp):
            return resp
        wait(resp, attempt)
    raise RuntimeError(f"{method} {path}: still failing, try again later")


def buy(service):
    """A buy is not safe to repeat blindly: look for the order first."""
    started = time.time()
    for attempt in range(12):
        try:
            resp = requests.post(
                f"{API}/orders", headers=HEADERS, json={"service": service}, timeout=60
            )
        except requests.RequestException:
            resp = None
        if resp is not None and resp.status_code == 201:
            return resp.json()["order"]
        if not is_temporary(resp):
            resp.raise_for_status()  # 4xx: fix the cause, do not repeat
        if resp is None or resp.status_code not in (429, 503):
            # Timeout, 502, 504 or 52x: the order may exist. (If you run buys
            # in parallel, also check another buy has not claimed it.)
            for order in request("GET", "/orders").json()["orders"]:
                created = datetime.fromisoformat(order["created_at"].replace("Z", "+00:00"))
                if order["service"] == service and created.timestamp() >= started - 5:
                    return order
        wait(resp, attempt)
    raise RuntimeError(f"buy {service}: still failing, try again later")

Error Code Reference

CodeHTTP StatusDescriptionWhat to do
UNAUTHORIZED401The API key is missing, malformed, unknown or revokedSend your current key as a Bearer token. Generating a new key revokes the previous one.
ACCOUNT_BANNED403The account has been permanently bannedThe key cannot be used. Open a support ticket if you think this is a mistake.
ACCOUNT_FROZEN403The account is temporarily frozenOpen a support ticket.
ACCOUNT_MERGED403The account has been merged into another accountUse an API key from the account it was merged into.
INSUFFICIENT_BALANCE402Your balance is too low for this purchaseTop up your balance, then retry.
NOT_FOUND404The service or order does not exist, or the order belongs to another accountCheck the service name or order ID.
ORDER_ERROR400Invalid order operation, e.g. cancelling or completing an order that is no longer activeFetch the order to check its status before trying again.
COOLDOWN400Cannot cancel within 60 seconds of purchaseWait until the time in the message has passed, then cancel again.
OUT_OF_STOCK409No numbers available right now: for the requested carrier or the service (buy), or for that same number (re-rent). On a buy, detail.error.scope says which: "carrier" or "service". Nothing is charged.scope "carrier": retry without carrier, or send carrier_fallback "any". scope "service": retry later or pick another service. For a re-rent, buy a new number.
MAX_RENTALS429Maximum 100 concurrent active rentals reachedWait for an active order to complete or expire, or cancel one, then retry.
SERVICE_UNAVAILABLE503Temporarily unable to process the request. A failed buy or re-rent keeps no order; any charge is credited back automatically.Retry after a few seconds, waiting longer each time (see Retries and maintenance).
MAINTENANCE503A short maintenance. Usually only new purchases and re-rents are paused, for about a minute; rarely the whole API, for a few minutes. Nothing was done and nothing was charged.Wait the seconds in the Retry-After header, then retry. Keep your open orders: they keep receiving codes.

HTTP Status Summary

StatusMeaning
200Success
201Created (new order, re-rented order or API key)
400Invalid operation (ORDER_ERROR, COOLDOWN)
401Missing or invalid API key (UNAUTHORIZED)
402Insufficient balance
403Account banned, frozen or merged
404Service or order not found
409Out of stock
422Request validation failed (missing or malformed fields)
429Rate limit or concurrent rental limit exceeded
502The API was unreachable for a moment (for example while it restarts); the body may not be JSON. Retry; after a buy, check your orders first
503Temporarily unavailable (SERVICE_UNAVAILABLE) or a short maintenance (MAINTENANCE); retry
504The API did not answer in time; the body may not be JSON. Retry; after a buy, check your orders first