cURL / SDK Examples
All examples use the public base URL:
https://api.zelnum.com/api/v1Replace YOUR_KEY_HERE with a key from Authentication, and take product IDs from Realtime products — the IDs below are examples and do change as products are added.
One phone number, one product
Section titled “One phone number, one product”curl -X POST "https://api.zelnum.com/api/v1/realtime/check/phone" \ -H "X-API-Key: YOUR_KEY_HERE" \ -H "Idempotency-Key: 8f14e45f-ceea-467a-9f3f-1b2c3d4e5f60" \ -H "Content-Type: application/json" \ -d '{"identifier":"+8613800138000","country":"CN","product_ids":[47]}'country is optional when the number is already in full E.164 form. Include it when you are sending a national number.
One phone number, several products
Section titled “One phone number, several products”curl -X POST "https://api.zelnum.com/api/v1/realtime/check/phone" \ -H "X-API-Key: YOUR_KEY_HERE" \ -H "Idempotency-Key: 5c2f1a90-6b7d-4e8f-9a01-2b3c4d5e6f70" \ -H "Content-Type: application/json" \ -d '{"identifier":"+8613800138000","country":"CN","product_ids":[47,50,51]}'Three products, one request_id, three entries in results, three charges. See Realtime products for the ten-ID ceiling.
One email address
Section titled “One email address”curl -X POST "https://api.zelnum.com/api/v1/realtime/check/email" \ -H "X-API-Key: YOUR_KEY_HERE" \ -H "Idempotency-Key: 0d9c8b7a-6543-4321-fedc-ba9876543210" \ -H "Content-Type: application/json" \ -d '{"identifier":"user@example.com","product_ids":[49]}'Note the absence of country — email products reject it.
Reading only what you need
Section titled “Reading only what you need”curl -s -X POST "https://api.zelnum.com/api/v1/realtime/check/phone" \ -H "X-API-Key: YOUR_KEY_HERE" \ -H "Idempotency-Key: 3a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9" \ -H "Content-Type: application/json" \ -d '{"identifier":"+8613800138000","product_ids":[47,50]}' \ | jq '{status, charged: .total_charged, items: [.results[] | {product: .product, result: .result, billed: .billing_status}]}'Check billing_status per entry, not the HTTP status alone — a 200 can still contain refunded entries. See Billing & refunds.
A retry loop that cannot double-charge
Section titled “A retry loop that cannot double-charge”The point of Idempotency-Key is that this script is safe to run twice:
KEY=$(uuidgen)BODY='{"identifier":"+8613800138000","country":"CN","product_ids":[47]}'
for attempt in 1 2 3; do response=$(curl -s -w '\n%{http_code}' -X POST \ "https://api.zelnum.com/api/v1/realtime/check/phone" \ -H "X-API-Key: YOUR_KEY_HERE" \ -H "Idempotency-Key: $KEY" \ -H "Content-Type: application/json" \ -d "$BODY")
code=$(printf '%s' "$response" | tail -n1) printf '%s' "$response" | sed '$d'
case "$code" in 200) exit 0 ;; # Uncertain outcome: the key is the whole point. Resend the same body. 500|000) sleep 2 ;; # Still running, or throttled: honour Retry-After and keep the same key. 409|429) sleep "$(printf '%s' "$response" | sed '$d' | jq -r '.retry_after // 2')" ;; *) exit 1 ;; esacdoneNever generate a new key to retry a request whose outcome you did not observe — that is exactly how one check gets paid for twice. See Idempotency & retries.
Client examples
The same call — one phone number, two products — in the four languages integrations are usually written in. There is no SDK to install: the API is plain JSON over HTTPS, so your own HTTP client is the client.
Python
import uuid
import requests
response = requests.post(
"https://api.zelnum.com/api/v1/realtime/check/phone",
headers={
"X-API-Key": "YOUR_KEY_HERE",
"Idempotency-Key": str(uuid.uuid4()),
"Content-Type": "application/json",
},
json={
"identifier": "+8613800138000",
"country": "CN",
"product_ids": [47, 50],
},
timeout=30,
)
response.raise_for_status()
body = response.json()
for item in body["results"]:
print(item["product"], item["result"], item["billing_status"]) Node.js
const response = await fetch(
'https://api.zelnum.com/api/v1/realtime/check/phone',
{
method: 'POST',
headers: {
'X-API-Key': 'YOUR_KEY_HERE',
'Idempotency-Key': crypto.randomUUID(),
'Content-Type': 'application/json',
},
body: JSON.stringify({
identifier: '+8613800138000',
country: 'CN',
product_ids: [47, 50],
}),
},
);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const body = await response.json();
for (const item of body.results) {
console.log(item.product, item.result, item.billing_status);
} PHP
$ch = curl_init('https://api.zelnum.com/api/v1/realtime/check/phone');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'X-API-Key: YOUR_KEY_HERE',
'Idempotency-Key: ' . bin2hex(random_bytes(16)),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'identifier' => '+8613800138000',
'country' => 'CN',
'product_ids' => [47, 50],
]),
]);
$body = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
foreach ($body['results'] as $item) {
echo $item['product'], ' ', $item['result'], ' ', $item['billing_status'], PHP_EOL;
} Go
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
)
func main() {
payload, _ := json.Marshal(map[string]any{
"identifier": "+8613800138000",
"country": "CN",
"product_ids": []int{47, 50},
})
req, _ := http.NewRequest(
http.MethodPost,
"https://api.zelnum.com/api/v1/realtime/check/phone",
bytes.NewReader(payload),
)
req.Header.Set("X-API-Key", "YOUR_KEY_HERE")
req.Header.Set("Idempotency-Key", "8f14e45f-ceea-467a-9f3f-1b2c3d4e5f60")
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
var body struct {
Status string `json:"status"`
Results []struct {
Product string `json:"product"`
Result string `json:"result"`
BillingStatus string `json:"billing_status"`
} `json:"results"`
}
if err := json.NewDecoder(res.Body).Decode(&body); err != nil {
log.Fatal(err)
}
for _, item := range body.Results {
fmt.Println(item.Product, item.Result, item.BillingStatus)
}
} Generate the Idempotency-Key, do not hardcode it: uuid.uuid4() in Python, crypto.randomUUID() in Node 18 and later, bin2hex(random_bytes(16)) in PHP, crypto/rand in Go. A constant key makes every request a replay of the first, and you get 409 idempotency_conflict instead of a fresh check.
Response shape
Section titled “Response shape”{ "request_id": "8c1e4f91-7bd8-4d7e-a3ae-3d7e5b0c6c11", "identifier": "+8613800138000", "series": "phone", "status": "succeeded", "request_status": "completed", "replayed": false, "total_reserved": 0.004000, "total_charged": 0.004000, "total_refunded": 0.000000, "charged_amount": 0.004000, "refunded_amount": 0.000000, "results": [ { "product_id": 47, "product": "wavalid-realtime", "result": "valid", "cached": false, "billing_status": "charged", "charged_amount": 0.002000, "refunded_amount": 0.000000, "checked_at": "2026-09-13T10:00:00Z", "error_code": null } ]}For the full field reference see Response fields.