SMSCode API Geliştirici Rehberi: SMS Doğrulamasını Otomatize Edin

SMSCode API Geliştirici Rehberi: SMS Doğrulamasını Otomatize Edin

TL;DR: SMSCode API, SMS doğrulama akışlarını tamamen otomatize etmenizi sağlar. REST tabanlı bu API ile katalogdan uygun bir ürün seçebilir, numara kiralayabilir, sipariş durumunu izleyebilir ve izin verildiğinde siparişi iptal edebilirsiniz. Bearer token ile kimlik doğrulama yapılır; ücretli oluşturma isteğinin kimliği ve gövdesi belirsiz bir sonuçtan sonra değiştirilmez.


Web panelinden manuel numara kiralama, tek seferlik işlemler için gayet uygundur. Ancak büyük hacimde SMS doğrulaması yapıyorsanız, otomatik test akışları kuruyorsanız ya da ürününüze SMS doğrulama özelliği entegre etmek istiyorsanız, API kullanımı kaçınılmazdır.

SMSCode API, geliştiricilere odaklanarak tasarlanmış minimal ve güçlü bir REST arayüzüdür. Bu rehberde API’yi en başından ele alıyoruz: kimlik doğrulama, katalog ve sipariş akışı, Python ve JavaScript örnekleri, hata yönetimi, rate limiting ve üretim ortamı için öneriler. Bu rehberi bitirdiğinizde, kendi entegrasyonunuzu sıfırdan yazabilecek bilgiye sahip olacaksınız.

API’ye Başlamadan Önce: Gereksinimler

Hesap ve API Anahtarı

Henüz hesabınız yoksa SMSCode’a kayıt olun. Kayıt yalnızca e-posta gerektirir, abonelik yoktur.

Hesap oluşturduktan sonra:

  1. Dashboard’a giriş yapın
  2. Sağ üst menüden Hesap Ayarları bölümüne gidin
  3. API Anahtarı sekmesini açın
  4. “Yeni API Anahtarı Oluştur” butonuna tıklayın
  5. Anahtarı hemen kopyalayın ve güvenli bir yerde saklayın

Önemli güvenlik notu: API anahtarı yalnızca oluşturulduğu anda tam olarak görüntülenir. Kaybederseniz iptal edip yenisini oluşturmanız gerekir. Anahtarı kaynak koduna, Git commit geçmişine veya log dosyasına yazmayın. Ortam değişkeni (environment variable) veya bir secrets yönetim sistemi (HashiCorp Vault, AWS Secrets Manager) kullanın.

Base URL ve Kimlik Doğrulama

Tüm API istekleri şu base URL’ye yapılır:

https://api.smscode.gg/v1

Her istekte kimlik doğrulama için Authorization başlığı zorunludur:

Authorization: Bearer YOUR_API_KEY

POST ve PATCH isteklerinde Content-Type: application/json başlığı da eklenmelidir.

Temel API Akışı

SMS doğrulama otomasyonu şu akışı izler:

1. GET  /catalog/products   → Uygun catalog_product_id değerini seç
2. POST /orders/create      → Sabit anahtar ve gövdeyle numara kirala
3. GET  /orders/{id}        → Sipariş durumunu ve SMS'i izle
4. POST /orders/cancel      → Yalnızca can_cancel=true ise iptal et

Katalog seçimi satın alma isteğinden önce tamamlanır. Oluşturma sonucu belirsizse yeni ülke, ürün, gövde veya idempotency anahtarına geçilmez; aynı mantıksal istek uzlaştırılır.

Adım 1: Numara Kiralama

Önce GET /v1/catalog/products?country_id=7&platform_id=1 ile uygun ve kullanılabilir bir ürün seçin. Ardından seçtiğiniz catalog_product_id için tek bir mantıksal POST isteği gönderin. Aşağıdaki anahtar ve JSON gövdesi istek sonucunu kesin olarak öğrenene kadar birlikte ve byte-for-byte aynı kalmalıdır:

POST /v1/orders/create HTTP/1.1
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
Idempotency-Key: order-example-001

{
  "catalog_product_id": 88,
  "quantity": 1
}

Başarılı yanıt (HTTP 200):

{
  "success": true,
  "data": {
    "orders": [
      {
        "id": 90210,
        "status": "ACTIVE",
        "phone_number": "+905551234567",
        "otp_code": null,
        "otp_received_at": null,
        "expires_at": "2026-03-16T10:20:00Z",
        "failed_reason": null,
        "product_id": 1024,
        "catalog_product_id": 88,
        "operator_id": null,
        "operator_name": null,
        "amount": 750000,
        "can_finish": false,
        "can_resend": false,
        "can_cancel": false,
        "can_replace": false,
        "can_reactivate": false,
        "resend_available_at": null,
        "cancel_available_at": "2026-03-16T10:02:00Z",
        "replace_available_at": "2026-03-16T10:02:00Z"
      }
    ],
    "failed_count": 0
  }
}

Yanıttaki alan açıklamaları:

  • data.orders[0].id: Sonraki adımlarda kullanacağınız sipariş kimliği. Kayıt altına alın.
  • phone_number: Atama tamamlandıysa ülke koduyla birlikte kullanacağınız numaradır; atama tamamlanana kadar opsiyonel/nullable olabilir.
  • can_cancel: O an iptal isteği gönderilip gönderilemeyeceğini belirleyen sunucu kararıdır.

Çözümlenmiş bir create yanıtında phone_number eksik, null veya boşsa aynı tamsayı id ile en fazla bir adet timeout’lu GET /v1/orders/{id} yapın. Alan hâlâ boş olmayan bir string değilse yeni bir ücretli create göndermeden yerel pending_assignment sonucunda durun; numarayı yalnızca atama tamamlandıktan sonra hedef platformda kullanın.

Desteklenen Ülke Kodları

Katalog sorgusunda ülke ve platform kimlikleri kullanılır. Aşağıdaki kodlar yalnızca ürünü seçerken insanların okuyacağı referanslardır; oluşturma gövdesine ülke veya platform kodu gönderilmez:

Kod Ülke Yaygın Kullanım
tr Türkiye WhatsApp, Trendyol, yerel platformlar
us ABD Google, OpenAI, büyük küresel platformlar
ua Ukrayna Telegram, WhatsApp (ekonomik)
pl Polonya Instagram, WhatsApp (Avrupa)
de Almanya Kripto borsaları, fintech
gb İngiltere Fintech, bankacılık uygulamaları
ru Rusya Telegram, VK
br Brezilya WhatsApp, Mercado Libre

Tam liste ve güncel stok durumu için sanal numara kataloğuna bakın.

Desteklenen Platform (service) Kodları

Platform kodu, numarayı hangi hizmet için kullanacağınızı belirtir:

  • Sosyal medya: google, whatsapp, telegram, instagram, facebook, twitter, tiktok, snapchat
  • Teknoloji: openai, steam, discord, spotify, netflix, apple
  • Kripto/Finans: binance, coinbase, kraken, paypal, wise
  • E-ticaret: amazon, ebay

Tam platform listesi ve kullanılabilir ürünler GET /v1/catalog/products?country_id=7&platform_id=1 endpoint’inden alınabilir. Dönen kayıtlarda catalog_product_id ve available alanlarını kullanın.

Adım 2: SMS Bekleme ve Okuma

Numarayı platforma girdikten sonra OTP kodunu beklemek için bu endpoint’i düzenli aralıklarla sorgulayın (polling):

GET /v1/orders/90210 HTTP/1.1
Authorization: Bearer YOUR_API_KEY

SMS henüz gelmediğinde V1OrderSummary projeksiyonu (seçili alanlar; tam wire response değildir):

{
  "success": true,
  "data": {
    "status": "ACTIVE",
    "otp_code": null,
    "otp_message": null,
    "sms_revision": 0,
    "can_cancel": true
  }
}

SMS geldiğinde V1OrderSummary projeksiyonu (seçili alanlar; tam wire response değildir):

{
  "success": true,
  "data": {
    "status": "OTP_RECEIVED",
    "otp_code": "847291",
    "otp_message": "Your Google verification code is 847291. Don't share it with anyone.",
    "sms_revision": 1,
    "can_cancel": false
  }
}

otp_code alanı OTP_RECEIVED durumunda bile null olabilir; örneğin sağlayıcı yalnızca bir giriş bağlantısı gönderebilir. Her sipariş için last_seen_revision = -1 ile başlayın. Yalnızca boolean olmayan açık bir tamsayı sms_revision önceki değerden kesin olarak büyük ve otp_message boş olmayan bir string ise mesajı tüketip revision durumunu ilerletin. Bunu status veya otp_code değerinden bağımsız olarak ve COMPLETED, CANCELED ya da EXPIRED kontrolünden önce yapın. Geçersiz, eski/eşit revision veya boş mesaj durumu ilerletmez.

Polling Stratejisi

Uygulamanız için sınırlı bir polling aralığı ve toplam timeout belirleyin. 429 yanıtında pozitif bir Retry-After değerine uyun; başlık yoksa veya geçersizse sınırlı bir fallback gecikmesi kullanın. Yerel aralıkları API’nin garanti ettiği bir kota veya teslim süresi olarak sunmayın.

Python’da polling örneği:

import time
import requests

API_KEY = "your_api_key_here"  # Gerçekte os.environ.get("SMSCODE_API_KEY") kullanın
BASE_URL = "https://api.smscode.gg/v1"
ORDER_TIMEOUT = (5, 30)
HEADERS = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json"
}

def poll_order_message(order_id, timeout=120, interval=3):
    """
    SMS gelene kadar bekle.

    Args:
        order_id: Sipariş kimliği
        timeout: Toplam bekleme süresi (saniye)
        interval: Sorgulama aralığı (saniye)

    Returns:
        Yeni SMS teslimat alanları (dict) veya None (timeout'ta)
    """
    deadline = time.time() + timeout
    last_seen_revision = -1
    while time.time() < deadline:
        response = requests.get(
            f"{BASE_URL}/orders/{order_id}",
            headers=HEADERS,
            timeout=ORDER_TIMEOUT,
        )
        response.raise_for_status()

        data = response.json()["data"]
        status = data["status"]
        revision = data.get("sms_revision")
        message = data.get("otp_message")

        # OTP_RECEIVED ve otp_code mesaj tüketimi için kapı değildir.
        if (
            type(revision) is int
            and revision > last_seen_revision
            and isinstance(message, str)
            and message.strip()
        ):
            last_seen_revision = revision
            return {
                "otp_code": data.get("otp_code"),
                "otp_message": message,
                "sms_revision": revision,
            }
        if status in {"COMPLETED", "CANCELED", "EXPIRED"}:
            return None

        time.sleep(interval)

    return None  # Timeout

OTP Kodu Çıkarma

SMS içeriğinden OTP kodunu ayıklamak için düzenli ifade (regex) kullanın:

import re

def extract_otp_from_message(sms_body, min_digits=4, max_digits=8):
    """
    SMS içeriğinden OTP kodunu çıkar.

    Yaygın OTP formatları: 4 haneli, 5 haneli, 6 haneli, 8 haneli
    """
    # Önce sayı-harf kombinasyonunu (alfanümerik OTP) dene
    alphanumeric = re.search(r'\b[A-Z0-9]{6,8}\b', sms_body)
    if alphanumeric and not alphanumeric.group().isdigit():
        return alphanumeric.group()

    # Yalnızca rakamlardan oluşan blok ara
    pattern = rf'\b\d{{{min_digits},{max_digits}}}\b'
    match = re.search(pattern, sms_body)
    return match.group() if match else None

Adım 3: Gerektiğinde İptal Etme

İptal kararını istemci tahminiyle vermeyin. Önce güncel sipariş snapshot’ını okuyun ve yalnızca can_cancel değeri true ise iptal isteği gönderin:

POST /v1/orders/cancel HTTP/1.1
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{
  "id": 90210
}

Yanıt:

{
  "success": true,
  "data": {
    "order_id": 90210,
    "status": "CANCELED",
    "refund_amount": 750000,
    "new_balance": 2000000
  }
}

can_cancel=false ise yeni bir iptal isteği üretmeyin; güncel durumu izlemeye devam edin. Bir siparişin ücret sonucu yalnızca tahmini istemci kurallarıyla çıkarılmamalıdır.

Tam Entegrasyon Örneği: Python

import time
import re
import os
import uuid
import requests
from contextlib import contextmanager

API_KEY = os.environ.get("SMSCODE_API_KEY")
if not API_KEY:
    raise ValueError("SMSCODE_API_KEY ortam değişkeni tanımlı değil")

BASE_URL = "https://api.smscode.gg/v1"
ORDER_TIMEOUT = (5, 30)
HEADERS = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json"
}


def rent_number(catalog_product_id: int) -> dict:
    """Önceden seçilmiş bir katalog ürünü için tek bir sipariş oluştur."""
    request_body = {"catalog_product_id": catalog_product_id, "quantity": 1}
    idempotency_key = str(uuid.uuid4())
    create_headers = {**HEADERS, "Idempotency-Key": idempotency_key}
    response = requests.post(
        f"{BASE_URL}/orders/create",
        headers=create_headers,
        json=request_body,
        timeout=ORDER_TIMEOUT,
    )
    response.raise_for_status()
    order = response.json()["data"]["orders"][0]

    # `phone_number` atama tamamlanana kadar opsiyonel/nullable'dır. Çözümlenmiş
    # create çözümlenmiş kalır: aynı id ile tek bir sınırlı okuma, asla ikinci
    # bir ücretli create.
    if not is_assigned(order.get("phone_number")):
        current = requests.get(
            f"{BASE_URL}/orders/{order['id']}",
            headers=HEADERS,
            timeout=ORDER_TIMEOUT,
        ).json()
        if current.get("success"):
            order["phone_number"] = current["data"].get("phone_number")
    return order


def is_assigned(phone) -> bool:
    return isinstance(phone, str) and bool(phone.strip())


class PendingAssignment(Exception):
    """Numara henüz atanmadı. Sipariş çözümlenmiş ve ücretlendirilmiş kalır;
    mutabakat için `order_id` `args[0]` içinde taşınır, sipariş değiştirilmez."""


def wait_for_sms(order_id: int, timeout: int = 120) -> str | None:
    """SMS gelene kadar bekle, timeout'ta None döndür."""
    deadline = time.time() + timeout
    last_seen_revision = -1
    while time.time() < deadline:
        response = requests.get(
            f"{BASE_URL}/orders/{order_id}",
            headers=HEADERS,
            timeout=ORDER_TIMEOUT,
        )
        response.raise_for_status()
        data = response.json()["data"]

        status = data["status"]
        revision = data.get("sms_revision")
        message = data.get("otp_message")

        # OTP_RECEIVED ve otp_code mesaj tüketimi için kapı değildir.
        if (
            type(revision) is int
            and revision > last_seen_revision
            and isinstance(message, str)
            and message.strip()
        ):
            last_seen_revision = revision
            return message
        if status in {"COMPLETED", "CANCELED", "EXPIRED"}:
            return None

        time.sleep(3)

    return None


def release_number(order_id: int) -> None:
    """Sunucu izin veriyorsa siparişi iptal et."""
    try:
        response = requests.get(
            f"{BASE_URL}/orders/{order_id}",
            headers=HEADERS,
            timeout=ORDER_TIMEOUT,
        )
        response.raise_for_status()
        if response.json()["data"]["can_cancel"]:
            requests.post(
                f"{BASE_URL}/orders/cancel",
                headers=HEADERS,
                json={"id": order_id},
                timeout=ORDER_TIMEOUT,
            ).raise_for_status()
    except Exception:
        pass  # Uygulamada bu hatayı OTP veya anahtar içermeden kaydedin


def extract_otp(sms_body: str) -> str | None:
    """SMS içeriğinden OTP kodunu çıkar."""
    match = re.search(r'\b\d{4,8}\b', sms_body)
    return match.group() if match else None


@contextmanager
def sms_session(catalog_product_id: int):
    """
    SMS doğrulama oturumu context manager.

    Kullanım:
        with sms_session(88) as (phone, get_code):
            # platform.register(phone)
            code = get_code()
    """
    order = rent_number(catalog_product_id)
    order_id = order["id"]
    phone = order.get("phone_number")
    if not is_assigned(phone):
        # pending_assignment: sipariş çözümlenmiş ve ücretlendirilmiş durumda ve
        # öyle kalır. Bu dalda ikinci bir okuma, polling, iptal veya yeni bir
        # ücretli create YOKTUR — ücretlendirilmiş bir siparişi iptal etmek onu
        # değiştirir; `order_id` ile mutabakat operatöre aittir.
        raise PendingAssignment(order_id)

    def get_code(timeout: int = 120) -> str | None:
        sms = wait_for_sms(order_id, timeout)
        return extract_otp(sms) if sms else None

    try:
        yield phone, get_code
    finally:
        release_number(order_id)


# Kullanım örneği
if __name__ == "__main__":
    with sms_session(88) as (phone, get_code):
        print(f"Numara: {phone}")

        # Buraya Google kayıt kodu gelir
        # google_signup(phone)

        otp = get_code(timeout=120)
        if otp:
            print(f"OTP kodu: {otp}")
            # google_verify(otp)
        else:
            print("SMS alınamadı; güncel sipariş durumunu kontrol edin")

Tam Entegrasyon Örneği: JavaScript/Node.js

const { Agent, fetch } = require('undici');
const crypto = require('crypto');

const API_KEY = process.env.SMSCODE_API_KEY;
if (!API_KEY) throw new Error('SMSCODE_API_KEY ortam değişkeni tanımlı değil');

const BASE_URL = 'https://api.smscode.gg/v1';
const orderDispatcher = new Agent({ connectTimeout: 5_000 });
const orderTransport = () => ({
  dispatcher: orderDispatcher,
  signal: AbortSignal.timeout(30_000)
});
const headers = {
  'Authorization': `Bearer ${API_KEY}`,
  'Content-Type': 'application/json'
};

async function rentNumber(catalogProductId) {
  const requestBody = { catalog_product_id: catalogProductId, quantity: 1 };
  const idempotencyKey = crypto.randomUUID();
  const createHeaders = { ...headers, 'Idempotency-Key': idempotencyKey };
  const response = await fetch(`${BASE_URL}/orders/create`, {
    method: 'POST',
    headers: createHeaders,
    body: JSON.stringify(requestBody),
    ...orderTransport()
  });
  const payload = await response.json();
  if (!response.ok || !payload.success) throw new Error(payload.error?.message);
  const order = payload.data.orders[0];

  // `phone_number` atama tamamlanana kadar opsiyonel/nullable'dır. Aynı id ile
  // tek bir sınırlı okuma; asla ikinci bir ücretli create.
  return order;
}

function isAssigned(phone) {
  return typeof phone === 'string' && phone.trim() !== '';
}

async function waitForSMS(orderId, timeout = 120000, interval = 3000) {
  const deadline = Date.now() + timeout;
  let lastSeenRevision = -1;
  while (Date.now() < deadline) {
    const response = await fetch(`${BASE_URL}/orders/${orderId}`, {
      headers,
      ...orderTransport()
    });
    const payload = await response.json();
    const { status, sms_revision: revision, otp_message: message } = payload.data;

    // OTP_RECEIVED ve otp_code mesaj tüketimi için kapı değildir.
    if (
      Number.isInteger(revision) &&
      revision > lastSeenRevision &&
      typeof message === 'string' &&
      message.trim()
    ) {
      lastSeenRevision = revision;
      return message;
    }
    if (['COMPLETED', 'CANCELED', 'EXPIRED'].includes(status)) {
      return null;
    }
    await new Promise(r => setTimeout(r, interval));
  }
  return null;
}

async function releaseNumber(orderId) {
  try {
    const currentResponse = await fetch(`${BASE_URL}/orders/${orderId}`, {
      headers,
      ...orderTransport()
    });
    const current = await currentResponse.json();
    if (current.data.can_cancel) {
      await fetch(`${BASE_URL}/orders/cancel`, {
        method: 'POST',
        headers,
        body: JSON.stringify({ id: orderId }),
        ...orderTransport()
      });
    }
  } catch (_) {
    // Uygulamada bu hatayı OTP veya anahtar içermeden kaydedin
  }
}

function extractOTP(smsBody) {
  const match = smsBody.match(/\b\d{4,8}\b/);
  return match ? match[0] : null;
}

async function withSMSSession(catalogProductId, callback) {
  const order = await rentNumber(catalogProductId);
  const { id: orderId, phone_number: phone } = order;
  if (!isAssigned(phone)) {
    // pending_assignment: sipariş çözümlenmiş ve ücretlendirilmiş durumda ve öyle
    // kalır. Bu dalda ikinci bir okuma, polling, iptal veya yeni bir ücretli
    // create YOKTUR — ücretlendirilmiş bir siparişi iptal etmek onu değiştirir.
    return { kind: 'pending_assignment', orderId };
  }

  const getCode = (timeout = 120000) =>
    waitForSMS(orderId, timeout).then(sms => sms ? extractOTP(sms) : null);

  try {
    return await callback(phone, getCode);
  } finally {
    await releaseNumber(orderId);
  }
}

// Kullanım örneği
(async () => {
  // `withSMSSession` numara atanmamışsa pending_assignment döner. Sonucu atmak
  // ücretlendirilmiş siparişin `orderId`'sini sessizce kaybeder.
  const oturum = await withSMSSession(88, async (phone, getCode) => {
    console.log(`Numara: ${phone}`);

    // Buraya platform kayıt kodu gelir
    // await googleSignup(phone);

    const otp = await getCode();
    if (otp) {
      console.log(`OTP kodu: ${otp}`);
      // await googleVerify(otp);
    } else {
      console.log('SMS alınamadı');
    }
  });

  if (oturum && oturum.kind === 'pending_assignment') {
    // Sipariş çözümlenmiş ve ücretlendirilmiş durumda kalır; numarayı hiçbir
    // yere girmeyin ve ikinci bir sipariş oluşturmayın. Mutabakat `orderId` ile.
    console.log(`pending_assignment: ${oturum.orderId} siparişine numara atanmadı`);
  }
})();

Hata Yönetimi

API her hata için tutarlı bir JSON yapısı döndürür:

{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_BALANCE",
    "message": "Bakiye yetersiz"
  }
}

Yaygın hata kodları ve çözüm yolları:

HTTP Kodu Hata Kodu Neden Çözüm
409 INSUFFICIENT_BALANCE Bakiye yetersiz Dashboard’dan bakiye yükleyin
422 NO_OFFER_AVAILABLE Seçilen katalog ürünü için teklif yok Bu istek kesin olarak sona ermiştir; yeni ürünü ayrı bir işlem olarak seçin
404 NOT_FOUND Geçersiz ya da silinmiş sipariş ID’yi kontrol edin
409 CONFLICT İstek mevcut durumla çakışıyor Güncel sipariş durumunu okuyun
429 RATE_LIMIT_EXCEEDED Çok fazla istek Polling aralığını artırın, backoff uygulayın
422 VALIDATION_ERROR Geçersiz istek gövdesi Katalogdan geçerli ürün kimliğini alın
401 UNAUTHORIZED API anahtarı geçersiz veya eksik Anahtarı kontrol edin

Kapsamlı hata yönetimi örneği (Python):

class SMSCodeError(Exception):
    def __init__(self, code: str, message: str, http_status: int):
        self.code = code
        self.message = message
        self.http_status = http_status
        super().__init__(f"[{http_status}] {code}: {message}")


DEFINITIVE_CREATE_ERRORS = {
    "NO_OFFER_AVAILABLE",
    "VALIDATION_ERROR",
    "PROVIDER_ERROR",
    "IDEMPOTENCY_KEY_REUSED",
}


def classify_create_error(error_code: str) -> str:
    """Ücretli create sonucunu pozitif allowlist ile sınıflandır."""
    if error_code in DEFINITIVE_CREATE_ERRORS:
        return "definitive"
    if error_code == "INSUFFICIENT_BALANCE":
        return "stop"
    if error_code == "REQUEST_IN_PROGRESS":
        return "retry_same_request"
    return "ambiguous"

REQUEST_IN_PROGRESS yalnızca aynı JSON gövdesi ve aynı Idempotency-Key ile sınırlı, gecikmeli yeniden denemeye izin verir. INSUFFICIENT_BALANCE sonrasında ikinci POST gönderilmez. Bilinen diğer kodlar, gelecekte eklenecek kodlar, bozuk JSON ve geçersiz UTF-8 yanıtları belirsiz kabul edilir; istemci yeni ürün veya ülkeye geçmeden uzlaştırma verilerini (endpoint, gövde, anahtar ve toplam deneme sayısı) kaydeder.

Rate Limiting

API 429 döndürdüğünde pozitif Retry-After değerini kullanın:

HTTP/1.1 429 Too Many Requests
Retry-After: 5

Doğrulanmış değeri yapılandırılmış uygulama logunda ayrı bir alan olarak kaydedebilirsiniz:

rate_limited retry_after_seconds=5

Retry-After yoksa, sonlu ve pozitif değilse veya ayrıştırılamıyorsa sınırlı bir fallback gecikmesi kullanın; sabit bir dakika kotası ya da X-RateLimit-* başlığı varsaymayın. Aşağıdaki 60 saniyelik üst sınır istemciye ait yerel bir koruma politikasıdır, API kotası veya SLA değildir. Mutlak toplam süre, beklemelerin yanında devam eden istekleri de kapsar; her GET’in bağlantı ve toplam taşıma timeout’u kalan pozitif bütçeyle sınırlandırılır.

Yalnızca GET polling için exponential backoff (Python):

import time
import random
import math
from urllib3.util import Timeout

def poll_with_retry(
    order_id: int,
    max_retries: int = 3,
    timeout: float = 120,
) -> dict:
    """GET sorgusunu güvenli Retry-After ve mutlak timeout ile yinele."""
    deadline = time.monotonic() + max(0.0, timeout)
    for attempt in range(max_retries):
        remaining_seconds = deadline - time.monotonic()
        if remaining_seconds <= 0:
            break  # Deadline sonrasında yeni GET başlatma.

        total_timeout = min(30.0, remaining_seconds)
        try:
            response = requests.get(
                f"{BASE_URL}/orders/{order_id}",
                headers=HEADERS,
                timeout=Timeout(
                    connect=min(5.0, total_timeout),
                    total=total_timeout,
                ),
            )
            response.raise_for_status()
            return response.json()["data"]
        except requests.HTTPError as e:
            if e.response.status_code == 429:
                raw_retry_after = e.response.headers.get("Retry-After")
                fallback_wait = min(
                    60.0,
                    max(1.0, (2 ** attempt) + random.uniform(0, 1)),
                )
                try:
                    parsed_retry_after = float(raw_retry_after)
                except (TypeError, ValueError):
                    parsed_retry_after = 0.0
                wait = (
                    min(60.0, max(1.0, parsed_retry_after))
                    if math.isfinite(parsed_retry_after) and parsed_retry_after > 0
                    else fallback_wait
                )
            elif e.response.status_code in (500, 502, 503, 504):
                wait = min(60.0, max(1.0, float(2 ** attempt)))
            else:
                raise

        remaining_seconds = deadline - time.monotonic()
        if remaining_seconds <= 0:
            break
        time.sleep(min(wait, remaining_seconds))

    raise RuntimeError(f"{max_retries} durum sorgusunda başarısız olundu")

Bakiye Yönetimi

API ile bakiye durumunu programatik olarak takip edebilirsiniz:

GET /v1/balance HTTP/1.1
Authorization: Bearer YOUR_API_KEY

Yanıt:

{
  "success": true,
  "data": {
    "currency": "IDR",
    "balance": 1250000
  }
}

Otomatik bakiye uyarısı (Python):

import smtplib  # Ya da tercih ettiğiniz bildirim servisini kullanın

BALANCE_THRESHOLD = 100000  # IDR cinsinden uyarı eşiği

def check_balance_and_alert():
    response = requests.get(f"{BASE_URL}/balance", headers=HEADERS)
    response.raise_for_status()

    balance = response.json()["data"]["balance"]

    if balance < BALANCE_THRESHOLD:
        print(f"UYARI: Bakiye düşük! Mevcut: Rp {balance:,}")
        # E-posta veya Slack bildirimi gönderin

    return balance

Üretim Ortamı için En İyi Pratikler

Yapılandırma yönetimi:

API anahtarınızı ve diğer hassas değerleri ortam değişkenleri olarak saklayın. Python için python-dotenv, Node.js için dotenv kütüphanelerini kullanabilirsiniz. Bulut ortamlarında (AWS, GCP, Azure) yerel secret manager servislerini tercih edin.

Loglama stratejisi:

Her numara kiralama ve SMS alma işlemini loglayın. Ancak log satırlarında API anahtarını ve OTP kodunu asla yazmayın:

import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def rent_number_logged(catalog_product_id: int) -> dict:
    logger.info(f"Numara kiralama başlıyor: catalog_product_id={catalog_product_id}")
    order = rent_number(catalog_product_id)
    logger.info(f"Numara kiralandı: order_id={order['id']}")
    return order

Eşzamanlılık yönetimi:

Paralel olarak birden fazla numara kiralamak istiyorsanız, Python’da asyncio veya concurrent.futures, Node.js’de Promise.all kullanabilirsiniz. Ancak eşzamanlı aktif sipariş sayısına dikkat edin — çok sayıda paralel istek rate limit’e takılabilir.

Ürün değiştirme stratejisi:

Katalogdan alternatif bir ürün seçmek yalnızca önceki create isteğinin kesin olarak sona erdiği biliniyorsa yeni bir kullanıcı kararı olarak yapılmalıdır. Bozuk, bilinmeyen veya belirsiz bir yanıttan sonra ülkeyi, ürünü, gövdeyi ya da idempotency anahtarını otomatik olarak değiştirmeyin.

Monitoring ve alerting:

Üretim sistemlerinde başarı oranını, ortalama SMS teslimat süresini ve hata oranını izleyin. Başarı oranı belirli bir eşiğin altına düştüğünde uyarı alın.

Kurumsal Kullanım Senaryoları

Otomatik test altyapısı:

Yazılım geliştirme süreçlerinde SMS doğrulama içeren akışları CI/CD pipeline’ına entegre edebilirsiniz. Her test gerçek bir sipariş oluşturur; temizlik adımı yalnızca güncel snapshot’ta can_cancel=true olduğunda iptal isteği gönderir.

# pytest test örneği
import pytest

@pytest.fixture
def verified_account():
    with sms_session(88) as (phone, get_code):
        account = google_api.create_account(phone)
        code = get_code()
        assert code is not None, "SMS kodu alınamadı"
        google_api.verify(account, code)
        yield account
        # Fixture bittikten sonra hesap temizleme
        google_api.delete_account(account)

Müşteri onboarding otomasyonu:

Toplu müşteri hesabı oluşturma süreçlerinde her kullanıcı için ayrı SMS doğrulaması gerekiyorsa API kullanımı tek pratik yoldur.

Pazar araştırması:

Farklı ülke numaralarıyla platform davranışlarını test etmek, bölgesel fiyat veya içerik farklılıklarını analiz etmek için kullanılır.

Güvenlik testi:

Kendi uygulamanızın SMS doğrulama akışlarını gerçekçi test verileriyle doğrulamak için.

FAQ

API anahtarım ele geçirilirse ne yapmalıyım?

Hesap ayarlarından API anahtarını hemen iptal edin ve yeni bir tane oluşturun. İptal işlemi anlıktır — eski anahtar derhal çalışmaz hale gelir. Anahtarı kaynak koduna, commit geçmişine veya log dosyasına yazmayın. .env dosyası + .gitignore kombinasyonu minimum güvenlik gerekliliğidir. Git geçmişinizde anahtar varsa, geçmişi yeniden yazmak yerine anahtarı iptal etmek çok daha pratiktir.

Aynı anda kaç paralel sipariş verilebilir?

Hesap bakiyesi ve hesap limiti kısıtlar. Standart hesaplarda orta düzey eşzamanlılık desteklenir. 50’den fazla paralel sipariş veya kurumsal hacimde otomasyon ihtiyacı için destek ekibiyle iletişime geçin.

API Webhook destekliyor mu?

Evet. Alıcı X-Webhook-Signature başlığındaki sha256={hex} imzasını, istek gövdesinin ayrıştırılmamış ham byte’ları üzerinde doğrulamalıdır. Karşılaştırmayı sabit sürede yapın; doğrulama başarılı olmadan JSON ayrıştırmayın veya olayı işlemeyin. order.otp_received, order.completed, order.expired, order.canceled ve webhook.test olaylarını kalıcı olarak kaydedin ya da dayanıklı bir kuyruğa ekleyin; ancak bu işlem başarıyla tamamlandıktan sonra 2xx yanıtı verin.

SMS Activate uyumlu API ile SMSCode arasındaki fark nedir?

SMSCode’un herkese açık API’si, bu rehberde açıklanan api.smscode.gg/v1 REST sözleşmesidir; SMS Activate protokolünün doğrudan yerine geçen uyumlu bir müşteri endpointi yoktur. Mevcut bir entegrasyon; query-string kimlik doğrulamasını Bearer başlığına, action çağrılarını REST endpointlerine, metin yanıtlarını JSON envelope’larına ve servis/aktivasyon kimliklerini SMSCode ürün/sipariş kimliklerine uyarlamalıdır. Yalnızca base URL’yi değiştirmek yeterli değildir.

API test ortamı (sandbox) var mı?

Ayrı bir sandbox kredisi yoktur. Gerçek ortamda katalogdan düşük maliyetli etkin bir ürün seçin, ücretli create sayısını sınırlayın ve yeni bir sipariş oluşturmadan önce belirsiz sonucu aynı anahtar ve aynı gövdeyle uzlaştırın.

Polling yerine SMS’i daha hızlı almak için ne yapılabilir?

Sınırlı bir polling aralığı kullanın ve 429 yanıtında geçerli Retry-After değerine uyun. Webhook kullanıyorsanız da aynı sipariş için periyodik uzlaştırma yapın: webhook teslimatı polling ihtiyacını azaltır, fakat kalıcı durum kontrolünün yerini tamamen almaz.

SMSCode'i denemeye hazır mısınız?

Hesap oluşturun ve ilk sanal numaranızı iki dakikadan kısa sürede alın.

Başlayın →