BULK WHOIS API

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
curl -s "https://whoisjson.com/api/v1/whois?domain=example.com" \
  -H "Authorization: TOKEN=YOUR_API_KEY"
Pricing

Volume Pricing

Fixed Quota — pay for what you use

PlanPrice / moRequests / moCost per 1k
Basic$01,000$0
Pro$1030,000$0.33
Ultra$30150,000$0.20
Scale$501,000,000$0.05

Unlimited Plans — no fixed monthly quota, limited only by rate limit

PlanPrice / moRate LimitCost / 1k at max *
Mega$80100 req/min~$0.023
Giga$120200 req/min~$0.017
Tera$200300 req/min~$0.019
Atlas$600900 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 →

Limits

Rate Limits by Plan

Fixed Quota Plans

PlanMonthly QuotaRate LimitCache TTL
Basic1,000 req/mo20 req/min3 hours
Pro30,000 req/mo40 req/min3 hours
Ultra150,000 req/mo60 req/min3 hours
Scale1,000,000 req/mo100 req/min3 hours

Unlimited Plans

PlanMax req / month *Rate LimitCache TTL
Mega~3,456,000100 req/min3 hours
Giga~6,912,000200 req/min3 hours
Tera~10,368,000300 req/min3 hours
Atlas~31,104,000900 req/min3 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 →

Why WhoisJSON

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.

Node.js

Bulk WHOIS in Node.js

Sequential processing with rate-limit pacing and exponential back-off on 429.

bulk-whois.js
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`)
);
Python

Bulk WHOIS in Python

Thread pool for parallelism, shared rate-limit pacing, exponential back-off on 429.

bulk_whois.py
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')
Response Format

Annotated JSON Response

Same normalized schema for every TLD and registrar. Thesource field indicates whether data came from WHOIS or RDAP.

GET /api/v1/whois?domain=example.com → 200 OK
{
  "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
}
Comparison

Provider Comparison

ProviderNormalized JSONMax Rate LimitPrice per 1kFree Tier
WhoisJSON✓ Yes900 req/min (Atlas)From $0.021,000 req/mo
WhoisXMLAPI✓ Yes~3,000 req/min30$ for 2k requests500 queries
Whoxy✓ Yes1,000 req/minFrom $0.40 / 1kPay-as-you-go
WhoisFreaks✓ Yes80 req/min (live)Credit-based500 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.

FAQ

Bulk WHOIS API — Technical FAQ

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

Rate limits are per API key. Fixed Quota plans: 20 req/min (Basic), 40 req/min (Pro), 60 req/min (Ultra), 100 req/min (Scale). Unlimited plans: 100 req/min (Mega), 200 req/min (Giga), 300 req/min (Tera), 900 req/min (Atlas). Limits are enforced on a rolling 60-second window — exceeding the limit returns a429. Custom enterprise limits are available for Atlas-tier customers —contact support.

Over 1,500 TLDs including all major gTLDs (.com, .net, .org, .io) and country-code TLDs. For TLDs with RDAP support the response includes additional enriched fields (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.

The API uses standard HTTP status codes: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.

JSON by default. Addformat=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.

RDAP (Registration Data Access Protocol) is the modern replacement for WHOIS, with structured JSON at the protocol level. When a registry supports RDAP, WhoisJSON uses it automatically and returns additional 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.