KDEFY SMS API
TXT JSON Get my tokenToken
v1.0

KDEFY SMS API

REST API to buy virtual numbers and receive SMS. It uses the same balance and activations as your account on the website.

Base URL
https://kdefy-sms.com/api/v1
AuthenticationX-API-Key
FormatJSON
Limit120 req/min
API prices are lower than website prices
Buying numbers through the API costs less than buying through the website. If you saw a higher price on the website, that is expected: website prices include the margin for using the interface, while the API charges a reduced rate for developers. The exact price of each purchase is always the one returned by the offers and services routes.

All routes

GET /balanceAccount balance and totals.
GET /profileUsername, balance and creation date.
GET /countriesList of countries.
GET /servicesServices of a country, with stock and starting price.
GET /operatorsCarriers of a country.
GET /countries/topCountries with stock for a service, in the provider’s order, with your starting price.
GET /offersPrice tiers and stock of a service in a country.
GET POST /number/getBuys one number, or several with quantity.
GET /number/statusActivation state and received codes.
GET /number/reactivate/optionsShows how much it costs to reactivate the number of a finished activation that received a code.
GET POST /number/reactivateReuses the number of a finished activation to receive a new code.
GET POST /number/cancelCancels and refunds the amount.
GET POST /number/finishEnds the activation after the code.
GET /activationsYour activations, including those made on the website, newest first.
GET /statsTotal activations, success rate and spending by service and country over a period.
GET /transactionsTop-ups, purchases and refunds.

These values apply to every test on the page. The token stays only in this tab. Buying really charges your balance.

Getting started

Authentication

Generate the token under API in the site menu and send it in the X-API-Key header on every request. Authorization: Bearer and ?api_key= also work, but the query string ends up in proxy logs and browser history. Generating a new token invalidates the previous one immediately. Never use the token in front-end code.

curl "https://kdefy-sms.com/api/v1/balance" -H "X-API-Key: SEU_TOKEN"
curl "https://kdefy-sms.com/api/v1/balance" -H "Authorization: Bearer SEU_TOKEN"
curl "https://kdefy-sms.com/api/v1/balance?api_key=SEU_TOKEN"

Responses and language

  • Every response is JSON. Success: { "status": "success", ... }. Error: { "status": "error", "error": "code", "message": "text" }.
  • Decide using the error field. It is stable. message is human-readable text and may change.
  • message and state_label follow the account language (pt, en, es, de or fr). To force one: ?lang=pt, ?lang=en, ?lang=es, ?lang=de or ?lang=fr.
  • JSON keys and state values do not change with the language.
  • Amounts in BRL with a decimal point. Dates in ISO 8601 (UTC).
Concepts

States

stateMeaning
waitingWaiting for the first code.
code_receivedCode received. New codes are added to codes while the activation is open.
completedCompleted by you (/number/finish).
cancelledCancelled. Amount refunded.
expiredNo code within 20 min. Amount refunded automatically.

Limits

  • 120 requests per minute per account.
  • Purchase: up to 50 numbers per request and 50 per minute. Above that: limite_de_numeros.
  • Status: poll every 5 s. Polling faster does not make the code arrive sooner.
Guides

Purchase flow

  1. GET /services?country=73: the country’s services and starting price.
  2. GET /offers?country=73&service=wa (optional): price tiers and stock.
  3. GET /number/get?country=73&service=wa&request_id=...: buys and returns phone and id.
  4. GET /number/status?id=ID every 5 s, until code is filled in.
  5. With a code: GET /number/finish?id=ID. Without a code: GET /number/cancel?id=ID (available 2 min after purchase) and the amount returns to your balance.

Waiting for the code

Poll /number/status every 5 s and stop when code is filled in or state becomes cancelled, expired or completed.

  • Set a maximum timeout in your code (20 min).
  • With several numbers, use /activations?state=active. One call returns all active ones.
  • An activation can receive more than one code. Read codes[].

Batch purchase

quantity=N buys N numbers in one call, up to 50. A partial purchase is normal: if stock or balance runs out midway, the rest is not charged.

  • The response includes numbers[], requested, purchased and failed.
  • errors lists the reason for any failures.
  • Limit of 50 numbers per minute per account.
curl "https://kdefy-sms.com/api/v1/number/get?country=73&service=wa&quantity=3&request_id=order-0001" \
  -H "X-API-Key: $KDEFY_API_KEY"

Idempotency (request_id)

Send request_id on every purchase. If it times out and you do not know whether the purchase went through, repeat the same call with the same request_id. The API returns the original response, with the Idempotent-Replay: true header, and does not charge again.

  • New purchase, new request_id.
  • 8 to 80 characters (letters, numbers, _ . : -). The Idempotency-Key header also works.
  • The key lasts 24 h and is only saved when the purchase succeeded. A failed purchase can be retried.
  • Same key with different parameters: request_id_reutilizada.

Complete example

Buy, wait for the code and finish. If the code does not arrive, cancel. The purchase uses request_id, so repeating the call does not duplicate it.

// Node 18+. Run with: KDEFY_API_KEY=your_token node kdefy.mjs
import { randomUUID } from "node:crypto";

const BASE = "https://kdefy-sms.com/api/v1";
const KEY = process.env.KDEFY_API_KEY;
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

async function api(path, params = {}) {
  const qs = new URLSearchParams(params).toString();
  const res = await fetch(`${BASE}${path}${qs ? "?" + qs : ""}`, {
    headers: { "X-API-Key": KEY },
    signal: AbortSignal.timeout(20000),
  });
  const body = await res.json();
  if (body.status !== "success") {
    const err = new Error(body.message);
    err.code = body.error;
    err.http = res.status;
    err.retryIn = body.retry_in_seconds;
    throw err;
  }
  return body;
}

async function buy(country, service) {
  const requestId = randomUUID(); // one per purchase
  for (let attempt = 1; attempt <= 4; attempt++) {
    try {
      return await api("/number/get", { country, service, request_id: requestId });
    } catch (e) {
      const transient = e.name === "TimeoutError" || e.name === "TypeError" || e.http === 502 || e.code === "requisicao_em_andamento";
      if (!transient || attempt === 4) throw e;
      await sleep(2000 * attempt); // repete com a mesma request_id
    }
  }
}

async function waitForCode(id, maxMinutes = 20) {
  const deadline = Date.now() + maxMinutes * 60_000;
  while (Date.now() < deadline) {
    const s = await api("/number/status", { id });
    if (s.code || ["cancelled", "expired", "completed"].includes(s.state)) return s;
    await sleep(5000);
  }
  return null;
}

const number = await buy(73, "wa");
console.log("Number:", number.phone);

const result = await waitForCode(number.id);
if (result?.code) {
  console.log("Code:", result.code);
  await api("/number/finish", { id: number.id });
} else {
  try {
    await api("/number/cancel", { id: number.id });
  } catch (e) {
    if (e.code !== "cancelamento_muito_cedo") throw e;
    await sleep((e.retryIn + 1) * 1000);
    await api("/number/cancel", { id: number.id });
  }
  console.log("Cancelado e reembolsado.");
}
# Python 3.9+. pip install requests
# Run with: KDEFY_API_KEY=your_token python kdefy.py
import os, time, uuid, requests

BASE = "https://kdefy-sms.com/api/v1"
HEADERS = {"X-API-Key": os.environ["KDEFY_API_KEY"]}

class ApiError(Exception):
    def __init__(self, body, http):
        super().__init__(body.get("message"))
        self.code = body.get("error")
        self.http = http
        self.retry_in = body.get("retry_in_seconds")

def api(path, **params):
    r = requests.get(BASE + path, params=params, headers=HEADERS, timeout=20)
    body = r.json()
    if body.get("status") != "success":
        raise ApiError(body, r.status_code)
    return body

def buy(country, service):
    request_id = str(uuid.uuid4())  # one per purchase
    for attempt in range(1, 5):
        try:
            return api("/number/get", country=country, service=service, request_id=request_id)
        except requests.RequestException:
            pass
        except ApiError as e:
            if not (e.http == 502 or e.code == "requisicao_em_andamento"):
                raise
        if attempt == 4:
            raise RuntimeError("could not buy right now")
        time.sleep(2 * attempt)  # repete com a mesma request_id

def wait_for_code(activation_id, max_minutes=20):
    deadline = time.time() + max_minutes * 60
    while time.time() < deadline:
        s = api("/number/status", id=activation_id)
        if s["code"] or s["state"] in ("cancelled", "expired", "completed"):
            return s
        time.sleep(5)
    return None

number = buy(73, "wa")
print("Number:", number["phone"])

result = wait_for_code(number["id"])
if result and result["code"]:
    print("Code:", result["code"])
    api("/number/finish", id=number["id"])
else:
    try:
        api("/number/cancel", id=number["id"])
    except ApiError as e:
        if e.code != "cancelamento_muito_cedo":
            raise
        time.sleep((e.retry_in or 5) + 1)
        api("/number/cancel", id=number["id"])
    print("Cancelado e reembolsado.")
<?php
const BASE = "https://kdefy-sms.com/api/v1";

function api(string $path, array $params = []): array {
    $ch = curl_init(BASE . $path . ($params ? "?" . http_build_query($params) : ""));
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 20,
        CURLOPT_HTTPHEADER => ["X-API-Key: " . getenv("KDEFY_API_KEY")],
    ]);
    $raw = curl_exec($ch);
    if ($raw === false) throw new RuntimeException("rede: " . curl_error($ch));
    $body = json_decode($raw, true);
    if (($body["status"] ?? null) !== "success") {
        $e = new RuntimeException($body["error"] ?? "erro_desconhecido");
        $e->retryIn = $body["retry_in_seconds"] ?? 0;
        throw $e;
    }
    return $body;
}

$requestId = bin2hex(random_bytes(16)); // one per purchase
$n = api("/number/get", ["country" => 73, "service" => "wa", "request_id" => $requestId]);
echo "Number: {$n['phone']}\n";

$deadline = time() + 20 * 60;
while (time() < $deadline) {
    $s = api("/number/status", ["id" => $n["id"]]);
    if (!empty($s["code"]) || in_array($s["state"], ["cancelled", "expired", "completed"], true)) break;
    sleep(5);
}

if (!empty($s["code"])) {
    echo "Code: {$s['code']}\n";
    api("/number/finish", ["id" => $n["id"]]);
} else {
    api("/number/cancel", ["id" => $n["id"]]);
    echo "Cancelado e reembolsado.\n";
}
# Ruby 3+. Run with: KDEFY_API_KEY=your_token ruby kdefy.rb
require "net/http"
require "json"
require "securerandom"

BASE = "https://kdefy-sms.com/api/v1"

def api(path, params = {})
  uri = URI(BASE + path)
  uri.query = URI.encode_www_form(params) unless params.empty?
  req = Net::HTTP::Get.new(uri, "X-API-Key" => ENV.fetch("KDEFY_API_KEY"))
  res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", read_timeout: 20) { |h| h.request(req) }
  body = JSON.parse(res.body)
  raise "#{body['error']}: #{body['message']}" unless body["status"] == "success"
  body
end

n = api("/number/get", country: 73, service: "wa", request_id: SecureRandom.uuid) # one per purchase
puts "Number: #{n['phone']}"

deadline = Time.now + 20 * 60
s = nil
while Time.now < deadline
  s = api("/number/status", id: n["id"])
  break if s["code"] || %w[cancelled expired completed].include?(s["state"])
  sleep 5
end

if s && s["code"]
  puts "Code: #{s['code']}"
  api("/number/finish", id: n["id"])
else
  api("/number/cancel", id: n["id"])
  puts "Cancelado e reembolsado."
end
#!/usr/bin/env bash
# Requires curl and jq. Run with: KDEFY_API_KEY=your_token bash kdefy.sh
set -euo pipefail
BASE="https://kdefy-sms.com/api/v1"

api() { # api /rota chave=valor ...
  local path="$1"; shift
  local args=(); for kv in "$@"; do args+=(--data-urlencode "$kv"); done
  curl -sS --max-time 20 -G "$BASE$path" -H "X-API-Key: $KDEFY_API_KEY" "${args[@]}"
}

num=$(api /number/get country=73 service=wa "request_id=$(uuidgen)") # one per purchase
[ "$(jq -r .status <<<"$num")" = success ] || { jq . <<<"$num"; exit 1; }
id=$(jq -r .id <<<"$num")
echo "Number: $(jq -r .phone <<<"$num")"

for _ in $(seq 240); do # 240 x 5 s = 20 min
  st=$(api /number/status "id=$id")
  code=$(jq -r '.code // empty' <<<"$st")
  state=$(jq -r .state <<<"$st")
  [ -n "$code" ] && break
  case "$state" in cancelled|expired|completed) break ;; esac
  sleep 5
done

if [ -n "${code:-}" ]; then
  echo "Code: $code"
  api /number/finish "id=$id" >/dev/null
else
  api /number/cancel "id=$id" >/dev/null
  echo "Cancelado e reembolsado."
fi

Best practices

  • Keep the token in an environment variable. Never in code, front-end or a repository.
  • request_id on every purchase. Repeating the call? Same request_id.
  • Polling every 5 s, with a maximum timeout.
  • Several numbers: /activations?state=active instead of one /number/status per activation.
  • Handle error, not message.
  • 429: wait retry_in_seconds. 502: retry with increasing backoff (2 s, 4 s, 6 s).
  • Use max_price_brl as a cap. Price and stock change, so check /offers before large batches.
  • Check /balance before large batches.
  • Keep request_id and the activation id in your logs.
  • Only cancel what you will not use. There is no refund after the first code.
Route reference

Account

GET
/api/v1/balance

Balance

Account balance and totals.

Response fields

FieldTypeDescription
balance_brlnumberAvailable balance.
total_spent_brlnumberTotal spent on the account.
total_recharged_brlnumberTotal topped up.
currencystringAlways "BRL".

Example

curl "https://kdefy-sms.com/api/v1/balance" \
  -H "X-API-Key: $KDEFY_API_KEY"
const res = await fetch("https://kdefy-sms.com/api/v1/balance", {
  headers: { "X-API-Key": process.env.KDEFY_API_KEY },
});
const data = await res.json();
if (data.status !== "success") throw new Error(data.error);
console.log(data);
import os, requests

r = requests.get(
    "https://kdefy-sms.com/api/v1/balance",
    headers={"X-API-Key": os.environ["KDEFY_API_KEY"]},
    timeout=20,
)
data = r.json()
if data["status"] != "success":
    raise RuntimeError(data["error"])
print(data)
<?php
$ch = curl_init("https://kdefy-sms.com/api/v1/balance");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 20,
    CURLOPT_HTTPHEADER => ["X-API-Key: " . getenv("KDEFY_API_KEY")],
]);
$data = json_decode(curl_exec($ch), true);
if (($data["status"] ?? null) !== "success") {
    throw new RuntimeException($data["error"] ?? "erro_desconhecido");
}
print_r($data);
require "net/http"
require "json"

uri = URI("https://kdefy-sms.com/api/v1/balance")
req = Net::HTTP::Get.new(uri, "X-API-Key" => ENV.fetch("KDEFY_API_KEY"))
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", read_timeout: 20) { |http| http.request(req) }
data = JSON.parse(res.body)
abort(data["error"]) unless data["status"] == "success"
puts JSON.pretty_generate(data)
#!/usr/bin/env bash
curl -sS --max-time 20 -G "https://kdefy-sms.com/api/v1/balance" \
  -H "X-API-Key: $KDEFY_API_KEY" | jq .

Response

{
  "status": "success",
  "balance_brl": 25.4,
  "total_spent_brl": 12.1,
  "total_recharged_brl": 37.5,
  "currency": "BRL"
}

Try it

GET
/api/v1/profile

Profile

Username, balance and creation date.

Response fields

FieldTypeDescription
usernamestringUsername.
balance_brlnumberAvailable balance.
created_atstringAccount creation (ISO 8601).
reseller.levelintegerCurrent level (0 to 3). 0 = standard price.
reseller.markup_discount_percentnumberDiscount on the service fee, in %.
reseller.permanent_levelintegerPermanent level earned (0, 2 or 3).
reseller.week_recharged_brlnumberLargest single top-up since the last weekly review.
reseller.lifetime_recharged_brlnumberTotal topped up on the account (R$).
reseller.next_reviewstringNext weekly review (Tuesday 02:00, in the account’s time zone).

Example

curl "https://kdefy-sms.com/api/v1/profile" \
  -H "X-API-Key: $KDEFY_API_KEY"
const res = await fetch("https://kdefy-sms.com/api/v1/profile", {
  headers: { "X-API-Key": process.env.KDEFY_API_KEY },
});
const data = await res.json();
if (data.status !== "success") throw new Error(data.error);
console.log(data);
import os, requests

r = requests.get(
    "https://kdefy-sms.com/api/v1/profile",
    headers={"X-API-Key": os.environ["KDEFY_API_KEY"]},
    timeout=20,
)
data = r.json()
if data["status"] != "success":
    raise RuntimeError(data["error"])
print(data)
<?php
$ch = curl_init("https://kdefy-sms.com/api/v1/profile");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 20,
    CURLOPT_HTTPHEADER => ["X-API-Key: " . getenv("KDEFY_API_KEY")],
]);
$data = json_decode(curl_exec($ch), true);
if (($data["status"] ?? null) !== "success") {
    throw new RuntimeException($data["error"] ?? "erro_desconhecido");
}
print_r($data);
require "net/http"
require "json"

uri = URI("https://kdefy-sms.com/api/v1/profile")
req = Net::HTTP::Get.new(uri, "X-API-Key" => ENV.fetch("KDEFY_API_KEY"))
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", read_timeout: 20) { |http| http.request(req) }
data = JSON.parse(res.body)
abort(data["error"]) unless data["status"] == "success"
puts JSON.pretty_generate(data)
#!/usr/bin/env bash
curl -sS --max-time 20 -G "https://kdefy-sms.com/api/v1/profile" \
  -H "X-API-Key: $KDEFY_API_KEY" | jq .

Response

{
  "status": "success",
  "username": "maria",
  "balance_brl": 25.4,
  "created_at": "2026-09-01T12:00:00.000Z"
}

Try it

Catalog

GET
/api/v1/countries

Countries

List of countries. Use the id in country.

Response fields

FieldTypeDescription
countries[].idintegerCountry ID. Use it in country.
countries[].namestringName.
countries[].dial_codestring | nullDial code, e.g. +55.

Example

curl "https://kdefy-sms.com/api/v1/countries" \
  -H "X-API-Key: $KDEFY_API_KEY"
const res = await fetch("https://kdefy-sms.com/api/v1/countries", {
  headers: { "X-API-Key": process.env.KDEFY_API_KEY },
});
const data = await res.json();
if (data.status !== "success") throw new Error(data.error);
console.log(data);
import os, requests

r = requests.get(
    "https://kdefy-sms.com/api/v1/countries",
    headers={"X-API-Key": os.environ["KDEFY_API_KEY"]},
    timeout=20,
)
data = r.json()
if data["status"] != "success":
    raise RuntimeError(data["error"])
print(data)
<?php
$ch = curl_init("https://kdefy-sms.com/api/v1/countries");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 20,
    CURLOPT_HTTPHEADER => ["X-API-Key: " . getenv("KDEFY_API_KEY")],
]);
$data = json_decode(curl_exec($ch), true);
if (($data["status"] ?? null) !== "success") {
    throw new RuntimeException($data["error"] ?? "erro_desconhecido");
}
print_r($data);
require "net/http"
require "json"

uri = URI("https://kdefy-sms.com/api/v1/countries")
req = Net::HTTP::Get.new(uri, "X-API-Key" => ENV.fetch("KDEFY_API_KEY"))
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", read_timeout: 20) { |http| http.request(req) }
data = JSON.parse(res.body)
abort(data["error"]) unless data["status"] == "success"
puts JSON.pretty_generate(data)
#!/usr/bin/env bash
curl -sS --max-time 20 -G "https://kdefy-sms.com/api/v1/countries" \
  -H "X-API-Key: $KDEFY_API_KEY" | jq .

Response

{
  "status": "success",
  "countries": [
    {
      "id": 73,
      "name": "Brazil",
      "dial_code": "+55"
    }
  ]
}

Try it

GET
/api/v1/services

Services

Services of a country, with stock and starting price. Use code in service.

Parameters

NameDescription
countryrequiredCountry ID.

Response fields

FieldTypeDescription
countryintegerCountry queried.
services[].codestringCode. Use it in service.
services[].namestringName.
services[].availableintegerStock.
services[].from_price_brlnumber | nullPrice of the cheapest offer.

Errors

Example

curl "https://kdefy-sms.com/api/v1/services?country=73" \
  -H "X-API-Key: $KDEFY_API_KEY"
const res = await fetch("https://kdefy-sms.com/api/v1/services?country=73", {
  headers: { "X-API-Key": process.env.KDEFY_API_KEY },
});
const data = await res.json();
if (data.status !== "success") throw new Error(data.error);
console.log(data);
import os, requests

r = requests.get(
    "https://kdefy-sms.com/api/v1/services",
    params={"country": 73},
    headers={"X-API-Key": os.environ["KDEFY_API_KEY"]},
    timeout=20,
)
data = r.json()
if data["status"] != "success":
    raise RuntimeError(data["error"])
print(data)
<?php
$ch = curl_init("https://kdefy-sms.com/api/v1/services?country=73");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 20,
    CURLOPT_HTTPHEADER => ["X-API-Key: " . getenv("KDEFY_API_KEY")],
]);
$data = json_decode(curl_exec($ch), true);
if (($data["status"] ?? null) !== "success") {
    throw new RuntimeException($data["error"] ?? "erro_desconhecido");
}
print_r($data);
require "net/http"
require "json"

uri = URI("https://kdefy-sms.com/api/v1/services?country=73")
req = Net::HTTP::Get.new(uri, "X-API-Key" => ENV.fetch("KDEFY_API_KEY"))
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", read_timeout: 20) { |http| http.request(req) }
data = JSON.parse(res.body)
abort(data["error"]) unless data["status"] == "success"
puts JSON.pretty_generate(data)
#!/usr/bin/env bash
curl -sS --max-time 20 -G "https://kdefy-sms.com/api/v1/services" \
  -H "X-API-Key: $KDEFY_API_KEY" \
  --data-urlencode "country=73" | jq .

Response

{
  "status": "success",
  "country": 73,
  "services": [
    {
      "code": "wa",
      "name": "WhatsApp",
      "available": 1240,
      "from_price_brl": 7.1
    }
  ]
}

Try it

GET
/api/v1/operators

Carriers

Carriers of a country. Optional use in operator when buying.

Parameters

NameDescription
countryrequiredCountry ID.

Response fields

FieldTypeDescription
countryintegerCountry queried.
operators[].idstringCarrier ID. Use it in operator.
operators[].namestringName.

Errors

Example

curl "https://kdefy-sms.com/api/v1/operators?country=73" \
  -H "X-API-Key: $KDEFY_API_KEY"
const res = await fetch("https://kdefy-sms.com/api/v1/operators?country=73", {
  headers: { "X-API-Key": process.env.KDEFY_API_KEY },
});
const data = await res.json();
if (data.status !== "success") throw new Error(data.error);
console.log(data);
import os, requests

r = requests.get(
    "https://kdefy-sms.com/api/v1/operators",
    params={"country": 73},
    headers={"X-API-Key": os.environ["KDEFY_API_KEY"]},
    timeout=20,
)
data = r.json()
if data["status"] != "success":
    raise RuntimeError(data["error"])
print(data)
<?php
$ch = curl_init("https://kdefy-sms.com/api/v1/operators?country=73");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 20,
    CURLOPT_HTTPHEADER => ["X-API-Key: " . getenv("KDEFY_API_KEY")],
]);
$data = json_decode(curl_exec($ch), true);
if (($data["status"] ?? null) !== "success") {
    throw new RuntimeException($data["error"] ?? "erro_desconhecido");
}
print_r($data);
require "net/http"
require "json"

uri = URI("https://kdefy-sms.com/api/v1/operators?country=73")
req = Net::HTTP::Get.new(uri, "X-API-Key" => ENV.fetch("KDEFY_API_KEY"))
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", read_timeout: 20) { |http| http.request(req) }
data = JSON.parse(res.body)
abort(data["error"]) unless data["status"] == "success"
puts JSON.pretty_generate(data)
#!/usr/bin/env bash
curl -sS --max-time 20 -G "https://kdefy-sms.com/api/v1/operators" \
  -H "X-API-Key: $KDEFY_API_KEY" \
  --data-urlencode "country=73" | jq .

Response

{
  "status": "success",
  "country": 73,
  "operators": [
    {
      "id": "vivo",
      "name": "vivo"
    }
  ]
}

Try it

GET
/api/v1/countries/top

Top countries

Countries with stock for a service, in the provider’s order, with your starting price.

Parameters

NameDescription
servicerequiredService code (see /services).
limitoptionalMaximum number of items. Default 15, max 50.

Response fields

FieldTypeDescription
servicestringService queried.
countries[].countryintegerCountry ID.
countries[].namestringCountry name.
countries[].availableintegerStock.
countries[].from_price_brlnumberPrice of the cheapest offer, in reais.

Errors

Example

curl "https://kdefy-sms.com/api/v1/countries/top?service=wa" \
  -H "X-API-Key: $KDEFY_API_KEY"
const res = await fetch("https://kdefy-sms.com/api/v1/countries/top?service=wa", {
  headers: { "X-API-Key": process.env.KDEFY_API_KEY },
});
const data = await res.json();
if (data.status !== "success") throw new Error(data.error);
console.log(data);
import os, requests

r = requests.get(
    "https://kdefy-sms.com/api/v1/countries/top",
    params={"service": "wa"},
    headers={"X-API-Key": os.environ["KDEFY_API_KEY"]},
    timeout=20,
)
data = r.json()
if data["status"] != "success":
    raise RuntimeError(data["error"])
print(data)
<?php
$ch = curl_init("https://kdefy-sms.com/api/v1/countries/top?service=wa");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 20,
    CURLOPT_HTTPHEADER => ["X-API-Key: " . getenv("KDEFY_API_KEY")],
]);
$data = json_decode(curl_exec($ch), true);
if (($data["status"] ?? null) !== "success") {
    throw new RuntimeException($data["error"] ?? "erro_desconhecido");
}
print_r($data);
require "net/http"
require "json"

uri = URI("https://kdefy-sms.com/api/v1/countries/top?service=wa")
req = Net::HTTP::Get.new(uri, "X-API-Key" => ENV.fetch("KDEFY_API_KEY"))
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", read_timeout: 20) { |http| http.request(req) }
data = JSON.parse(res.body)
abort(data["error"]) unless data["status"] == "success"
puts JSON.pretty_generate(data)
#!/usr/bin/env bash
curl -sS --max-time 20 -G "https://kdefy-sms.com/api/v1/countries/top" \
  -H "X-API-Key: $KDEFY_API_KEY" \
  --data-urlencode "service=wa" | jq .

Response

{
  "status": "success",
  "service": "wa",
  "countries": [
    {
      "country": 73,
      "name": "Brazil",
      "available": 1200,
      "from_price_brl": 6.9
    }
  ]
}

Try it

GET
/api/v1/offers

Offers

Price tiers and stock of a service in a country. Cheap tiers have little stock; the more expensive ones tend to have numbers.

Parameters

NameDescription
countryrequiredCountry ID.
servicerequiredService code.

Response fields

FieldTypeDescription
offers[].price_brlnumberTier price.
offers[].availableintegerTier stock.

Errors

Example

curl "https://kdefy-sms.com/api/v1/offers?country=73&service=wa" \
  -H "X-API-Key: $KDEFY_API_KEY"
const res = await fetch("https://kdefy-sms.com/api/v1/offers?country=73&service=wa", {
  headers: { "X-API-Key": process.env.KDEFY_API_KEY },
});
const data = await res.json();
if (data.status !== "success") throw new Error(data.error);
console.log(data);
import os, requests

r = requests.get(
    "https://kdefy-sms.com/api/v1/offers",
    params={"country": 73, "service": "wa"},
    headers={"X-API-Key": os.environ["KDEFY_API_KEY"]},
    timeout=20,
)
data = r.json()
if data["status"] != "success":
    raise RuntimeError(data["error"])
print(data)
<?php
$ch = curl_init("https://kdefy-sms.com/api/v1/offers?country=73&service=wa");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 20,
    CURLOPT_HTTPHEADER => ["X-API-Key: " . getenv("KDEFY_API_KEY")],
]);
$data = json_decode(curl_exec($ch), true);
if (($data["status"] ?? null) !== "success") {
    throw new RuntimeException($data["error"] ?? "erro_desconhecido");
}
print_r($data);
require "net/http"
require "json"

uri = URI("https://kdefy-sms.com/api/v1/offers?country=73&service=wa")
req = Net::HTTP::Get.new(uri, "X-API-Key" => ENV.fetch("KDEFY_API_KEY"))
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", read_timeout: 20) { |http| http.request(req) }
data = JSON.parse(res.body)
abort(data["error"]) unless data["status"] == "success"
puts JSON.pretty_generate(data)
#!/usr/bin/env bash
curl -sS --max-time 20 -G "https://kdefy-sms.com/api/v1/offers" \
  -H "X-API-Key: $KDEFY_API_KEY" \
  --data-urlencode "country=73" \
  --data-urlencode "service=wa" | jq .

Response

{
  "status": "success",
  "country": 73,
  "service": "wa",
  "offers": [
    {
      "price_brl": 7.1,
      "available": 12
    },
    {
      "price_brl": 8.4,
      "available": 340
    }
  ]
}

Try it

Numbers

GETPOST
/api/v1/number/get

Buy number

Buys one number, or several with quantity. Charged immediately. If the number is cancelled or expires without a code, the amount returns to your balance.

Batches and safe retries: see Batch purchase and Idempotency in Guides.

Parameters

NameDescription
countryrequiredCountry ID.
servicerequiredService code.
max_price_brloptionalPrice cap. Uses the most expensive tier that fits. Without it, the cheapest is used.
operatoroptionalCarrier. Default: any. Restricts the stock.
quantityoptionalQuantity, from 1 to 50. Without it, buys 1 and the response comes in the simple format.
request_idoptionalAnti-duplication key, 8 to 80 characters. The Idempotency-Key header also works. Recommended.

Response fields

FieldTypeDescription
idstringActivation ID. Used in /number/status, /number/cancel and /number/finish.
servicestringService code.
service_namestringService name. Comes from the provider and is not translated.
countryintegerCountry ID.
country_namestringCountry name.
phonestringNumber with country code, digits only (5511999999999).
statestringActivation state (see States). Fixed English value; use this one in your logic.
state_labelstringState as text, in the account language. Display only.
codestring | nullLast code received. null until it arrives.
codesarrayAll codes, [{ code, received_at }], from oldest to newest.
price_brlnumberAmount charged, in reais.
seconds_leftintegerSeconds until the activation expires.
cancel_in_secondsintegerSeconds until cancellation is allowed. 0 = already allowed.
balance_brlnumberBalance after the purchase.
with quantity
requestedintegerQuantity requested.
purchasedintegerQuantity purchased.
failedintegerQuantity not purchased (not charged).
numbersarrayActivations, with the same fields as above.
errorsarrayError codes of the purchases that failed (partial purchase).

Errors

Example

curl "https://kdefy-sms.com/api/v1/number/get?country=73&service=wa&request_id=order-0001" \
  -H "X-API-Key: $KDEFY_API_KEY"
const res = await fetch("https://kdefy-sms.com/api/v1/number/get?country=73&service=wa&request_id=order-0001", {
  headers: { "X-API-Key": process.env.KDEFY_API_KEY },
});
const data = await res.json();
if (data.status !== "success") throw new Error(data.error);
console.log(data);
import os, requests

r = requests.get(
    "https://kdefy-sms.com/api/v1/number/get",
    params={"country": 73, "service": "wa", "request_id": "order-0001"},
    headers={"X-API-Key": os.environ["KDEFY_API_KEY"]},
    timeout=20,
)
data = r.json()
if data["status"] != "success":
    raise RuntimeError(data["error"])
print(data)
<?php
$ch = curl_init("https://kdefy-sms.com/api/v1/number/get?country=73&service=wa&request_id=order-0001");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 20,
    CURLOPT_HTTPHEADER => ["X-API-Key: " . getenv("KDEFY_API_KEY")],
]);
$data = json_decode(curl_exec($ch), true);
if (($data["status"] ?? null) !== "success") {
    throw new RuntimeException($data["error"] ?? "erro_desconhecido");
}
print_r($data);
require "net/http"
require "json"

uri = URI("https://kdefy-sms.com/api/v1/number/get?country=73&service=wa&request_id=order-0001")
req = Net::HTTP::Get.new(uri, "X-API-Key" => ENV.fetch("KDEFY_API_KEY"))
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", read_timeout: 20) { |http| http.request(req) }
data = JSON.parse(res.body)
abort(data["error"]) unless data["status"] == "success"
puts JSON.pretty_generate(data)
#!/usr/bin/env bash
curl -sS --max-time 20 -G "https://kdefy-sms.com/api/v1/number/get" \
  -H "X-API-Key: $KDEFY_API_KEY" \
  --data-urlencode "country=73" \
  --data-urlencode "service=wa" \
  --data-urlencode "request_id=order-0001" | jq .

Response

{
  "id": "1842",
  "service": "wa",
  "service_name": "WhatsApp",
  "country": 73,
  "country_name": "Brazil",
  "phone": "5511999999999",
  "state": "waiting",
  "state_label": "Waiting for code",
  "code": null,
  "codes": [],
  "price_brl": 8.4,
  "seconds_left": 1200,
  "cancel_in_seconds": 120,
  "status": "success",
  "balance_brl": 17
}

Response with quantity=2

{
  "status": "success",
  "requested": 2,
  "purchased": 2,
  "failed": 0,
  "numbers": [
    {
      "id": "1842",
      "service": "wa",
      "service_name": "WhatsApp",
      "country": 73,
      "country_name": "Brazil",
      "phone": "5511999999999",
      "state": "waiting",
      "state_label": "Waiting for code",
      "code": null,
      "codes": [],
      "price_brl": 8.4,
      "seconds_left": 1200,
      "cancel_in_seconds": 120
    },
    {
      "id": "1843",
      "service": "wa",
      "service_name": "WhatsApp",
      "country": 73,
      "country_name": "Brazil",
      "phone": "5511988887777",
      "state": "waiting",
      "state_label": "Waiting for code",
      "code": null,
      "codes": [],
      "price_brl": 8.4,
      "seconds_left": 1200,
      "cancel_in_seconds": 120
    }
  ],
  "balance_brl": 8.6
}

Try it

GET
/api/v1/number/status

Status and code

Activation state and received codes.

Parameters

NameDescription
idrequiredActivation ID, returned by the purchase.

Response fields

FieldTypeDescription
idstringActivation ID. Used in /number/status, /number/cancel and /number/finish.
servicestringService code.
service_namestringService name. Comes from the provider and is not translated.
countryintegerCountry ID.
country_namestringCountry name.
phonestringNumber with country code, digits only (5511999999999).
statestringActivation state (see States). Fixed English value; use this one in your logic.
state_labelstringState as text, in the account language. Display only.
codestring | nullLast code received. null until it arrives.
codesarrayAll codes, [{ code, received_at }], from oldest to newest.
price_brlnumberAmount charged, in reais.
seconds_leftintegerSeconds until the activation expires.
cancel_in_secondsintegerSeconds until cancellation is allowed. 0 = already allowed.

Errors

Example

curl "https://kdefy-sms.com/api/v1/number/status?id=1842" \
  -H "X-API-Key: $KDEFY_API_KEY"
const res = await fetch("https://kdefy-sms.com/api/v1/number/status?id=1842", {
  headers: { "X-API-Key": process.env.KDEFY_API_KEY },
});
const data = await res.json();
if (data.status !== "success") throw new Error(data.error);
console.log(data);
import os, requests

r = requests.get(
    "https://kdefy-sms.com/api/v1/number/status",
    params={"id": "1842"},
    headers={"X-API-Key": os.environ["KDEFY_API_KEY"]},
    timeout=20,
)
data = r.json()
if data["status"] != "success":
    raise RuntimeError(data["error"])
print(data)
<?php
$ch = curl_init("https://kdefy-sms.com/api/v1/number/status?id=1842");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 20,
    CURLOPT_HTTPHEADER => ["X-API-Key: " . getenv("KDEFY_API_KEY")],
]);
$data = json_decode(curl_exec($ch), true);
if (($data["status"] ?? null) !== "success") {
    throw new RuntimeException($data["error"] ?? "erro_desconhecido");
}
print_r($data);
require "net/http"
require "json"

uri = URI("https://kdefy-sms.com/api/v1/number/status?id=1842")
req = Net::HTTP::Get.new(uri, "X-API-Key" => ENV.fetch("KDEFY_API_KEY"))
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", read_timeout: 20) { |http| http.request(req) }
data = JSON.parse(res.body)
abort(data["error"]) unless data["status"] == "success"
puts JSON.pretty_generate(data)
#!/usr/bin/env bash
curl -sS --max-time 20 -G "https://kdefy-sms.com/api/v1/number/status" \
  -H "X-API-Key: $KDEFY_API_KEY" \
  --data-urlencode "id=1842" | jq .

Response

{
  "status": "success",
  "id": "1842",
  "service": "wa",
  "service_name": "WhatsApp",
  "country": 73,
  "country_name": "Brazil",
  "phone": "5511999999999",
  "state": "code_received",
  "state_label": "Code received",
  "code": "123456",
  "codes": [
    {
      "code": "123456",
      "received_at": "2026-10-19T14:02:11.000Z"
    }
  ],
  "price_brl": 8.4,
  "seconds_left": 1103,
  "cancel_in_seconds": 0
}

Try it

GET
/api/v1/number/reactivate/options

Reactivate: price

Shows how much it costs to reactivate the number of a finished activation that received a code.

Parameters

NameDescription
idrequiredID of the original activation (finished, with a code).

Response fields

FieldTypeDescription
idstringID of the original activation.
servicestringService code.
countryintegerCountry ID.
price_brlnumberCurrent reactivation price, in reais.

Errors

Example

curl "https://kdefy-sms.com/api/v1/number/reactivate/options?id=1842" \
  -H "X-API-Key: $KDEFY_API_KEY"
const res = await fetch("https://kdefy-sms.com/api/v1/number/reactivate/options?id=1842", {
  headers: { "X-API-Key": process.env.KDEFY_API_KEY },
});
const data = await res.json();
if (data.status !== "success") throw new Error(data.error);
console.log(data);
import os, requests

r = requests.get(
    "https://kdefy-sms.com/api/v1/number/reactivate/options",
    params={"id": "1842"},
    headers={"X-API-Key": os.environ["KDEFY_API_KEY"]},
    timeout=20,
)
data = r.json()
if data["status"] != "success":
    raise RuntimeError(data["error"])
print(data)
<?php
$ch = curl_init("https://kdefy-sms.com/api/v1/number/reactivate/options?id=1842");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 20,
    CURLOPT_HTTPHEADER => ["X-API-Key: " . getenv("KDEFY_API_KEY")],
]);
$data = json_decode(curl_exec($ch), true);
if (($data["status"] ?? null) !== "success") {
    throw new RuntimeException($data["error"] ?? "erro_desconhecido");
}
print_r($data);
require "net/http"
require "json"

uri = URI("https://kdefy-sms.com/api/v1/number/reactivate/options?id=1842")
req = Net::HTTP::Get.new(uri, "X-API-Key" => ENV.fetch("KDEFY_API_KEY"))
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", read_timeout: 20) { |http| http.request(req) }
data = JSON.parse(res.body)
abort(data["error"]) unless data["status"] == "success"
puts JSON.pretty_generate(data)
#!/usr/bin/env bash
curl -sS --max-time 20 -G "https://kdefy-sms.com/api/v1/number/reactivate/options" \
  -H "X-API-Key: $KDEFY_API_KEY" \
  --data-urlencode "id=1842" | jq .

Response

{
  "status": "success",
  "id": "1842",
  "service": "wa",
  "country": 73,
  "price_brl": 3.1
}

Try it

GETPOST
/api/v1/number/reactivate

Reactivate number

Reuses the number of a finished activation to receive a new code. Debits the current price immediately and creates a new activation.

Check the price first at /number/reactivate/options.

Parameters

NameDescription
idrequiredID of the original activation.
max_price_brloptionalMaximum price you accept. If the current price is higher, nothing is charged.

Response fields

FieldTypeDescription
idstringActivation ID. Used in /number/status, /number/cancel and /number/finish.
servicestringService code.
service_namestringService name. Comes from the provider and is not translated.
countryintegerCountry ID.
country_namestringCountry name.
phonestringNumber with country code, digits only (5511999999999).
statestringActivation state (see States). Fixed English value; use this one in your logic.
state_labelstringState as text, in the account language. Display only.
codestring | nullLast code received. null until it arrives.
codesarrayAll codes, [{ code, received_at }], from oldest to newest.
price_brlnumberAmount charged, in reais.
seconds_leftintegerSeconds until the activation expires.
cancel_in_secondsintegerSeconds until cancellation is allowed. 0 = already allowed.
reactivated_fromstringID of the original activation.
balance_brlnumberBalance after the charge.

Errors

Example

curl "https://kdefy-sms.com/api/v1/number/reactivate?id=1842" \
  -H "X-API-Key: $KDEFY_API_KEY"
const res = await fetch("https://kdefy-sms.com/api/v1/number/reactivate?id=1842", {
  headers: { "X-API-Key": process.env.KDEFY_API_KEY },
});
const data = await res.json();
if (data.status !== "success") throw new Error(data.error);
console.log(data);
import os, requests

r = requests.get(
    "https://kdefy-sms.com/api/v1/number/reactivate",
    params={"id": "1842"},
    headers={"X-API-Key": os.environ["KDEFY_API_KEY"]},
    timeout=20,
)
data = r.json()
if data["status"] != "success":
    raise RuntimeError(data["error"])
print(data)
<?php
$ch = curl_init("https://kdefy-sms.com/api/v1/number/reactivate?id=1842");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 20,
    CURLOPT_HTTPHEADER => ["X-API-Key: " . getenv("KDEFY_API_KEY")],
]);
$data = json_decode(curl_exec($ch), true);
if (($data["status"] ?? null) !== "success") {
    throw new RuntimeException($data["error"] ?? "erro_desconhecido");
}
print_r($data);
require "net/http"
require "json"

uri = URI("https://kdefy-sms.com/api/v1/number/reactivate?id=1842")
req = Net::HTTP::Get.new(uri, "X-API-Key" => ENV.fetch("KDEFY_API_KEY"))
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", read_timeout: 20) { |http| http.request(req) }
data = JSON.parse(res.body)
abort(data["error"]) unless data["status"] == "success"
puts JSON.pretty_generate(data)
#!/usr/bin/env bash
curl -sS --max-time 20 -G "https://kdefy-sms.com/api/v1/number/reactivate" \
  -H "X-API-Key: $KDEFY_API_KEY" \
  --data-urlencode "id=1842" | jq .

Response

{
  "status": "success",
  "id": "1842",
  "service": "wa",
  "service_name": "WhatsApp",
  "country": 73,
  "country_name": "Brazil",
  "phone": "5511999999999",
  "state": "waiting",
  "state_label": "Waiting for code",
  "code": null,
  "codes": [],
  "price_brl": 8.4,
  "seconds_left": 1200,
  "cancel_in_seconds": 120,
  "reactivated_from": "1842",
  "balance_brl": 6.4
}

Try it

GETPOST
/api/v1/number/cancel

Cancel

Cancels and refunds the amount. Only without a code and 2 min after purchase; before that it returns cancelamento_muito_cedo with retry_in_seconds. The API has no penalty for consecutive cancellations.

Parameters

NameDescription
idrequiredActivation ID.

Response fields

FieldTypeDescription
idstringActivation ID.
statestringAlways "cancelled".
refunded_brlnumberAmount refunded.
balance_brlnumberBalance after the refund.

Errors

Example

curl "https://kdefy-sms.com/api/v1/number/cancel?id=1842" \
  -H "X-API-Key: $KDEFY_API_KEY"
const res = await fetch("https://kdefy-sms.com/api/v1/number/cancel?id=1842", {
  headers: { "X-API-Key": process.env.KDEFY_API_KEY },
});
const data = await res.json();
if (data.status !== "success") throw new Error(data.error);
console.log(data);
import os, requests

r = requests.get(
    "https://kdefy-sms.com/api/v1/number/cancel",
    params={"id": "1842"},
    headers={"X-API-Key": os.environ["KDEFY_API_KEY"]},
    timeout=20,
)
data = r.json()
if data["status"] != "success":
    raise RuntimeError(data["error"])
print(data)
<?php
$ch = curl_init("https://kdefy-sms.com/api/v1/number/cancel?id=1842");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 20,
    CURLOPT_HTTPHEADER => ["X-API-Key: " . getenv("KDEFY_API_KEY")],
]);
$data = json_decode(curl_exec($ch), true);
if (($data["status"] ?? null) !== "success") {
    throw new RuntimeException($data["error"] ?? "erro_desconhecido");
}
print_r($data);
require "net/http"
require "json"

uri = URI("https://kdefy-sms.com/api/v1/number/cancel?id=1842")
req = Net::HTTP::Get.new(uri, "X-API-Key" => ENV.fetch("KDEFY_API_KEY"))
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", read_timeout: 20) { |http| http.request(req) }
data = JSON.parse(res.body)
abort(data["error"]) unless data["status"] == "success"
puts JSON.pretty_generate(data)
#!/usr/bin/env bash
curl -sS --max-time 20 -G "https://kdefy-sms.com/api/v1/number/cancel" \
  -H "X-API-Key: $KDEFY_API_KEY" \
  --data-urlencode "id=1842" | jq .

Response

{
  "status": "success",
  "id": "1842",
  "state": "cancelled",
  "refunded_brl": 8.4,
  "balance_brl": 25.4
}

Try it

GETPOST
/api/v1/number/finish

Finish

Ends the activation after the code. Stops waiting for extra codes. No refund.

Parameters

NameDescription
idrequiredActivation ID.

Response fields

FieldTypeDescription
idstringActivation ID.
statestringAlways "completed".

Errors

Example

curl "https://kdefy-sms.com/api/v1/number/finish?id=1842" \
  -H "X-API-Key: $KDEFY_API_KEY"
const res = await fetch("https://kdefy-sms.com/api/v1/number/finish?id=1842", {
  headers: { "X-API-Key": process.env.KDEFY_API_KEY },
});
const data = await res.json();
if (data.status !== "success") throw new Error(data.error);
console.log(data);
import os, requests

r = requests.get(
    "https://kdefy-sms.com/api/v1/number/finish",
    params={"id": "1842"},
    headers={"X-API-Key": os.environ["KDEFY_API_KEY"]},
    timeout=20,
)
data = r.json()
if data["status"] != "success":
    raise RuntimeError(data["error"])
print(data)
<?php
$ch = curl_init("https://kdefy-sms.com/api/v1/number/finish?id=1842");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 20,
    CURLOPT_HTTPHEADER => ["X-API-Key: " . getenv("KDEFY_API_KEY")],
]);
$data = json_decode(curl_exec($ch), true);
if (($data["status"] ?? null) !== "success") {
    throw new RuntimeException($data["error"] ?? "erro_desconhecido");
}
print_r($data);
require "net/http"
require "json"

uri = URI("https://kdefy-sms.com/api/v1/number/finish?id=1842")
req = Net::HTTP::Get.new(uri, "X-API-Key" => ENV.fetch("KDEFY_API_KEY"))
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", read_timeout: 20) { |http| http.request(req) }
data = JSON.parse(res.body)
abort(data["error"]) unless data["status"] == "success"
puts JSON.pretty_generate(data)
#!/usr/bin/env bash
curl -sS --max-time 20 -G "https://kdefy-sms.com/api/v1/number/finish" \
  -H "X-API-Key: $KDEFY_API_KEY" \
  --data-urlencode "id=1842" | jq .

Response

{
  "status": "success",
  "id": "1842",
  "state": "completed"
}

Try it

History

GET
/api/v1/activations

List activations

Your activations, including those made on the website, newest first.

Parameters

NameDescription
stateoptionalFilters by state: active, waiting, code_received, completed, cancelled or expired.
serviceoptionalFilters by service code.
countryoptionalFilters by country ID.
fromoptionalOnly activations created from this date on (ISO 8601).
tooptionalOnly activations created before this date (ISO 8601).
pageoptionalPage number, starting at 1. Use together with limit.
limitoptionalMaximum number of items. Default 50, max 100.

Response fields

FieldTypeDescription
activations[]arrayActivations, with the same fields as /number/status.

Example

curl "https://kdefy-sms.com/api/v1/activations?state=active" \
  -H "X-API-Key: $KDEFY_API_KEY"
const res = await fetch("https://kdefy-sms.com/api/v1/activations?state=active", {
  headers: { "X-API-Key": process.env.KDEFY_API_KEY },
});
const data = await res.json();
if (data.status !== "success") throw new Error(data.error);
console.log(data);
import os, requests

r = requests.get(
    "https://kdefy-sms.com/api/v1/activations",
    params={"state": "active"},
    headers={"X-API-Key": os.environ["KDEFY_API_KEY"]},
    timeout=20,
)
data = r.json()
if data["status"] != "success":
    raise RuntimeError(data["error"])
print(data)
<?php
$ch = curl_init("https://kdefy-sms.com/api/v1/activations?state=active");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 20,
    CURLOPT_HTTPHEADER => ["X-API-Key: " . getenv("KDEFY_API_KEY")],
]);
$data = json_decode(curl_exec($ch), true);
if (($data["status"] ?? null) !== "success") {
    throw new RuntimeException($data["error"] ?? "erro_desconhecido");
}
print_r($data);
require "net/http"
require "json"

uri = URI("https://kdefy-sms.com/api/v1/activations?state=active")
req = Net::HTTP::Get.new(uri, "X-API-Key" => ENV.fetch("KDEFY_API_KEY"))
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", read_timeout: 20) { |http| http.request(req) }
data = JSON.parse(res.body)
abort(data["error"]) unless data["status"] == "success"
puts JSON.pretty_generate(data)
#!/usr/bin/env bash
curl -sS --max-time 20 -G "https://kdefy-sms.com/api/v1/activations" \
  -H "X-API-Key: $KDEFY_API_KEY" \
  --data-urlencode "state=active" | jq .

Response

{
  "status": "success",
  "activations": [
    {
      "id": "1842",
      "service": "wa",
      "service_name": "WhatsApp",
      "country": 73,
      "country_name": "Brazil",
      "phone": "5511999999999",
      "state": "waiting",
      "state_label": "Waiting for code",
      "code": null,
      "codes": [],
      "price_brl": 8.4,
      "seconds_left": 1200,
      "cancel_in_seconds": 120
    }
  ]
}

Try it

GET
/api/v1/stats

Statistics

Total activations, success rate and spending by service and country over a period.

Parameters

NameDescription
fromoptionalStart of the period (ISO 8601 date). Default: 30 days ago.
tooptionalEnd of the period (ISO 8601 date). Default: now.

Response fields

FieldTypeDescription
fromstringStart of the period (ISO 8601 date). Default: 30 days ago.
tostringEnd of the period (ISO 8601 date). Default: now.
totalintegerActivations in the period.
successintegerActivations that received a code.
success_percentnumberSuccess rate, from 0 to 100.
spent_brlnumberSpent on activations that received a code.
by_service_countryarrayBreakdown by service and country: { service, service_name, country, country_name, count, success, success_percent }.

Example

curl "https://kdefy-sms.com/api/v1/stats" \
  -H "X-API-Key: $KDEFY_API_KEY"
const res = await fetch("https://kdefy-sms.com/api/v1/stats", {
  headers: { "X-API-Key": process.env.KDEFY_API_KEY },
});
const data = await res.json();
if (data.status !== "success") throw new Error(data.error);
console.log(data);
import os, requests

r = requests.get(
    "https://kdefy-sms.com/api/v1/stats",
    headers={"X-API-Key": os.environ["KDEFY_API_KEY"]},
    timeout=20,
)
data = r.json()
if data["status"] != "success":
    raise RuntimeError(data["error"])
print(data)
<?php
$ch = curl_init("https://kdefy-sms.com/api/v1/stats");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 20,
    CURLOPT_HTTPHEADER => ["X-API-Key: " . getenv("KDEFY_API_KEY")],
]);
$data = json_decode(curl_exec($ch), true);
if (($data["status"] ?? null) !== "success") {
    throw new RuntimeException($data["error"] ?? "erro_desconhecido");
}
print_r($data);
require "net/http"
require "json"

uri = URI("https://kdefy-sms.com/api/v1/stats")
req = Net::HTTP::Get.new(uri, "X-API-Key" => ENV.fetch("KDEFY_API_KEY"))
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", read_timeout: 20) { |http| http.request(req) }
data = JSON.parse(res.body)
abort(data["error"]) unless data["status"] == "success"
puts JSON.pretty_generate(data)
#!/usr/bin/env bash
curl -sS --max-time 20 -G "https://kdefy-sms.com/api/v1/stats" \
  -H "X-API-Key: $KDEFY_API_KEY" | jq .

Response

{
  "status": "success",
  "total": 40,
  "success": 36,
  "success_percent": 90,
  "spent_brl": 120.5,
  "by_service_country": [
    {
      "service": "wa",
      "service_name": "WhatsApp",
      "country": 73,
      "country_name": "Brazil",
      "count": 40,
      "success": 36,
      "success_percent": 90
    }
  ]
}

Try it

GET
/api/v1/transactions

Statement

Top-ups, purchases and refunds.

Parameters

NameDescription
limitoptionalMaximum number of items. Default 50, max 200.

Response fields

FieldTypeDescription
transactions[].typestringType (PURCHASE, REFUND...).
transactions[].amount_brlnumberAmount.
transactions[].balance_after_brlnumberBalance after.
transactions[].descriptionstringDescription.
transactions[].created_atstringDate (ISO 8601).

Example

curl "https://kdefy-sms.com/api/v1/transactions?limit=20" \
  -H "X-API-Key: $KDEFY_API_KEY"
const res = await fetch("https://kdefy-sms.com/api/v1/transactions?limit=20", {
  headers: { "X-API-Key": process.env.KDEFY_API_KEY },
});
const data = await res.json();
if (data.status !== "success") throw new Error(data.error);
console.log(data);
import os, requests

r = requests.get(
    "https://kdefy-sms.com/api/v1/transactions",
    params={"limit": 20},
    headers={"X-API-Key": os.environ["KDEFY_API_KEY"]},
    timeout=20,
)
data = r.json()
if data["status"] != "success":
    raise RuntimeError(data["error"])
print(data)
<?php
$ch = curl_init("https://kdefy-sms.com/api/v1/transactions?limit=20");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 20,
    CURLOPT_HTTPHEADER => ["X-API-Key: " . getenv("KDEFY_API_KEY")],
]);
$data = json_decode(curl_exec($ch), true);
if (($data["status"] ?? null) !== "success") {
    throw new RuntimeException($data["error"] ?? "erro_desconhecido");
}
print_r($data);
require "net/http"
require "json"

uri = URI("https://kdefy-sms.com/api/v1/transactions?limit=20")
req = Net::HTTP::Get.new(uri, "X-API-Key" => ENV.fetch("KDEFY_API_KEY"))
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", read_timeout: 20) { |http| http.request(req) }
data = JSON.parse(res.body)
abort(data["error"]) unless data["status"] == "success"
puts JSON.pretty_generate(data)
#!/usr/bin/env bash
curl -sS --max-time 20 -G "https://kdefy-sms.com/api/v1/transactions" \
  -H "X-API-Key: $KDEFY_API_KEY" \
  --data-urlencode "limit=20" | jq .

Response

{
  "status": "success",
  "transactions": [
    {
      "type": "PURCHASE",
      "amount_brl": 8.4,
      "balance_after_brl": 17,
      "description": "Purchase WhatsApp · Brazil (API)",
      "created_at": "2026-10-19T14:00:00.000Z"
    }
  ]
}

Try it

Errors

Format: { "status": "error", "error": "saldo_insuficiente", "message": "..." }. Decide using the field error.

HTTPerrorWhenWhat to do
401token_nao_informado token_invalido Token missing, invalid or revoked.Fix the token. Do not retry.
403conta_bloqueada token_suspenso Account banned or API suspended by the admin.Contact support.
400pais_invalido servico_invalido id_invalido quantidade_invalida preco_invalido operador_invalido request_id_invalido Missing or invalid parameter.Fix the call.
402saldo_insuficiente Balance lower than the price.Top up on the website.
404sem_numeros_disponiveis oferta_indisponivel No stock in this tier right now.Raise max_price_brl, change the country or try later.
404ativacao_nao_encontrada ID does not exist or belongs to another account.Check the id.
409requisicao_em_andamento The request_id is still being processed.Wait and repeat the same call.
409cancelamento_muito_cedo Less than 2 min since the purchase.Wait retry_in_seconds.
409ativacao_nao_reativavel reativacao_indisponivel Only numbers from finished activations that received a code can be reactivated, and the provider must allow it.Check /number/reactivate/options.
409ativacao_nao_cancelavel ativacao_nao_finalizavel The current state does not allow this action.Check /number/status.
422request_id_reutilizada Same request_id with different parameters.Use a new request_id.
422preco_acima_do_maximo The current price is higher than max_price_brl.Raise max_price_brl.
422preco_abaixo_do_minimo max_price_brl is lower than the cheapest offer.Raise it to min_price_brl or more.
429limite_de_requisicoes limite_de_numeros Limit exceeded.Wait retry_in_seconds.
502falha_temporaria Temporary instability. You are not charged.Retry with increasing backoff, same request_id.
Help

FAQ

How long does the code take?

Usually seconds, but it depends on the service. The activation waits up to 20 min. With no code in that time, it expires and the amount returns to your balance.

My purchase timed out. Now what?

Repeat the same call with the same request_id: the original response comes back and nothing is charged again. If you did not use request_id, list /activations?state=active before buying again.

Can I cancel right after buying?

No. Cancellation unlocks 2 min after purchase. cancel_in_seconds shows how long is left.

sem_numeros_disponiveis even with balance. Why?

The price tier is out of stock right now. Raise max_price_brl, change the country or see where there is stock in /offers.

Can the price change between the query and the purchase?

It can. The price_brl in the purchase response is what counts, and max_price_brl prevents it from going over your cap.

Do I need request_id?

It is not mandatory, but use it in any automation. Without it, repeating the call by mistake buys twice.

Can I buy more than 50 numbers?

Not in a single request, and the cap is 50 per minute per account. Above that the API returns limite_de_numeros.

Why is the response in Portuguese?

message and state_label follow the account language. Force it with ?lang=pt, ?lang=en, ?lang=es, ?lang=de or ?lang=fr. JSON keys, state and error codes are always the same.

How do I top up my balance?

On the website, via PIX. The API balance is the same as the website’s.

My token leaked.

Generate a new one under API on the website. The old one stops working immediately. If you saw unusual usage, contact support.

Usage rules

  • Each activation waits for the code for up to 20 min. Without a code, it expires and the amount returns to your balance.
  • There is no refund after the first code.
  • You can only see and operate your own activations.
  • Balance top-ups are made on the website (PIX).
  • Every request is logged on the account. The admin can review token usage.
  • Abuse or fraud may result in the account being blocked.