Bulk WHOIS API for Thousands of Domain Lookups
Check thousands of domains through one WHOIS API. Get normalized JSON responses, handle rate limits safely, and scale from 1,000 free monthly requests to high-volume plans built for production workloads.
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.