TL;DR: API SMSCode cho phép tự động hóa quy trình mua số ảo và nhận OTP — chọn sản phẩm, đặt mua số, polling trạng thái, hủy khi server cho phép, và kiểm tra số dư. Mỗi create trả phí dùng một body cùng
Idempotency-Keyổn định cho đến khi kết quả được xác định.
Nếu bạn là developer hoặc đang xây dựng hệ thống cần xác minh số điện thoại tự động ở quy mô lớn, API SMSCode là giải pháp cho phép tích hợp đầy đủ mà không cần thao tác thủ công.
Bài viết này hướng dẫn toàn diện cách sử dụng API — từ xác thực đến xử lý OTP hoàn chỉnh — với ví dụ code thực tế bằng Python, JavaScript, và cURL.
Tại Sao Cần API Số Ảo?
Khi bạn cần xác minh hàng chục hoặc hàng trăm tài khoản, làm thủ công qua dashboard web là không thực tế về mặt thời gian và chi phí nhân lực. API giải quyết vấn đề này bằng cách cho phép:
Tự động hóa hoàn toàn: Không cần con người can thiệp vào từng bước. Hệ thống tự mua số, chờ OTP, nhập OTP, và xử lý kết quả.
Tích hợp vào quy trình CI/CD: Automation testing với số điện thoại thực sự — test đăng ký người dùng, test luồng xác minh OTP, test toàn bộ onboarding flow.
Scale có kiểm soát: Giới hạn concurrency trong ứng dụng, tôn trọng 429/Retry-After, và theo dõi
từng order riêng; API không tự quản lý tải thay cho client.
Xây dựng sản phẩm trên nền tảng số ảo: Dùng số ảo như một infrastructure layer trong ứng dụng của bạn — cung cấp dịch vụ xác minh cho người dùng của bạn.
Quản lý chi phí programmatically: Theo dõi chi tiêu, phân tích tỷ lệ thành công theo dịch vụ/quốc gia, tối ưu ngân sách dựa trên dữ liệu thực.
Bắt Đầu — Lấy API Key
- Đăng ký tài khoản SMSCode nếu chưa có — miễn phí, chỉ cần email
- Đăng nhập vào dashboard
- Vào Account > API để xem và copy API key
- Nạp tiền vào tài khoản trước khi bắt đầu test thực tế
Bảo mật API key:
- Lưu vào biến môi trường (environment variable), không hardcode trong source code
- Không commit vào git repository — thêm vào
.gitignore - Không chia sẻ key trên Slack, email, hoặc bất kỳ kênh nào
- Nếu key bị lộ, tạo key mới ngay trong phần Account
Base URL Và Xác Thực
Base URL: https://api.smscode.gg/v1
Tất cả requests cần header xác thực:
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
Ví dụ cURL kiểm tra kết nối:
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://api.smscode.gg/v1/balance
Các Endpoint Chính
Endpoint 1: Kiểm Tra Số Dư
GET /v1/balance
Response thành công:
{
"success": true,
"data": {
"currency": "IDR",
"balance": 542000
}
}
Dùng endpoint này để:
- Kiểm tra trước khi đặt order để tránh lỗi insufficient balance
- Monitoring số dư trong hệ thống tự động
- Alert khi số dư xuống thấp
Endpoint 2: Xem Catalog — Số Khả Dụng
GET /v1/catalog/products?platform_id=1&country_id=7 HTTP/1.1
Authorization: Bearer YOUR_API_KEY
Parameters:
platform_id— ID nền tảng trong catalogcountry_id— ID quốc gia trong catalog
Response:
{
"success": true,
"data": [
{
"id": 1024,
"name": "WhatsApp - Indonesia",
"catalog_product_id": 88,
"country_id": 7,
"platform_id": 1,
"available": 142,
"price": 5000,
"active": true
},
{
"id": 1025,
"name": "Telegram - Indonesia",
"catalog_product_id": 89,
"country_id": 7,
"platform_id": 2,
"available": 58,
"price": 7000,
"active": true
}
],
"meta": { "page": 1, "limit": 1000, "count": 2 }
}
Kiểm tra available trước khi đặt order. Việc chọn sản phẩm phải hoàn tất trước create; không đổi
sản phẩm sau một response create không rõ ràng.
Endpoint 3: Tạo Đơn Hàng — Mua Số
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
}
Response thành công:
{
"success": true,
"data": {
"orders": [
{
"id": 90210,
"status": "ACTIVE",
"phone_number": "+84987654321",
"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
}
}
Ví dụ trên minh họa một đơn đã được gán số, nhưng phone_number là field optional/nullable cho đến
khi việc gán hoàn tất. Sau khi nhận response:
- Lưu integer
data.orders[0].idđể poll sau. - Chỉ dùng
phone_numbernếu đó là string không rỗng. - Nếu field bị thiếu, là
nullhoặc trống, thực hiện tối đa mộtGET /v1/orders/{id}có timeout với đúng cùngid; nếu vẫn chưa được gán, dừng ở kết quả cục bộpending_assignmentmà không phát lại paid create. - Chỉ sau khi có số hợp lệ mới yêu cầu ứng dụng đích gửi OTP và bắt đầu polling.
Endpoint 4: Polling OTP — Chờ Mã
GET /v1/orders/90210 HTTP/1.1
Authorization: Bearer YOUR_API_KEY
Projection V1OrderSummary khi đang chờ (chỉ các trường được chọn, không phải wire response đầy đủ):
{
"success": true,
"data": {
"status": "ACTIVE",
"otp_code": null,
"otp_message": null,
"sms_revision": 0,
"can_cancel": true
}
}
Projection V1OrderSummary khi OTP đến (chỉ các trường được chọn, không phải wire response đầy đủ):
{
"success": true,
"data": {
"status": "OTP_RECEIVED",
"otp_code": "847291",
"otp_message": "Mã xác minh của bạn là 847291",
"sms_revision": 1,
"can_cancel": false
}
}
Projection V1OrderSummary khi hết hạn (chỉ các trường được chọn, không phải wire response đầy đủ):
{
"success": true,
"data": {
"status": "EXPIRED",
"otp_code": null,
"otp_message": null,
"sms_revision": 1,
"can_cancel": false
}
}
otp_code có thể là null khi status là OTP_RECEIVED. Với từng order, khởi tạo
last_seen_revision = -1; chỉ consume otp_message và cập nhật state khi sms_revision là integer
tường minh, không phải boolean, lớn hơn nghiêm ngặt revision đã thấy và message là string không rỗng.
Thực hiện trước khi xét COMPLETED, CANCELED hoặc EXPIRED, độc lập với OTP_RECEIVED và
otp_code. Revision không hợp lệ, cũ/bằng hoặc message rỗng không làm state tiến lên; không tự suy
luận kết quả tiền từ status.
Endpoint 5: Hủy Đơn Hàng
POST /v1/orders/cancel HTTP/1.1
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{ "id": 90210 }
Response:
{
"success": true,
"data": {
"order_id": 90210,
"status": "CANCELED",
"refund_amount": 750000,
"new_balance": 2000000
}
}
Chỉ gửi endpoint này sau khi snapshot mới nhất trả về can_cancel=true. Nếu can_cancel=false,
tiếp tục đối soát trạng thái thay vì gửi một lệnh hủy dựa trên suy đoán phía client.
Ví Dụ Code Hoàn Chỉnh — Python
import requests
import time
import os
import uuid
API_KEY = os.environ.get("SMSCODE_API_KEY") # Lấy từ env, không hardcode
BASE_URL = "https://api.smscode.gg/v1"
ORDER_TIMEOUT = (5, 30)
HEADERS = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
def check_balance():
"""Kiểm tra số dư tài khoản"""
response = requests.get(f"{BASE_URL}/balance", headers=HEADERS)
data = response.json()
if data["success"]:
return data["data"]["balance"]
raise Exception(f"Lỗi kiểm tra số dư: {data['error']['message']}")
def get_available_products(platform_id: int, country_id: int):
"""Lấy danh sách số khả dụng"""
response = requests.get(
f"{BASE_URL}/catalog/products",
headers=HEADERS,
params={"platform_id": platform_id, "country_id": country_id}
)
data = response.json()
if data["success"]:
products = [
p for p in data["data"]
if p["available"] > 0 and type(p.get("catalog_product_id")) is int
]
return sorted(products, key=lambda x: x["price"]) # Sắp xếp theo giá
return []
def buy_number(catalog_product_id: int):
"""Mua số ảo và trả về thông tin đơn hàng"""
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,
)
data = response.json()
if not data["success"]:
raise Exception(f"Không thể mua số: {data['error']['code']}")
order = data["data"]["orders"][0]
# `phone_number` là optional/nullable cho đến khi việc gán số hoàn tất. Đơn đã
# resolved vẫn resolved: một lần đọc giới hạn với cùng id, không bao giờ tạo
# thêm một create trả phí.
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())
def wait_for_otp(order_id, timeout_seconds=120, poll_interval=5):
"""
Poll OTP cho đến khi nhận được hoặc hết thời gian chờ.
Returns: delivery dict nếu thành công, None nếu thất bại/hết hạn
"""
start = time.time()
last_seen_revision = -1
while time.time() - start < timeout_seconds:
response = requests.get(
f"{BASE_URL}/orders/{order_id}",
headers=HEADERS,
timeout=ORDER_TIMEOUT,
)
data = response.json()["data"]
status = data["status"]
revision = data.get("sms_revision")
message = data.get("otp_message")
# OTP_RECEIVED và otp_code không phải điều kiện để consume message.
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", "EXPIRED", "CANCELED"):
print(f"Phiên {order_id} đã kết thúc với status: {status}")
return None
print(f" Đang chờ OTP... ({int(time.time() - start)}s)")
time.sleep(poll_interval)
print(f"Timeout sau {timeout_seconds}s")
return None
def cancel_order(order_id):
"""Chỉ hủy đơn khi server cho phép"""
current = requests.get(
f"{BASE_URL}/orders/{order_id}",
headers=HEADERS,
timeout=ORDER_TIMEOUT,
)
current.raise_for_status()
if not current.json()["data"]["can_cancel"]:
return current.json()["data"]
response = requests.post(
f"{BASE_URL}/orders/cancel",
headers=HEADERS,
json={"id": order_id},
timeout=ORDER_TIMEOUT,
)
return response.json()
def get_sms_for_service(platform_id: int, country_id: int):
"""
Workflow một lần: chọn sản phẩm, tạo một đơn, rồi chờ tin nhắn.
Returns: (phone_number, delivery) hoặc (None, None)
"""
products = get_available_products(platform_id, country_id)
if not products:
print("Không có sản phẩm khả dụng")
return None, None
product = products[0]
print(f"Mua sản phẩm catalog, giá Rp {product['price']:,}")
order = buy_number(product["catalog_product_id"])
order_id = order["id"]
phone = order.get("phone_number")
if not is_assigned(phone):
# pending_assignment: đơn vẫn resolved và đã bị trừ tiền; đừng nhập số
# vào ứng dụng nào và đừng tạo đơn mới. Trả về `order_id` để đối soát —
# một tuple (None, None) sẽ làm mất id của đơn đã bị trừ tiền.
print(f"pending_assignment: đơn {order_id} chưa được gán số")
return {"kind": "pending_assignment", "order_id": order_id}, None
print(f"Đã mua số: {phone}")
print(" Nhập số này vào ứng dụng và yêu cầu OTP...")
delivery = wait_for_otp(order_id)
if delivery:
print(f" Tin nhắn nhận được: {delivery['otp_message']}")
return phone, delivery
cancel_order(order_id)
return phone, None
# Sử dụng
if __name__ == "__main__":
balance = check_balance()
print(f"Số dư hiện tại: Rp {balance:,}")
phone, delivery = get_sms_for_service(platform_id=1, country_id=7)
if isinstance(phone, dict) and phone.get("kind") == "pending_assignment":
# Đơn vẫn resolved và đã bị trừ tiền; giữ `order_id` để đối soát.
print(f"\npending_assignment: đơn {phone['order_id']} chưa được gán số")
elif phone and delivery:
print(f"\nThành công!\nSố: {phone}\nTin nhắn: {delivery['otp_message']}")
else:
print("\nThất bại — xem log ở trên để debug")
Ví Dụ Code — JavaScript (Node.js)
const { Agent, fetch } = require('undici');
const API_KEY = process.env.SMSCODE_API_KEY;
const crypto = require('crypto');
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 checkBalance() {
const res = await fetch(`${BASE_URL}/balance`, { headers });
const { data } = await res.json();
return data.balance;
}
async function getProducts(platformId, countryId) {
const query = `?platform_id=${platformId}&country_id=${countryId}`;
const res = await fetch(`${BASE_URL}/catalog/products${query}`, { headers });
const { data } = await res.json();
return data.filter(
p => p.available > 0 && Number.isInteger(p.catalog_product_id)
);
}
async function buyNumber(catalogProductId) {
const requestBody = { catalog_product_id: catalogProductId, quantity: 1 };
const idempotencyKey = crypto.randomUUID();
const res = await fetch(`${BASE_URL}/orders/create`, {
method: 'POST',
headers: { ...headers, 'Idempotency-Key': idempotencyKey },
body: JSON.stringify(requestBody),
...orderTransport()
});
const { data } = await res.json();
const order = data.orders[0];
// `phone_number` là optional/nullable cho đến khi việc gán số hoàn tất: một lần
// đọc giới hạn với cùng id, không bao giờ tạo thêm một create trả phí.
return order;
}
function isAssigned(phone) {
return typeof phone === 'string' && phone.trim() !== '';
}
async function waitForOtp(orderId, timeoutMs = 120000, pollMs = 5000) {
const deadline = Date.now() + timeoutMs;
let lastSeenRevision = -1;
while (Date.now() < deadline) {
const res = await fetch(`${BASE_URL}/orders/${orderId}`, {
headers,
...orderTransport()
});
const { data } = await res.json();
const { status, sms_revision: revision, otp_message: message } = data;
// OTP_RECEIVED và otp_code không phải điều kiện để consume message.
if (
Number.isInteger(revision) &&
revision > lastSeenRevision &&
typeof message === 'string' &&
message.trim()
) {
lastSeenRevision = revision;
return {
otpCode: data.otp_code,
otpMessage: message,
smsRevision: revision
};
}
if (['COMPLETED', 'EXPIRED', 'CANCELED'].includes(status)) return null;
await new Promise(r => setTimeout(r, pollMs));
}
return null;
}
async function cancelOrder(orderId) {
const snapshot = await fetch(`${BASE_URL}/orders/${orderId}`, {
headers,
...orderTransport()
});
const { data: current } = await snapshot.json();
if (!current.can_cancel) return current;
const res = await fetch(`${BASE_URL}/orders/cancel`, {
method: 'POST',
body: JSON.stringify({ id: orderId }),
headers,
...orderTransport()
});
return res.json();
}
// Workflow một lần; không tạo đơn thứ hai khi kết quả create chưa rõ ràng.
async function getSmsForService(platformId, countryId) {
const products = await getProducts(platformId, countryId);
if (!products.length) {
console.log('Không có sản phẩm khả dụng');
return null;
}
const product = products.sort((a, b) => a.price - b.price)[0];
const order = await buyNumber(product.catalog_product_id);
if (!isAssigned(order.phone_number)) {
// pending_assignment: đơn vẫn resolved và đã bị trừ tiền; đừng nhập số vào
// đâu cả và đừng tạo đơn mới.
console.log(`pending_assignment: đơn ${order.id} chưa được gán số`);
return { kind: 'pending_assignment', orderId: order.id };
}
console.log(`Đã mua số: ${order.phone_number}`);
const delivery = await waitForOtp(order.id);
if (delivery) {
console.log(`Tin nhắn nhận được: ${delivery.otpMessage}`);
return { phone: order.phone_number, delivery };
}
await cancelOrder(order.id);
return { phone: order.phone_number, delivery: null };
}
// Sử dụng
(async () => {
const balance = await checkBalance();
console.log(`Số dư: Rp ${balance}`);
const result = await getSmsForService(1, 7);
if (result && result.kind === 'pending_assignment') {
// Đơn vẫn resolved và đã bị trừ tiền; giữ `orderId` để đối soát, đừng dùng số.
console.log(`pending_assignment: đơn ${result.orderId} chưa được gán số`);
} else if (result && result.delivery) {
console.log(`\nThành công!\nSố: ${result.phone}\nTin nhắn: ${result.delivery.otpMessage}`);
}
})();
Xử Lý Lỗi
API trả về lỗi theo format nhất quán:
{
"success": false,
"error": {
"code": "INSUFFICIENT_BALANCE",
"message": "Số dư không đủ để thực hiện đơn hàng này"
}
}
Bảng mã lỗi và cách xử lý:
| Code | Ý nghĩa | Cách xử lý |
|---|---|---|
INSUFFICIENT_BALANCE |
Số dư không đủ | Nạp thêm tiền, hoặc check balance trước |
VALIDATION_ERROR |
Body hoặc product ID không hợp lệ | Kiểm tra catalog_product_id từ catalog |
NO_OFFER_AVAILABLE |
Sản phẩm đã chọn không còn offer | Dừng request này; lựa chọn mới phải là thao tác riêng |
CONFLICT |
Request xung đột với trạng thái hiện tại | Đọc lại trạng thái order |
NOT_FOUND |
Order ID không tồn tại | Kiểm tra lại order ID |
RATE_LIMIT_EXCEEDED |
Quá nhiều requests | Giảm tần suất poll, thêm delay |
UNAUTHORIZED |
API key không hợp lệ | Kiểm tra API key trong Account |
SERVICE_UNAVAILABLE |
Dịch vụ tạm thời không khả dụng | Dừng create và đối soát trước khi thử lại |
Best Practices Cho Production
Polling thông minh — không poll quá dày:
Chọn khoảng poll và timeout tổng có giới hạn trong cấu hình ứng dụng. Khi nhận 429, dùng
Retry-After dương; nếu header thiếu hoặc không hợp lệ, dùng một khoảng chờ dự phòng có giới hạn.
Create fail-closed:
Chỉ NO_OFFER_AVAILABLE, VALIDATION_ERROR, PROVIDER_ERROR, và IDEMPOTENCY_KEY_REUSED là kết
quả create dứt khoát cho phép quy trình hiện tại kết thúc. REQUEST_IN_PROGRESS chỉ được retry có
giới hạn với đúng body và Idempotency-Key; INSUFFICIENT_BALANCE dừng sau một POST. Với mã khác,
mã tương lai, JSON hỏng hoặc UTF-8 không hợp lệ, lưu endpoint, body, key và số lần thử để đối soát,
không mua số mới.
Kiểm tra số dư trước khi batch: Trước khi bắt đầu batch lớn (10+ orders), check balance để đảm bảo đủ tiền. Dừng sớm khi số dư thấp hơn threshold.
MIN_BALANCE = 100000 # Ngưỡng IDR tối thiểu
if check_balance() < MIN_BALANCE:
raise Exception("Số dư quá thấp, hãy nạp thêm tiền")
Logging đầy đủ:
Log ID order, catalog_product_id, kết quả và thời gian. Không log API key, OTP hoặc toàn bộ nội dung SMS.
Không hardcode catalog_product_id:
Product ID có thể thay đổi. Luôn gọi catalog API để lấy catalog_product_id hiện tại thay vì hardcode.
Xử lý concurrent orders:
Nếu cần nhiều số cùng lúc, dùng async/await hoặc threading nhưng vẫn giới hạn concurrency trong ứng
dụng và phản ứng với 429; không giả định quota orders/phút cố định.
Health check định kỳ: Trong production, chạy health check mỗi giờ: kiểm tra balance, kiểm tra catalog có số khả dụng không, alert nếu có vấn đề.
Use Cases Thực Tế
Automation Testing: Tích hợp với Selenium, Playwright, hoặc Cypress để test toàn bộ luồng đăng ký người dùng bao gồm OTP verification. Môi trường staging cần tài khoản thật, API SMSCode cho phép điều này một cách programmatic.
# Ví dụ với Playwright
from playwright.sync_api import sync_playwright
from smscode_client import get_sms_for_service # Hàm một lần ở trên
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://app-to-test.com/register")
# Lấy số ảo
phone, delivery = get_sms_for_service(platform_id=1, country_id=7)
# `phone` có thể là kết quả pending_assignment: đơn vẫn resolved và đã bị trừ
# tiền, nên đừng điền gì vào form và giữ lại `order_id` để đối soát.
if isinstance(phone, dict) and phone.get("kind") == "pending_assignment":
print(f"pending_assignment: đơn {phone['order_id']} chưa được gán số")
browser.close()
raise SystemExit(0)
# Đừng dựa vào `assert delivery is not None` để bảo vệ ô nhập số: đó là một
# biến khác. Kiểm tra chính giá trị sẽ được điền vào form.
if not (isinstance(phone, str) and phone.strip()):
print("Không có số khả dụng — không điền gì vào form")
browser.close()
raise SystemExit(0)
assert delivery is not None
# Điền vào form
page.fill("#phone-input", phone)
page.click("#send-otp-btn")
# OTP đã có sẵn
page.fill("#otp-input", delivery["otp_code"] or delivery["otp_message"])
page.click("#verify-btn")
assert page.url.endswith("/dashboard")
Bulk Account Verification:
Với workflow đã được phép, API có thể xử lý nhiều order khi client tự giới hạn concurrency, theo dõi
idempotency và phản ứng đúng với 429; rate limit không được quản lý tự động thay cho client.
SaaS Product — Cung Cấp Xác Minh Cho Khách Hàng: Nếu bạn xây dựng sản phẩm yêu cầu xác minh số điện thoại, API SMSCode cho phép bạn cung cấp dịch vụ này mà không cần đầu tư vào hạ tầng SMS. Bạn là khách hàng của SMSCode, khách hàng của bạn là người dùng cuối.
FAQ
API có hỗ trợ webhook không?
Có. Đọc raw request bytes và kiểm tra X-Webhook-Signature theo định dạng sha256={hex} bằng so
sánh constant-time trước khi parse JSON. Sau khi xác minh, xử lý mọi nhánh sự kiện, persist hoặc
enqueue bền vững, rồi mới trả 2xx. Polling GET /v1/orders/{id} vẫn là cơ chế đối soát khi webhook
đến chậm hoặc bị mất.
Có thể dùng API cho automation testing không?
Có — đây là một trong những use case phổ biến và được hỗ trợ tốt nhất. Tích hợp tốt với Selenium, Playwright, Puppeteer, Cypress. API response time thấp phù hợp với test runner có timeout chặt.
Nên giới hạn số lượng đơn hàng đồng thời thế nào?
Đặt giới hạn concurrency theo workload của bạn. Khi API trả về 429, tôn trọng Retry-After dương
hoặc dùng fallback có giới hạn; không hardcode một quota chung cho mọi tài khoản.
Làm thế nào để test API mà không tốn nhiều tiền?
Dùng sản phẩm đang hoạt động có giá IDR thấp trong catalog để kiểm tra logic trước. Sau mỗi lần tạo đơn có tính phí, hãy đối soát trạng thái trước khi gửi một yêu cầu tạo đơn mới.
API có tài liệu Swagger/OpenAPI không?
Có — truy cập tài liệu API để xem endpoint, schema và ví dụ request/response. Thực thi request bằng HTTP client phía server của bạn; trang tài liệu không phải công cụ gửi request trực tiếp từ trình duyệt.
Có SDK chính thức không?
API REST có thể được gọi trực tiếp từ mọi ngôn ngữ bằng HTTP client chuẩn. Các ví dụ trong bài này minh họa hợp đồng HTTP và không giả định có SDK chính thức.
Sẵn sàng tích hợp? Đăng ký SMSCode và lấy API key miễn phí ngay hôm nay. Tham khảo thêm số ảo cho kinh doanh và developer để khám phá toàn bộ capabilities, hoặc xem hướng dẫn nạp tiền để cấu hình tài khoản cho production.