Umum untuk semua bahasa
Base URL: https://api.nomorotp.id/. Query string format: ?action=X¶m=value. Auth via header X-API-Key. Simpan API key di environment variable:
export NOMOROTP_API_KEY="xxxxxxxxxxxxxxxxxxx"
PHP (native cURL)
Tanpa dependency — pakai cURL bawaan PHP. Cocok integrate ke website reseller yang pakai PHP hosting standard.
<?php
/**
* NomorOTP.id API client — minimal PHP wrapper
* Docs: https://docs.nomorotp.id
*/
class NomorOtpClient {
private $apiKey;
private $baseUrl = 'https://api.nomorotp.id/';
public function __construct(string $apiKey) {
$this->apiKey = $apiKey;
}
private function call(string $method, string $action, array $params = []): array {
$params['action'] = $action;
$url = $this->baseUrl . '?' . http_build_query($params);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => strtoupper($method),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['X-API-Key: ' . $this->apiKey],
CURLOPT_TIMEOUT => 20,
]);
$body = curl_exec($ch);
$err = curl_error($ch);
curl_close($ch);
if ($body === false) throw new RuntimeException("cURL error: $err");
return json_decode($body, true) ?? [];
}
public function balance(): float {
return (float)($this->call('GET', 'getBalance')['balance'] ?? 0);
}
public function rentNumber(string $service, int $country = 6, string $server = 'sh'): array {
return $this->call('POST', 'getNumber', [
'server' => $server,
'service' => $service,
'country' => $country,
]);
}
public function pollStatus(string $activationId): array {
return $this->call('GET', 'getStatus', ['id' => $activationId]);
}
public function finish(string $activationId): array {
return $this->call('POST', 'setStatus', [
'id' => $activationId,
'status' => 6, // 6 = finish
]);
}
public function cancel(string $activationId): array {
return $this->call('POST', 'cancelActivation', ['id' => $activationId]);
}
}
// === Usage ===
$client = new NomorOtpClient(getenv('NOMOROTP_API_KEY'));
echo "Balance: Rp " . number_format($client->balance(), 0, ',', '.') . "\n";
$result = $client->rentNumber('wa', 6);
if (empty($result['success'])) {
die("Failed: " . ($result['error'] ?? 'unknown') . "\n");
}
$act = $result['activation'];
echo "Number: " . $act['phone'] . "\n";
echo "Cost: Rp " . $act['cost'] . "\n";
echo "Activation ID: " . $act['id'] . "\n";
// Poll for SMS every 3 seconds, max 20 minutes
$deadline = time() + $act['expires_in'];
while (time() < $deadline) {
$status = $client->pollStatus($act['id']);
if (($status['status'] ?? '') === 'STATUS_OK') {
echo "OTP received: " . $status['code'] . "\n";
$client->finish($act['id']); // mark as complete
break;
}
sleep(3);
}
Untuk framework-based (Laravel / Symfony), pakai Guzzle:
use GuzzleHttp\Client;
$http = new Client([
'base_uri' => 'https://api.nomorotp.id/',
'headers' => ['X-API-Key' => env('NOMOROTP_API_KEY')],
]);
$response = $http->get('', ['query' => ['action' => 'getBalance']]);
$data = json_decode($response->getBody(), true);
echo $data['balance'];
Node.js (fetch API)
Node 18+ punya fetch built-in. No dependency needed.
// nomorotp.js — minimal Node.js client
// Docs: https://docs.nomorotp.id
const BASE_URL = 'https://api.nomorotp.id/';
class NomorOtpClient {
constructor(apiKey) {
this.apiKey = apiKey;
}
async call(method, action, params = {}) {
const query = new URLSearchParams({ action, ...params });
const res = await fetch(`${BASE_URL}?${query}`, {
method,
headers: { 'X-API-Key': this.apiKey },
});
if (!res.ok && res.status !== 400 && res.status !== 404) {
throw new Error(`HTTP ${res.status}`);
}
return res.json();
}
balance() {
return this.call('GET', 'getBalance').then(r => parseFloat(r.balance || 0));
}
rentNumber(service, country = 6, server = 'sh') {
return this.call('POST', 'getNumber', { server, service, country });
}
pollStatus(activationId) {
return this.call('GET', 'getStatus', { id: activationId });
}
finish(activationId) {
return this.call('POST', 'setStatus', { id: activationId, status: 6 });
}
cancel(activationId) {
return this.call('POST', 'cancelActivation', { id: activationId });
}
}
// === Usage ===
const client = new NomorOtpClient(process.env.NOMOROTP_API_KEY);
const balance = await client.balance();
console.log(`Balance: Rp ${balance.toLocaleString('id-ID')}`);
const result = await client.rentNumber('wa', 6);
if (!result.success) {
throw new Error(`Failed: ${result.error}`);
}
const act = result.activation;
console.log(`Number: ${act.phone}, Cost: Rp ${act.cost}, ID: ${act.id}`);
// Poll dengan interval 3 detik, max 20 menit
const deadline = Date.now() + (act.expires_in * 1000);
while (Date.now() < deadline) {
const status = await client.pollStatus(act.id);
if (status.status === 'STATUS_OK') {
console.log(`OTP received: ${status.code}`);
await client.finish(act.id);
break;
}
await new Promise(r => setTimeout(r, 3000));
}
Buat integrate ke bot Telegram (grammy / node-telegram-bot-api), baca panduannya di blog.
Python (requests)
Butuh requests library — pip install requests.
# nomorotp.py — minimal Python client
# Docs: https://docs.nomorotp.id
import os
import time
import requests
BASE_URL = "https://api.nomorotp.id/"
class NomorOtpClient:
def __init__(self, api_key: str):
self.headers = {"X-API-Key": api_key}
def _call(self, method: str, action: str, **params) -> dict:
params["action"] = action
r = requests.request(
method, BASE_URL, headers=self.headers, params=params, timeout=20
)
# Don't raise on 4xx — API returns valid JSON error body
return r.json()
def balance(self) -> float:
return float(self._call("GET", "getBalance").get("balance", 0))
def rent_number(self, service: str, country: int = 6, server: str = "sh") -> dict:
return self._call(
"POST", "getNumber", server=server, service=service, country=country
)
def poll_status(self, activation_id: str) -> dict:
return self._call("GET", "getStatus", id=activation_id)
def finish(self, activation_id: str) -> dict:
return self._call("POST", "setStatus", id=activation_id, status=6)
def cancel(self, activation_id: str) -> dict:
return self._call("POST", "cancelActivation", id=activation_id)
# === Usage ===
if __name__ == "__main__":
client = NomorOtpClient(os.environ["NOMOROTP_API_KEY"])
print(f"Balance: Rp {client.balance():,.0f}")
result = client.rent_number("wa", 6)
if not result.get("success"):
raise SystemExit(f"Failed: {result.get('error')}")
act = result["activation"]
print(f"Number: {act['phone']}, Cost: Rp {act['cost']}, ID: {act['id']}")
deadline = time.time() + act["expires_in"]
while time.time() < deadline:
status = client.poll_status(act["id"])
if status.get("status") == "STATUS_OK":
print(f"OTP received: {status['code']}")
client.finish(act["id"])
break
time.sleep(3)
Best practices — production checklist
- Environment variable untuk API key — jangan hardcode. Regenerate kalau leak.
- Timeout 20 detik untuk semua HTTP call — jangan hang selamanya.
- Retry backoff exponential pada error 5xx atau rate limit — 3s, 9s, 27s.
- Poll interval 3 detik untuk
getStatus— lebih pendek waste rate limit, lebih panjang delay UX. - Deadline pakai
expires_indari responsegetNumber— biasanya 1200 detik (20 menit). - Selalu
finishsetelah code diterima — cegah retry accidentally + clean up state. - Log activation ID + cost untuk audit trail kalau ada dispute.
- Handle
NO_NUMBERSgracefully — fallback ke country lain atau tampilkan message ke user.
Contoh use case: bot Telegram auto-verify
Bot Telegram yang butuh terima OTP untuk verify user secara otomatis, alur ideal:
- User request verifikasi di bot → bot panggil
rentNumber('tg', 6) - Bot reply nomor virtual + activation ID ke user
- User pakai nomor untuk trigger OTP di app tujuan
- Bot spawn background worker yg poll
getStatussetiap 3 detik - Worker terima kode → forward ke user via bot message
- Selesai — bot call
finish(activation_id)untuk mark complete
Baca panduan reseller untuk strategi hemat volume banyak.
Contribute SDK library resmi
Kalau lu bikin wrapper untuk bahasa yang belum ada (Go, Rust, Ruby, dll), kami welcome PR ke github.com/nomorotp. Best-effort di-feature di docs ini.