Skip to content

cURL / SDK Examples

All examples use the public base URL:

https://api.zelnum.com/api/v1

Replace 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.

Terminal window
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.

Terminal window
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.

Terminal window
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.

Terminal window
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.

The point of Idempotency-Key is that this script is safe to run twice:

Terminal window
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 ;;
esac
done

Never 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.

{
"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.