Bulk WHOIS API for High-Volume Domain Lookups
Process thousands or millions of WHOIS lookups with normalized JSON, transparent pricing, and plans from $10/mo up to 900 req/min.
Start with 1,000 free requests. No credit card required.
curl -s "https://whoisjson.com/api/v1/whois?domain=example.com" \
-H "Authorization: TOKEN=YOUR_API_KEY"Volume Pricing
Fixed Quota — pay for what you use
| Plan | Price / mo | Requests / mo | Cost per 1k |
|---|---|---|---|
| Basic | $0 | 1,000 | $0 |
| Pro | $10 | 30,000 | $0.33 |
| Ultra | $30 | 150,000 | $0.20 |
| Scale | $50 | 1,000,000 | $0.05 |
Unlimited Plans — no fixed monthly quota, limited only by rate limit
| Plan | Price / mo | Rate Limit | Cost / 1k at max * |
|---|---|---|---|
| Mega | $80 | 100 req/min | ~$0.023 |
| Giga | $120 | 200 req/min | ~$0.017 |
| Tera | $200 | 300 req/min | ~$0.019 |
| Atlas | $600 | 900 req/min | ~$0.019 |
* Cost/1k at continuous full-rate usage — actual cost is lower at partial utilization.
Need a free tier first? Get 1,000 requests/month at no cost →
Rate Limits by Plan
Fixed Quota Plans
| Plan | Monthly Quota | Rate Limit | Cache TTL |
|---|---|---|---|
| Basic | 1,000 req/mo | 20 req/min | 3 hours |
| Pro | 30,000 req/mo | 40 req/min | 3 hours |
| Ultra | 150,000 req/mo | 60 req/min | 3 hours |
| Scale | 1,000,000 req/mo | 100 req/min | 3 hours |
Unlimited Plans
| Plan | Max req / month * | Rate Limit | Cache TTL |
|---|---|---|---|
| Mega | ~3,456,000 | 100 req/min | 3 hours |
| Giga | ~6,912,000 | 200 req/min | 3 hours |
| Tera | ~10,368,000 | 300 req/min | 3 hours |
| Atlas | ~31,104,000 | 900 req/min | 3 hours |
* Max at continuous full-rate usage. Add_forceRefresh=1 to bypass cache (costs 2× credits). All endpoints share the same quota. Full plan details →
Built for Pipelines, Not Just One-Off Lookups
Consistent schema across 1,500+ TLDs
Every response uses the same field names — registrar, created, expires, status, nameserver — regardless of whether the data came from a legacy WHOIS server or an RDAP endpoint. No per-TLD parsing logic, no schema branching in your pipeline. Thesource field tells you which protocol was used; RDAP responses include additional enriched fields (age,expiration.daysLeft,nsAnalysis) that your code can use when present.
Predictable rate limits, standard back-off
Rate limits are documented, fixed per plan, and enforced on a rolling 60-second window. A429 response means slow down — not that your key is revoked. Implement exponential back-off and your pipeline recovers automatically. TheRemaining-Requests response header is present in every reply so you can pause before hitting the monthly cap rather than discovering it mid-run.
One key for the full domain intelligence stack
The same API token also gives you access to DNS record lookups, SSL certificate checks, real-time availability, subdomain discovery, and change monitoring. One HTTP client, one auth header, six tools. If your pipeline needs WHOIS + DNS + SSL in a single pass, you don't sign up anywhere else.
Caching that works with bulk workloads
Responses are cached for 3 hours, which matters when you're running the same domain list more than once (monitoring sweeps, reprocessing failures). Cache hits don't count against your monthly quota. When freshness is required, add_forceRefresh=1 to any request — it bypasses the cache and costs 2× credits, so use it only where it changes the outcome.
Bulk WHOIS in Node.js
Sequential processing with rate-limit pacing and exponential back-off on 429.
const API_KEY = 'YOUR_API_KEY';
const RATE_LIMIT = 40; // req/min — Pro:40, Ultra:60, Scale/Mega:100, Atlas:900
const domains = ['example.com', 'github.com', 'google.com'];
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function whoisLookup(domain, retries = 3) {
const url = `https://whoisjson.com/api/v1/whois?domain=${encodeURIComponent(domain)}`;
for (let attempt = 1; attempt <= retries; attempt++) {
const res = await fetch(url, {
headers: { Authorization: `TOKEN=${API_KEY}` },
});
if (res.status === 429) { // rate-limited — back off
await sleep(2 ** attempt * 1000);
continue;
}
if (!res.ok) throw new Error(`HTTP ${res.status} for ${domain}`);
return res.json();
}
throw new Error(`${domain}: exceeded retry limit`);
}
async function bulkWhois(domains) {
const results = [];
const interval = 60_000 / RATE_LIMIT; // ms between requests
for (const domain of domains) {
const t0 = Date.now();
try {
const data = await whoisLookup(domain);
results.push({ domain, data });
console.log(`[ok] ${domain} registered=${data.registered}`);
} catch (err) {
results.push({ domain, error: err.message });
console.error(`[err] ${err.message}`);
}
const elapsed = Date.now() - t0;
if (elapsed < interval) await sleep(interval - elapsed);
}
return results;
}
bulkWhois(domains).then((r) =>
console.log(`Done: ${r.length} lookups`)
);
Bulk WHOIS in Python
Thread pool for parallelism, shared rate-limit pacing, exponential back-off on 429.
import time
import requests
from concurrent.futures import ThreadPoolExecutor, as_completed
API_KEY = 'YOUR_API_KEY'
MAX_WORKERS = 5 # concurrent threads
RATE_LIMIT = 40 # req/min — Pro:40, Ultra:60, Scale/Mega:100, Atlas:900
session = requests.Session()
session.headers['Authorization'] = f'TOKEN={API_KEY}'
def whois_lookup(domain: str, retries: int = 3) -> dict:
url = f'https://whoisjson.com/api/v1/whois?domain={domain}'
for attempt in range(1, retries + 1):
try:
r = session.get(url, timeout=10)
if r.status_code == 429: # rate-limited — back off
time.sleep(2 ** attempt)
continue
r.raise_for_status()
return r.json()
except requests.RequestException as exc:
if attempt == retries:
raise
time.sleep(attempt)
raise RuntimeError(f'{domain}: exceeded retry limit')
def bulk_whois(domains: list[str]) -> list[dict]:
results = []
interval = 60.0 / RATE_LIMIT # seconds between requests
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as pool:
futures = {pool.submit(whois_lookup, d): d for d in domains}
for future in as_completed(futures):
domain = futures[future]
try:
data = future.result()
results.append({'domain': domain, 'data': data})
print(f'[ok] {domain} registered={data.get("registered")}')
except Exception as exc:
results.append({'domain': domain, 'error': str(exc)})
print(f'[err] {exc}')
time.sleep(interval)
return results
if __name__ == '__main__':
domains = ['example.com', 'github.com', 'google.com']
results = bulk_whois(domains)
print(f'Done: {len(results)} lookups')
Annotated JSON Response
Same normalized schema for every TLD and registrar. Thesource field indicates whether data came from WHOIS or RDAP.
{
"name": "example.com", // queried domain, normalized
"registered": true, // false when domain is unregistered
"source": "rdap", // "whois" | "rdap"
"created": "1995-08-14T04:00:00Z",
"changed": "2023-08-14T07:01:31Z",
"expires": "2024-08-13T04:00:00Z",
"age": { // only when source = "rdap"
"days": 10484,
"months": 345,
"years": 28,
"isNewlyRegistered": false, // true when created <= 30 days ago
"isYoung": false // true when created <= 365 days ago
},
"expiration": { // only when source = "rdap"
"daysLeft": 109,
"isExpiringSoon": false,
"isExpired": false
},
"status": [ // EPP status codes
"clientDeleteProhibited",
"clientTransferProhibited"
],
"nameserver": [
"a.iana-servers.net",
"b.iana-servers.net"
],
"registrar": {
"name": "ICANN",
"url": "https://www.icann.org"
},
"contacts": { // null when privacy-shielded
"owner": [{ "name": "...", "email": "...", "country": "..." }],
"admin": [{ "name": "...", "email": "..." }],
"tech": [{ "name": "...", "email": "..." }]
},
"dnssec": "signedDelegation",
"parsedContacts": true,
"whoisserver": "whois.iana.org" // WHOIS server queried
}
Provider Comparison
| Provider | Normalized JSON | Max Rate Limit | Price per 1k | Free Tier |
|---|---|---|---|---|
| WhoisJSON | ✓ Yes | 900 req/min (Atlas) | From $0.02 | 1,000 req/mo |
| WhoisXMLAPI | ✓ Yes | ~3,000 req/min | 30$ for 2k requests | 500 queries |
| Whoxy | ✓ Yes | 1,000 req/min | From $0.40 / 1k | Pay-as-you-go |
| WhoisFreaks | ✓ Yes | 80 req/min (live) | Credit-based | 500 credits |
Data sourced from public pricing and documentation pages, April 2026. WhoisXMLAPI pricing requires login. WhoisFreaks uses a credit system — cost per request varies by endpoint. Verify current rates before choosing a provider.
Bulk WHOIS API — Technical FAQ
requests.Session for connection reuse andThreadPoolExecutor for parallelism. Cap concurrent threads to stay under your plan's rate limit and implement exponential back-off when you receive a429. The example above dispatches up to 5 domains in parallel and sleeps60/RATE_LIMIT seconds between dispatches (1.5 s on Pro, 1 s on Ultra). For datasets over 1M, process in batches of ~10k and checkpoint to disk between batches.429. Custom enterprise limits are available for Atlas-tier customers —contact support.age,expiration,statusAnalysis,nsAnalysis). Thesource field in every response tells you which protocol was used. A small number of TLDs restrict automated queries — for those the API returns a structured error you can log and skip.200 success,400 bad domain format,401 invalid API key,429 rate limit exceeded,5xx server error. On429, apply exponential back-off (see examples above). On5xx, retry after 2–5 seconds. TheRemaining-Requests response header tells you how many credits remain so you can pause before hitting the monthly cap.format=xml to receive XML instead. The schema is consistent across all TLDs and registrars — same field names whether the lookup hits a WHOIS server or an RDAP endpoint. See theAPI reference for the complete field list, or the annotated response above for the most common fields.statusAnalysis (parsed EPP flags),nsAnalysis (nameserver infrastructure),age (days/months/years since registration,isNewlyRegistered,isYoung), andexpiration.daysLeft. Thesource field is always present so your pipeline can branch on data availability. For a deeper comparison seeWHOIS vs RDAP →Start Processing Domains at Scale
1,000 free requests included. No credit card required. Upgrade when you need more.