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:
- Dashboard’a giriş yapın
- Sağ üst menüden Hesap Ayarları bölümüne gidin
- API Anahtarı sekmesini açın
- “Yeni API Anahtarı Oluştur” butonuna tıklayın
- 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.