Rate limits
Stay within request limits and retry safely when the API asks you to wait.
https://api.dehashit.com/api/v1/Rate limits
Limits use a sliding 60-second window. A 429 response is not billed and includes Retry-After in seconds (normally 60). Wait at least that long before trying again; immediate retries use your retry budget without helping the request succeed.
Request limits
| Scope | Limit |
|---|---|
| Look up hashed MSISDN, per API key | 100 / min |
| Look up hashed MSISDN, per source IP | 400 / min |
| Phone validation, per API key | 200 / min |
| Batch creation, per account | 10 / min |
Service keys cannot access batch jobs; API integrations require a tenant-owned API key.
For billable lookups, preserve the same Idempotency-Key and hash across retries, including after a network timeout. Generate the key once per logical lookup, not inside the retry loop. The Python, Node.js, and Go helpers retry 429 responses only; cURL also retries transient server errors. Handle other API responses in your application.
Handling rate limits (429)
# Modern cURL honors Retry-After for transient HTTP errors.
curl -i -X POST https://api.dehashit.com/api/v1/msisdn/decrypt/ \
-H "X-Api-Key: dh_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: payment-order-123" \
-d '{"msisdn_hash": "7132104d6aae9c3fac82095a42c2817952bca48e09d98d5bf4ac08218982fb90"}' \
--retry 3 --retry-max-time 240import time
import requests
def request_with_backoff(hash_hex: str, idempotency_key: str, max_attempts: int = 3):
url = "https://api.dehashit.com/api/v1/msisdn/decrypt/"
headers = {
"X-Api-Key": "dh_live_...",
"Idempotency-Key": idempotency_key, # Keep unchanged across retries.
}
for attempt in range(max_attempts):
res = requests.post(url, headers=headers, json={"msisdn_hash": hash_hex}, timeout=10)
if res.status_code != 429:
return res.json()
if attempt + 1 < max_attempts:
time.sleep(max(1, int(res.headers.get("Retry-After", "60"))))
raise RuntimeError("Rate limit retry budget exceeded.")async function requestWithBackoff(hashHex, idempotencyKey, maxAttempts = 3) {
const url = "https://api.dehashit.com/api/v1/msisdn/decrypt/";
for (let attempt = 0; attempt < maxAttempts; attempt++) {
const res = await fetch(url, {
method: "POST",
headers: {
"X-Api-Key": "dh_live_...",
"Content-Type": "application/json",
"Idempotency-Key": idempotencyKey, // Keep unchanged across retries.
},
body: JSON.stringify({ msisdn_hash: hashHex }),
signal: AbortSignal.timeout(10_000),
});
if (res.status !== 429) return res.json();
const seconds = Math.max(1, Number(res.headers.get("Retry-After")) || 60);
await res.body?.cancel();
if (attempt + 1 < maxAttempts) {
await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
}
}
throw new Error("Rate limit retry budget exceeded.");
}package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"strconv"
"time"
)
func requestWithBackoff(hashHex, idempotencyKey string, maxAttempts int) (*http.Response, error) {
url := "https://api.dehashit.com/api/v1/msisdn/decrypt/"
body, _ := json.Marshal(map[string]string{"msisdn_hash": hashHex})
client := &http.Client{Timeout: 10 * time.Second}
for attempt := 0; attempt < maxAttempts; attempt++ {
req, _ := http.NewRequest("POST", url, bytes.NewReader(body))
req.Header.Set("X-Api-Key", "dh_live_...")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", idempotencyKey)
res, err := client.Do(req)
if err != nil || res.StatusCode != http.StatusTooManyRequests {
return res, err // Caller closes the response body.
}
seconds, err := strconv.Atoi(res.Header.Get("Retry-After"))
if err != nil || seconds < 1 {
seconds = 60
}
res.Body.Close()
if attempt + 1 < maxAttempts {
time.Sleep(time.Duration(seconds) * time.Second)
}
}
return nil, fmt.Errorf("rate limit retry budget exceeded")
}Rate limit response (429)
{
"status": false,
"status_code": 429,
"error_code": "rate_limited",
"message": "Rate limit exceeded. Back off and retry.",
"data": {}
}Requests exceeding these thresholds return HTTP 429 with error_code: "rate_limited" and a Retry-After: 60 header. Spread requests over time instead of sending large bursts. Authentication, invalid-input, balance, and key-conflict errors need application action rather than an automatic retry.
Need a hand with your integration? Contact support.