There are three practical ways to run a WHOIS lookup in Python: the python-whois library, direct RDAP calls, and a WHOIS API. If you're a backend or security developer, you're rarely looking up one domain. You're watching a portfolio for expiry, catching newly registered lookalikes before they turn into phishing, or enriching alerts with a domain's age, registrar, and status. All three methods read the same underlying records, so the real question isn't which is correct, it's which one holds up when it runs on a schedule across thousands of domains. This guide covers each with working code and marks the exact point where it stops being dependable, whether that's an uncommon TLD, a high-volume job, or a field that quietly comes back empty.
Key Takeaways
- python-whois is the fastest way to a result for one common domain, but it parses each TLD with hardcoded regex and queries raw WHOIS on port 43, so newer TLDs and high-volume jobs are where it struggles.
- RDAP is the standards-based successor to WHOIS. As of 28 January 2025, ICANN dropped the WHOIS requirement for gTLDs and made RDAP the mandated protocol.
- RDAP returns structured JSON over HTTPS, but you route each query yourself using the IANA bootstrap file, and many country-code TLDs aren't in it.
- Contact data is redacted by default across every method, thanks to GDPR, not because a registrant switched privacy on. Public records return registrar info, dates, name servers, and status codes, not a person's name and address.
- For consistent JSON at scale, a WHOIS API removes the parsing, routing, and rate-limit handling, and a bulk endpoint sends many domains in one request instead of looping.
What's in a WHOIS Record?
A WHOIS record is the registration metadata for a domain: the registrar managing it, the registration, updated, and expiry dates, the name servers, the EPP status codes that show whether the domain is locked (clientTransferProhibited and the like), and contact roles for the registrant, admin, technical, and billing parties. That data drives domain monitoring, brand protection, and phishing or fraud investigation, the same domain-intelligence work that pairs naturally with investigating a suspicious IP address. In practice the contact fields are mostly redacted now (more on that below), so you're usually working with the registrar, dates, name servers, and status codes. Those name servers are the handoff to DNS, where a DNS lookup resolves what they actually serve.
One naming note: WHOIS is the original protocol, and RDAP is its modern replacement. People say "WHOIS lookup" for both, because the data is the same. What changed is how you fetch it. If you need to see how a record looked in the past rather than today, that's a separate job handled by the Domain WHOIS Historical Lookup API.
Three Methods at a Glance
| Method | Best for | Trade-offs |
|---|---|---|
| python-whois library | Quick scripts, one-off checks, common TLDs like .com, .net, .org | Newer TLDs can return partial or empty records; high-volume loops hit port 43 rate limits |
| RDAP (direct) | Structured JSON without a paid service, standards-compliant access | You own the bootstrap routing and retries; many country-code TLDs aren't in the bootstrap |
| WHOIS API | Reliability at scale, bulk jobs, uniform JSON across every TLD | Costs money at scale, and adds a dependency on an outside service |
Method 1: The python-whois Library
python-whois is the most common open-source option, a small library that wraps the classic WHOIS protocol and parses the response into a Python object. Under the hood it resolves the authoritative WHOIS server for the domain's TLD, opens a socket to that server on port 43, sends the domain name, and reads back a block of free-form text. It then runs a regex parser matched to that TLD to turn the text into fields like registrar and creation_date.
For a single domain, this is the fastest path to a result. Install it, call one function, read the fields:
# pip install python-whois
import whois
record = whois.whois("google.com")
print(record.registrar) # MarkMonitor, Inc.
print(record.creation_date) # [datetime(1997, 9, 15, 4, 0, ...), datetime(1997, 9, 15, 7, 0, ...)]
print(record.expiration_date) # [datetime(2028, 9, 14, 4, 0, ...), datetime(2028, 9, 13, 7, 0, ...)]
print(record.name_servers) # ['NS1.GOOGLE.COM', 'NS2.GOOGLE.COM', ...]Here's the full parsed record whois.whois("google.com") returns. The dates come back as lists and the status codes arrive duplicated (registry plus registrar), the exact quirks you clean up after:
{
"domain_name": "GOOGLE.COM",
"registrar": "MarkMonitor, Inc.",
"registrar_url": "http://www.markmonitor.com",
"reseller": null,
"whois_server": "whois.markmonitor.com",
"referral_url": null,
"updated_date": [
"2019-09-09 15:39:04+00:00",
"2024-08-02 02:17:33+00:00"
],
"creation_date": [
"1997-09-15 04:00:00+00:00",
"1997-09-15 07:00:00+00:00"
],
"expiration_date": [
"2028-09-14 04:00:00+00:00",
"2028-09-13 07:00:00+00:00"
],
"name_servers": [
"NS1.GOOGLE.COM",
"NS2.GOOGLE.COM",
"NS3.GOOGLE.COM",
"NS4.GOOGLE.COM"
],
"status": [
"clientDeleteProhibited https://icann.org/epp#clientDeleteProhibited",
"clientTransferProhibited https://icann.org/epp#clientTransferProhibited",
"clientUpdateProhibited https://icann.org/epp#clientUpdateProhibited",
"serverDeleteProhibited https://icann.org/epp#serverDeleteProhibited",
"serverTransferProhibited https://icann.org/epp#serverTransferProhibited",
"serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited",
"clientUpdateProhibited (https://www.icann.org/epp#clientUpdateProhibited)",
"clientTransferProhibited (https://www.icann.org/epp#clientTransferProhibited)",
"clientDeleteProhibited (https://www.icann.org/epp#clientDeleteProhibited)",
"serverUpdateProhibited (https://www.icann.org/epp#serverUpdateProhibited)",
"serverTransferProhibited (https://www.icann.org/epp#serverTransferProhibited)",
"serverDeleteProhibited (https://www.icann.org/epp#serverDeleteProhibited)"
],
"emails": [
"abusecomplaints@markmonitor.com",
"whoisrequest@markmonitor.com"
],
"dnssec": "unsigned",
"name": null,
"org": "Google LLC",
"address": null,
"city": null,
"state": null,
"registrant_postal_code": null,
"country": "US",
"tech_name": null,
"tech_org": null,
"admin_name": null,
"admin_org": null
}Two things trip people up right away. The package installs as python-whois but imports as whois, and a separate, unrelated whois package also exists on PyPI, so installing the wrong one is the most common first mistake. And fields like creation_date and expiration_date sometimes return a single datetime and sometimes a list of them, because the registry and registrar report slightly different values. Normalize before you use them:
def normalize_date(value):
if not value: # None or empty list
return None
return value[0] if isinstance(value, list) else value
created = normalize_date(record.creation_date)
expires = normalize_date(record.expiration_date)
print(f"Created: {created}, Expires: {expires}") # Created: 1997-09-15 04:00:00+00:00, Expires: 2028-09-14 04:00:00+00:00Parsing is inconsistent across TLDs. python-whois doesn't read a structured feed. It runs a regex parser matched to each TLD against free-form text, backed by about 105 hardcoded per-TLD classes plus a generic fallback for everything else. Because every registry formats its output differently, what comes back varies. In our test, .de (heise.de) returned name servers but no registrar and no dates, .com returned its status codes duplicated (registry plus registrar, twelve entries for six real statuses), and .fr (afnic.fr) returned non-standard tokens like associated and not identified instead of EPP status codes. The long tail is worse: .dev and .app (svelte.dev, netlify.app) came back completely empty because the library couldn't reach a WHOIS server for them. None of this throws an exception, you get None or malformed fields, so a batch job logs blanks and moves on.
Port 43 is slow and rule-bound. The library reads plain text off TCP port 43, which registries throttle under their terms of service. One lookup in the run (t.co) took about 12 seconds. Combined with the parsing gaps above, that makes a loop over a few thousand domains the exact pattern these limits exist to stop.
That's usually the cue to step up: not to a better parser, but to a protocol built for structured, automated access instead of text scraping.
Method 2: RDAP, the WHOIS Replacement
On 28 January 2025, ICANN removed the requirement for gTLD registries and registrars to operate a WHOIS service, and made the Registration Data Access Protocol (RDAP) the definitive source for gTLD registration data. Many operators shut their WHOIS servers down soon after. For anything built today, RDAP is the current standard and raw WHOIS is the legacy path.
RDAP returns registration data as JSON over HTTPS, standardized by the IETF in RFC 9082 and RFC 9083. Instead of scraping text, you get typed fields: an events array for registration and expiration dates, a status array of EPP codes, nameservers, and an entities array for the parties on the record.
A query is a plain HTTPS GET to a predictable path. Once you know the registry's base URL, a domain lookup is {base}/domain/{domain}, for example
https://rdap.verisign.com/com/v1/domain/google.comThe same servers answer /nameserver/, /entity/ (registrars and contacts), and /ip/ queries, all defined in RFC 9082.
The one extra step is service discovery. There is no single server. Each TLD is served by its own registry's RDAP endpoint, and you find it through the IANA bootstrap file at data.iana.org/rdap/dns.json, which maps every registered TLD to its RDAP base URL. Read the file, match your domain's TLD, query that server.
import requests
# 1. Load IANA's RDAP bootstrap registry (maps TLD -> RDAP base URL)
bootstrap = requests.get("https://data.iana.org/rdap/dns.json", timeout=20).json()
def find_rdap_base(tld):
for entry in bootstrap["services"]:
tlds, servers = entry[0], entry[1]
if tld in tlds:
return servers[0].rstrip("/")
return None
def rdap_lookup(domain):
tld = domain.rsplit(".", 1)[-1]
base = find_rdap_base(tld)
if base is None:
raise LookupError(f"No RDAP server in the IANA bootstrap for .{tld}")
resp = requests.get(
f"{base}/domain/{domain}",
headers={"Accept": "application/rdap+json"},
timeout=20,
)
resp.raise_for_status()
return resp.json()
for domain in ["google.com", "t.co"]:
try:
data = rdap_lookup(domain)
events = {e.get("eventAction"): e.get("eventDate") for e in data.get("events", [])}
print(domain, "->", data.get("status"), events.get("expiration"))
except LookupError as err:
print(domain, "->", err)Against a covered domain and an uncovered one, the two outcomes look nothing alike:
google.com -> ['client delete prohibited', 'client transfer prohibited', ...] 2028-09-14T04:00:00Z
t.co -> No RDAP server in the IANA bootstrap for .coThe data object behind that first line is the full RDAP record. For google.com it looks like this, typed fields, but nested, and the entities array carries only the registrar:
{
"objectClassName": "domain",
"handle": "2138514_DOMAIN_COM-VRSN",
"ldhName": "GOOGLE.COM",
"links": [
{
"value": "https://rdap.verisign.com/com/v1/domain/GOOGLE.COM",
"rel": "self",
"href": "https://rdap.verisign.com/com/v1/domain/GOOGLE.COM",
"type": "application/rdap+json"
},
{
"value": "https://rdap.markmonitor.com/rdap/domain/GOOGLE.COM",
"rel": "related",
"href": "https://rdap.markmonitor.com/rdap/domain/GOOGLE.COM",
"type": "application/rdap+json"
}
],
"status": [
"client delete prohibited",
"client transfer prohibited",
"client update prohibited",
"server delete prohibited",
"server transfer prohibited",
"server update prohibited"
],
"entities": [
{
"objectClassName": "entity",
"handle": "292",
"roles": [
"registrar"
],
"links": [
{
"href": "http://www.markmonitor.com",
"type": "text/html",
"value": "https://rdap.markmonitor.com/rdap/",
"rel": "about"
}
],
"publicIds": [
{
"type": "IANA Registrar ID",
"identifier": "292"
}
],
"vcardArray": [
"vcard",
[
[
"version",
{},
"text",
"4.0"
],
[
"fn",
{},
"text",
"MarkMonitor Inc."
]
]
],
"entities": [
{
"objectClassName": "entity",
"roles": [
"abuse"
],
"vcardArray": [
"vcard",
[
[
"version",
{},
"text",
"4.0"
],
[
"fn",
{},
"text",
""
],
[
"tel",
{
"type": "voice"
},
"uri",
"tel:+1.2086851750"
],
[
"email",
{},
"text",
"abusecomplaints@markmonitor.com"
]
]
]
}
]
}
],
"events": [
{
"eventAction": "registration",
"eventDate": "1997-09-15T04:00:00Z"
},
{
"eventAction": "expiration",
"eventDate": "2028-09-14T04:00:00Z"
},
{
"eventAction": "last changed",
"eventDate": "2019-09-09T15:39:04Z"
},
{
"eventAction": "last update of RDAP database",
"eventDate": "2026-07-13T07:57:39Z"
}
],
"secureDNS": {
"delegationSigned": false
},
"nameservers": [
{
"objectClassName": "nameserver",
"ldhName": "NS1.GOOGLE.COM"
},
{
"objectClassName": "nameserver",
"ldhName": "NS2.GOOGLE.COM"
},
{
"objectClassName": "nameserver",
"ldhName": "NS3.GOOGLE.COM"
},
{
"objectClassName": "nameserver",
"ldhName": "NS4.GOOGLE.COM"
}
],
"rdapConformance": [
"rdap_level_0",
"icann_rdap_technical_implementation_guide_1",
"icann_rdap_response_profile_1"
],
"notices": [
{
"title": "Terms of Service",
"description": [
"Service subject to Terms of Use."
],
"links": [
{
"href": "https://www.verisign.com/domain-names/registration-data-access-protocol/terms-service/index.xhtml",
"type": "text/html",
"value": "https://rdap.verisign.com/com/v1/domain/google.com",
"rel": "terms-of-service"
}
]
},
{
"title": "Status Codes",
"description": [
"For more information on domain status codes, please visit https://icann.org/epp"
],
"links": [
{
"href": "https://icann.org/epp",
"type": "text/html"
}
]
},
{
"title": "RDDS Inaccuracy Complaint Form",
"description": [
"URL of the ICANN RDDS Inaccuracy Complaint Form: https://icann.org/wicf"
],
"links": [
{
"href": "https://icann.org/wicf",
"type": "text/html",
"value": "https://rdap.verisign.com/com/v1/domain/google.com",
"rel": "help"
}
]
}
]
}Two operational realities come with running this yourself. First, RDAP servers rate limit. Because it's HTTP, the limit is standardized: exceed a server's threshold and you get an HTTP 429 with a Retry-After header telling you how long to wait, so backoff and caching are straightforward to implement. Public aggregators are strict (rdap.org caps at 10 requests per 10 seconds); registry servers set their own thresholds. Second, coverage is uneven, which is the bigger surprise.
RDAP Coverage Is Uneven

Bootstrap coverage is where RDAP bites. Every gTLD is required to offer RDAP, and in testing .com, .org, .net, .xyz, .dev, and .app all resolved and returned records. But .io, .co, .so, and .de have no entry in the IANA bootstrap at all, so a compliant client has nowhere to send the query, even though socket.io, t.co, notion.so, and heise.de are all live, heavily used sites. The failure isn't a dead domain; it's a missing route. Many country-code TLDs sit outside ICANN's contracts and add RDAP on their own timeline, if at all. .io is the one to watch: it's everywhere in developer projects and still isn't in the bootstrap. If your workload spans ccTLDs, plan for the gap rather than meeting it in production. The comparison table further down shows exactly which of these route and which don't.
Contact Data Is Redacted, No Matter How You Fetch It
Across the gTLD domains that returned a full record, the entities array from the registry held only the registrar, with no registrant name, address, phone, or email. This isn't an RDAP shortcoming or something you can code around, and it isn't only in effect when a registrant buys privacy protection. Since GDPR and ICANN's 2018 Temporary Specification, registrars redact personal registrant fields from public WHOIS and RDAP by default, replacing them with values like REDACTED FOR PRIVACY. Most apply that globally rather than trying to separate EU individuals from everyone else.
What still comes through: the registrar, dates, name servers, and status codes always; an organization name and country often, since GDPR covers people, not companies; and fuller detail on some ccTLDs that set their own rules. A .de lookup on heise.de, for instance, returns the full Heise Medien organization and address, because Germany's registry publishes company records. The data behind gTLD redaction is reachable only through gated channels like ICANN's RDRS or a documented request to the registrar. Raw WHOIS, direct RDAP, and every commercial WHOIS API read the same redacted public record, so no method, free or paid, returns personal contact data the registry withholds.
One trap for automated pipelines: redacted fields arrive as text, not empty values. Across our run we saw REDACTED FOR PRIVACY, DATA REDACTED, and REDACTED REGISTRANT, with the exact wording varying by registrar. A truthiness check like if record.registrant_name: treats all of these as real data, so filter on the sentinel strings, not on whether the field is populated.
Method 3: A WHOIS API
The library and the raw protocol both leave you the same chores: parse whatever each registry returns, work out how to reach it, and respect each server's rate limits. A WHOIS API handles all of that on the server side. You send a domain and get back the same JSON shape whether the TLD is .com or something obscure.
The APIFreaks Domain WHOIS Lookup API is a single GET call:
import requests
resp = requests.get(
"https://api.apifreaks.com/v2.0/domain/whois/live",
params={"domainName": "google.com", "apiKey": "YOUR_API_KEY"},
timeout=20,
)
data = resp.json()
print(data["domain_registrar"]["registrar_name"]) # MarkMonitor, Inc
print(data["create_date"], data["expiry_date"]) # 1997-09-15 2028-09-13
print(data["domain_status"]) # ['clientupdateprohibited', 'clienttransferprohibited', ...]Prefer not to hand-roll the request? The official Python SDK wraps the same endpoints. Its version argument defaults to "1.0", so pass version="2.0" to get the response shape shown below:
# pip install apifreaks
from apifreaks import ApifreaksApi
client = ApifreaksApi()
record = client.domain_whois_lookup(
api_key="YOUR_API_KEY", domain_name="google.com", version="2.0"
)AsyncApifreaksApi provides async variants of both calls. The rest of this section uses the requests version, since the fields are identical either way.
The response is flat, normalized JSON, and the domain_registrar block is worth a close look. Beyond the name and IANA ID, it gives you the registrar's ICANN status (accredited), a normalized_name you can match on across records, and both its whois_server and rdap_server, so the RDAP endpoint you routed to by hand in Method 2 is resolved for you. Contacts arrive as structured registrant_contact and technical_contact objects instead of loose text, and the payload actually carries two records: the top level is the registrar's view, while registry_data is the registry's own authoritative copy, each with its full raw WHOIS text kept intact.
{
"status": true,
"domain_name": "google.com",
"query_time": "2026-07-13 08:05:54",
"whois_server": "whois.verisign-grs.com",
"domain_registered": "yes",
"create_date": "1997-09-15",
"update_date": "2024-08-02",
"expiry_date": "2028-09-13",
"domain_registrar": {
"iana_id": "292",
"status": "accredited",
"registrar_name": "MarkMonitor, Inc",
"normalized_name": "markmonitor",
"whois_server": "whois.markmonitor.com",
"rdap_server": "https://rdap.markmonitor.com/rdap/",
"website_url": "https://www.markmonitor.com/",
"email_address": "https://corp.markmonitor.com/domain/ui/abuse-report",
"phone_number": "+12086851750"
},
"registrant_contact": {
"company": "Google LLC",
"country_name": "United States",
"country_code": "US",
"email_address": "Select Request Email Form at https://domains.markmonitor.com/whois/google.com"
},
"technical_contact": {
"email_address": "Select Request Email Form at https://domains.markmonitor.com/whois/google.com"
},
"name_servers": [
"ns1.google.com",
"ns2.google.com",
"ns3.google.com",
"ns4.google.com"
],
"domain_status": [
"clientupdateprohibited",
"clienttransferprohibited",
"clientdeleteprohibited",
"serverdeleteprohibited",
"serverupdateprohibited",
"servertransferprohibited"
],
"whois_raw_domain": "\nDomain Name: google.com\nRegistry Domain ID: 2138514_DOMAIN_COM-VRSN\nRegistrar WHOIS Server: whois.markmonitor.com\nRegistrar URL: http://www.markmonitor.com\nUpdated Date: 2024-08-02T02:17:33+0000\nCreation Date: 1997-09-15T07:00:00+0000\nRegistrar Registration Expiration Date: 2028-09-13T07:00:00+0000\nRegistrar: MarkMonitor, Inc.\nRegistrar IANA ID: 292\nRegistrar Abuse Contact: https://corp.markmonitor.com/domain/ui/abuse-report\nRegistrar Abuse Contact Phone: +1.2086851750\nDomain Status: clientUpdateProhibited (https://www.icann.org/epp#clientUpdateProhibited)\nDomain Status: clientTransferProhibited (https://www.icann.org/epp#clientTransferProhibited)\nDomain Status: clientDeleteProhibited (https://www.icann.org/epp#clientDeleteProhibited)\nDomain Status: serverUpdateProhibited (https://www.icann.org/epp#serverUpdateProhibited)\nDomain Status: serverTransferProhibited (https://www.icann.org/epp#serverTransferProhibited)\nDomain Status: serverDeleteProhibited (https://www.icann.org/epp#serverDeleteProhibited)\nRegistrant Organization: Google LLC\nRegistrant Country: US\nRegistrant Email: Select Request Email Form at https://domains.markmonitor.com/whois/google.com\nTech Email: Select Request Email Form at https://domains.markmonitor.com/whois/google.com\nName Server: ns1.google.com\nName Server: ns2.google.com\nName Server: ns3.google.com\nName Server: ns4.google.com\nDNSSEC: unsigned\nURL of the ICANN WHOIS Data Problem Reporting System: http://wdprs.internic.net/\n>>> Last update of WHOIS database: 2026-07-13T08:02:14+0000 <<<\n\nFor more information on WHOIS status codes, please visit:\n https://www.icann.org/resources/pages/epp-status-codes\n\nIf you wish to contact this domain\u2019s Registrant or Technical\ncontact, and such email address is not visible above, you may do so via our web\nform, pursuant to ICANN\u2019s Temporary Specification. To verify that you are not a\nrobot, please enter your email address to receive a link to a page that\nfacilitates email communication with the relevant contact(s).\n\nWeb-based WHOIS:\n https://domains.markmonitor.com/whois/contact/google.com\n\nIf you have a legitimate interest in viewing the non-public WHOIS details, send\nyour request and the reasons for your request to whoisrequest@markmonitor.com\nand specify the domain name in the subject line. We will review that request and\nmay ask for supporting documentation and explanation.\n\nThe data in MarkMonitor\u2019s WHOIS database is provided for information purposes,\nand to assist persons in obtaining information about or related to a domain\nname\u2019s registration record. While MarkMonitor believes the data to be accurate,\nthe data is provided \"as is\" with no guarantee or warranties regarding its\naccuracy.\n\nBy submitting a WHOIS query, you agree that you will use this data only for\nlawful purposes and that, under no circumstances will you use this data to:\n (1) allow, enable, or otherwise support the transmission by email, telephone,\nor facsimile of mass, unsolicited, commercial advertising, or spam; or\n (2) enable high volume, automated, or electronic processes that send queries,\ndata, or email to MarkMonitor (or its systems) or the domain name contacts (or\nits systems).\n\nMarkMonitor reserves the right to modify these terms at any time.\n\nBy submitting this query, you agree to abide by this policy.\n\nMarkMonitor Domain Management(TM)\nProtecting companies and consumers in a digital world.\n\nVisit MarkMonitor at https://www.markmonitor.com\nContact us at +1.8007459229\nIn Europe, at +44.02032062220\n--",
"registry_data": {
"domain_name": "GOOGLE.COM",
"query_time": "2026-07-13 08:05:52",
"whois_server": "whois.verisign-grs.com",
"domain_registered": "yes",
"create_date": "1997-09-15",
"update_date": "2019-09-09",
"expiry_date": "2028-09-14",
"domain_registrar": {
"iana_id": "292",
"registrar_name": "MarkMonitor Inc",
"whois_server": "whois.markmonitor.com",
"website_url": "http://www.markmonitor.com",
"email_address": "abusecomplaints@markmonitor.com",
"phone_number": "+12086851750"
},
"name_servers": [
"ns1.google.com",
"ns2.google.com",
"ns3.google.com",
"ns4.google.com"
],
"domain_status": [
"clientupdateprohibited",
"clientdeleteprohibited",
"clienttransferprohibited",
"serverdeleteprohibited",
"serverupdateprohibited",
"servertransferprohibited"
],
"whois_raw_registery": "\nDomain Name: GOOGLE.COM\n Registry Domain ID: 2138514_DOMAIN_COM-VRSN\n Registrar WHOIS Server: whois.markmonitor.com\n Registrar URL: http://www.markmonitor.com\n Updated Date: 2019-09-09T15:39:04Z\n Creation Date: 1997-09-15T04:00:00Z\n Registry Expiry Date: 2028-09-14T04:00:00Z\n Registrar: MarkMonitor Inc.\n Registrar IANA ID: 292\n Registrar Abuse Contact Email: abusecomplaints@markmonitor.com\n Registrar Abuse Contact Phone: +1.2086851750\n Domain Status: clientDeleteProhibited https://icann.org/epp#clientDeleteProhibited\n Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited\n Domain Status: clientUpdateProhibited https://icann.org/epp#clientUpdateProhibited\n Domain Status: serverDeleteProhibited https://icann.org/epp#serverDeleteProhibited\n Domain Status: serverTransferProhibited https://icann.org/epp#serverTransferProhibited\n Domain Status: serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited\n Name Server: NS1.GOOGLE.COM\n Name Server: NS2.GOOGLE.COM\n Name Server: NS3.GOOGLE.COM\n Name Server: NS4.GOOGLE.COM\n DNSSEC: unsigned\n URL of the ICANN Whois Inaccuracy Complaint Form: https://www.icann.org/wicf/\n>>> Last update of whois database: 2026-07-13T08:05:39Z <<<\n\nFor more information on Whois status codes, please visit https://icann.org/epp\n\nNOTICE: The expiration date displayed in this record is the date the\nregistrar's sponsorship of the domain name registration in the registry is\ncurrently set to expire. This date does not necessarily reflect the expiration\ndate of the domain name registrant's agreement with the sponsoring\nregistrar. Users may consult the sponsoring registrar's Whois database to\nview the registrar's reported date of expiration for this registration.\n\nTERMS OF USE: You are not authorized to access or query our Whois\ndatabase through the use of electronic processes that are high-volume and\nautomated except as reasonably necessary to register domain names or\nmodify existing registrations; the Data in VeriSign Global Registry\nServices' (\"VeriSign\") Whois database is provided by VeriSign for\ninformation purposes only, and to assist persons in obtaining information\nabout or related to a domain name registration record. VeriSign does not\nguarantee its accuracy. By submitting a Whois query, you agree to abide\nby the following terms of use: You agree that you may use this Data only\nfor lawful purposes and that under no circumstances will you use this Data\nto: (1) allow, enable, or otherwise support the transmission of mass\nunsolicited, commercial advertising or solicitations via e-mail, telephone,\nor facsimile; or (2) enable high volume, automated, electronic processes\nthat apply to VeriSign (or its computer systems). The compilation,\nrepackaging, dissemination or other use of this Data is expressly\nprohibited without the prior written consent of VeriSign. You agree not to\nuse electronic processes that are automated and high-volume to access or\nquery the Whois database except as reasonably necessary to register\ndomain names or modify existing registrations. VeriSign reserves the right\nto restrict your access to the Whois database in its sole discretion to ensure\noperational stability. VeriSign may restrict or terminate your access to the\nWhois database for failure to abide by these terms of use. VeriSign\nreserves the right to modify these terms at any time.\n\nThe Registry database contains ONLY .COM, .NET, .EDU domains and\nRegistrars."
}
}Because the fields land in the same place on every TLD, there's no per-extension parser to maintain and no silent None on an extension you didn't anticipate. The registrar and registry records don't always agree (above, the registrar reports an expiry of 2028-09-13 while the registry says 2028-09-14), and having both lets you reconcile them instead of trusting a single source. Redaction is handled in the open: personal registrant fields come back marked REDACTED FOR PRIVACY, while organization and country remain where the registry publishes them.
Bulk Lookups: The Part That Matters at Scale
Enriching a domain portfolio, monitoring a brand's typosquats (the kind of sweep the Domain WHOIS Reverse Lookup API does by registrant or keyword), or feeding a threat-hunting pipeline means looking up hundreds of domains on a schedule, not one. Looping single calls is where rate limits, partial failures, and retry logic pile up.
The APIFreaks Bulk Domain WHOIS Lookup API takes up to 100 domains in a single POST to the same endpoint, and returns an array of records:
import requests
resp = requests.post(
"https://api.apifreaks.com/v2.0/domain/whois/live",
params={"apiKey": "YOUR_API_KEY"},
json={"domainNames": ["google.com", "example.com"]}, # up to 100 per request
timeout=60,
)
for record in resp.json()["bulk_whois_response"]:
# top-level fields can be absent for unusual domains (reserved names, etc.), so use .get()
print(record["domain_name"], record.get("expiry_date"), record.get("domain_status"))The same call through the SDK returns typed objects, so an absent field like example.com's status reads back as None with no .get() needed:
# reusing the client from the SDK example above
bulk = client.bulk_domain_whois_lookup(
api_key="YOUR_API_KEY", domain_names=["google.com", "example.com"], version="2.0"
)
for record in bulk.bulk_whois_response:
print(record.domain_name, record.expiry_date, record.domain_status)The response wraps the per-domain records in a bulk_whois_response array. Notice that example.com, a reserved name, comes back with no top-level domain_registrar or domain_status, the fields google.com has, which is exactly why the loop reads them with .get():
google.com 2028-09-13 ['clientupdateprohibited', 'clienttransferprohibited', 'clientdeleteprohibited', 'serverdeleteprohibited', 'serverupdateprohibited', 'servertransferprohibited']
example.com 2026-08-13 None{
"bulk_whois_response": [
{
"status": true,
"domain_name": "google.com",
"query_time": "2026-07-13 08:13:22",
"whois_server": "whois.verisign-grs.com",
"domain_registered": "yes",
"create_date": "1997-09-15",
"update_date": "2024-08-02",
"expiry_date": "2028-09-13",
"domain_registrar": {
"iana_id": "292",
"status": "accredited",
"registrar_name": "MarkMonitor, Inc",
"normalized_name": "markmonitor",
"whois_server": "whois.markmonitor.com",
"rdap_server": "https://rdap.markmonitor.com/rdap/",
"website_url": "https://www.markmonitor.com/",
"email_address": "https://corp.markmonitor.com/domain/ui/abuse-report",
"phone_number": "+12086851750"
},
"registrant_contact": {
"company": "Google LLC",
"country_name": "United States",
"country_code": "US",
"email_address": "Select Request Email Form at https://domains.markmonitor.com/whois/google.com"
},
"technical_contact": {
"email_address": "Select Request Email Form at https://domains.markmonitor.com/whois/google.com"
},
"name_servers": [
"ns1.google.com",
"ns2.google.com",
"ns3.google.com",
"ns4.google.com"
],
"domain_status": [
"clientupdateprohibited",
"clienttransferprohibited",
"clientdeleteprohibited",
"serverdeleteprohibited",
"serverupdateprohibited",
"servertransferprohibited"
],
"whois_raw_domain": "\nDomain Name: google.com\nRegistry Domain ID: 2138514_DOMAIN_COM-VRSN\nRegistrar WHOIS Server: whois.markmonitor.com\nRegistrar URL: http://www.markmonitor.com\nUpdated Date: 2024-08-02T02:17:33+0000\nCreation Date: 1997-09-15T07:00:00+0000\nRegistrar Registration Expiration Date: 2028-09-13T07:00:00+0000\nRegistrar: MarkMonitor, Inc.\nRegistrar IANA ID: 292\nRegistrar Abuse Contact: https://corp.markmonitor.com/domain/ui/abuse-report\nRegistrar Abuse Contact Phone: +1.2086851750\nDomain Status: clientUpdateProhibited (https://www.icann.org/epp#clientUpdateProhibited)\nDomain Status: clientTransferProhibited (https://www.icann.org/epp#clientTransferProhibited)\nDomain Status: clientDeleteProhibited (https://www.icann.org/epp#clientDeleteProhibited)\nDomain Status: serverUpdateProhibited (https://www.icann.org/epp#serverUpdateProhibited)\nDomain Status: serverTransferProhibited (https://www.icann.org/epp#serverTransferProhibited)\nDomain Status: serverDeleteProhibited (https://www.icann.org/epp#serverDeleteProhibited)\nRegistrant Organization: Google LLC\nRegistrant Country: US\nRegistrant Email: Select Request Email Form at https://domains.markmonitor.com/whois/google.com\nTech Email: Select Request Email Form at https://domains.markmonitor.com/whois/google.com\nName Server: ns4.google.com\nName Server: ns1.google.com\nName Server: ns2.google.com\nName Server: ns3.google.com\nDNSSEC: unsigned\nURL of the ICANN WHOIS Data Problem Reporting System: http://wdprs.internic.net/\n>>> Last update of WHOIS database: 2026-07-13T08:12:15+0000 <<<\n\nFor more information on WHOIS status codes, please visit:\n https://www.icann.org/resources/pages/epp-status-codes\n\nIf you wish to contact this domain\u2019s Registrant or Technical\ncontact, and such email address is not visible above, you may do so via our web\nform, pursuant to ICANN\u2019s Temporary Specification. To verify that you are not a\nrobot, please enter your email address to receive a link to a page that\nfacilitates email communication with the relevant contact(s).\n\nWeb-based WHOIS:\n https://domains.markmonitor.com/whois/contact/google.com\n\nIf you have a legitimate interest in viewing the non-public WHOIS details, send\nyour request and the reasons for your request to whoisrequest@markmonitor.com\nand specify the domain name in the subject line. We will review that request and\nmay ask for supporting documentation and explanation.\n\nThe data in MarkMonitor\u2019s WHOIS database is provided for information purposes,\nand to assist persons in obtaining information about or related to a domain\nname\u2019s registration record. While MarkMonitor believes the data to be accurate,\nthe data is provided \"as is\" with no guarantee or warranties regarding its\naccuracy.\n\nBy submitting a WHOIS query, you agree that you will use this data only for\nlawful purposes and that, under no circumstances will you use this data to:\n (1) allow, enable, or otherwise support the transmission by email, telephone,\nor facsimile of mass, unsolicited, commercial advertising, or spam; or\n (2) enable high volume, automated, or electronic processes that send queries,\ndata, or email to MarkMonitor (or its systems) or the domain name contacts (or\nits systems).\n\nMarkMonitor reserves the right to modify these terms at any time.\n\nBy submitting this query, you agree to abide by this policy.\n\nMarkMonitor Domain Management(TM)\nProtecting companies and consumers in a digital world.\n\nVisit MarkMonitor at https://www.markmonitor.com\nContact us at +1.8007459229\nIn Europe, at +44.02032062220\n--",
"registry_data": {
"domain_name": "GOOGLE.COM",
"query_time": "2026-07-13 08:13:20",
"whois_server": "whois.verisign-grs.com",
"domain_registered": "yes",
"create_date": "1997-09-15",
"update_date": "2019-09-09",
"expiry_date": "2028-09-14",
"domain_registrar": {
"iana_id": "292",
"registrar_name": "MarkMonitor Inc",
"whois_server": "whois.markmonitor.com",
"website_url": "http://www.markmonitor.com",
"email_address": "abusecomplaints@markmonitor.com",
"phone_number": "+12086851750"
},
"name_servers": [
"ns1.google.com",
"ns2.google.com",
"ns3.google.com",
"ns4.google.com"
],
"domain_status": [
"clientupdateprohibited",
"clientdeleteprohibited",
"clienttransferprohibited",
"serverdeleteprohibited",
"serverupdateprohibited",
"servertransferprohibited"
],
"whois_raw_registery": "\nDomain Name: GOOGLE.COM\n Registry Domain ID: 2138514_DOMAIN_COM-VRSN\n Registrar WHOIS Server: whois.markmonitor.com\n Registrar URL: http://www.markmonitor.com\n Updated Date: 2019-09-09T15:39:04Z\n Creation Date: 1997-09-15T04:00:00Z\n Registry Expiry Date: 2028-09-14T04:00:00Z\n Registrar: MarkMonitor Inc.\n Registrar IANA ID: 292\n Registrar Abuse Contact Email: abusecomplaints@markmonitor.com\n Registrar Abuse Contact Phone: +1.2086851750\n Domain Status: clientDeleteProhibited https://icann.org/epp#clientDeleteProhibited\n Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited\n Domain Status: clientUpdateProhibited https://icann.org/epp#clientUpdateProhibited\n Domain Status: serverDeleteProhibited https://icann.org/epp#serverDeleteProhibited\n Domain Status: serverTransferProhibited https://icann.org/epp#serverTransferProhibited\n Domain Status: serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited\n Name Server: NS1.GOOGLE.COM\n Name Server: NS2.GOOGLE.COM\n Name Server: NS3.GOOGLE.COM\n Name Server: NS4.GOOGLE.COM\n DNSSEC: unsigned\n URL of the ICANN Whois Inaccuracy Complaint Form: https://www.icann.org/wicf/\n>>> Last update of whois database: 2026-07-13T08:13:09Z <<<\n\nFor more information on Whois status codes, please visit https://icann.org/epp\n\nNOTICE: The expiration date displayed in this record is the date the\nregistrar's sponsorship of the domain name registration in the registry is\ncurrently set to expire. This date does not necessarily reflect the expiration\ndate of the domain name registrant's agreement with the sponsoring\nregistrar. Users may consult the sponsoring registrar's Whois database to\nview the registrar's reported date of expiration for this registration.\n\nTERMS OF USE: You are not authorized to access or query our Whois\ndatabase through the use of electronic processes that are high-volume and\nautomated except as reasonably necessary to register domain names or\nmodify existing registrations; the Data in VeriSign Global Registry\nServices' (\"VeriSign\") Whois database is provided by VeriSign for\ninformation purposes only, and to assist persons in obtaining information\nabout or related to a domain name registration record. VeriSign does not\nguarantee its accuracy. By submitting a Whois query, you agree to abide\nby the following terms of use: You agree that you may use this Data only\nfor lawful purposes and that under no circumstances will you use this Data\nto: (1) allow, enable, or otherwise support the transmission of mass\nunsolicited, commercial advertising or solicitations via e-mail, telephone,\nor facsimile; or (2) enable high volume, automated, electronic processes\nthat apply to VeriSign (or its computer systems). The compilation,\nrepackaging, dissemination or other use of this Data is expressly\nprohibited without the prior written consent of VeriSign. You agree not to\nuse electronic processes that are automated and high-volume to access or\nquery the Whois database except as reasonably necessary to register\ndomain names or modify existing registrations. VeriSign reserves the right\nto restrict your access to the Whois database in its sole discretion to ensure\noperational stability. VeriSign may restrict or terminate your access to the\nWhois database for failure to abide by these terms of use. VeriSign\nreserves the right to modify these terms at any time.\n\nThe Registry database contains ONLY .COM, .NET, .EDU domains and\nRegistrars."
}
},
{
"status": true,
"domain_name": "example.com",
"query_time": "2026-07-13 08:13:29",
"whois_server": "whois.verisign-grs.com",
"domain_registered": "yes",
"create_date": "1992-01-01",
"update_date": "2026-01-16",
"expiry_date": "2026-08-13",
"name_servers": [
"elliott.ns.cloudflare.com",
"hera.ns.cloudflare.com"
],
"whois_raw_domain": "\n% IANA WHOIS server\n% for more information on IANA, visit http://www.iana.org\n% This query returned 1 object\n\ndomain: EXAMPLE.COM\n\norganisation: Internet Assigned Numbers Authority\n\ncreated: 1992-01-01\nsource: IANA",
"registry_data": {
"domain_name": "EXAMPLE.COM",
"query_time": "2026-07-13 08:13:20",
"whois_server": "whois.verisign-grs.com",
"domain_registered": "yes",
"create_date": "1995-08-14",
"update_date": "2026-01-16",
"expiry_date": "2026-08-13",
"domain_registrar": {
"iana_id": "376",
"status": "reserved",
"registrar_name": "RESERVED-Internet Assigned Numbers Authority",
"normalized_name": "reserved-internet assigned numbers authority",
"whois_server": "whois.iana.org"
},
"name_servers": [
"elliott.ns.cloudflare.com",
"hera.ns.cloudflare.com"
],
"domain_status": [
"clientupdateprohibited",
"clientdeleteprohibited",
"clienttransferprohibited"
],
"whois_raw_registery": "\nDomain Name: EXAMPLE.COM\n Registry Domain ID: 2336799_DOMAIN_COM-VRSN\n Registrar WHOIS Server: whois.iana.org\n Registrar URL: http://res-dom.iana.org\n Updated Date: 2026-01-16T18:26:50Z\n Creation Date: 1995-08-14T04:00:00Z\n Registry Expiry Date: 2026-08-13T04:00:00Z\n Registrar: RESERVED-Internet Assigned Numbers Authority\n Registrar IANA ID: 376\n Registrar Abuse Contact Email:\n Registrar Abuse Contact Phone:\n Domain Status: clientDeleteProhibited https://icann.org/epp#clientDeleteProhibited\n Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited\n Domain Status: clientUpdateProhibited https://icann.org/epp#clientUpdateProhibited\n Name Server: ELLIOTT.NS.CLOUDFLARE.COM\n Name Server: HERA.NS.CLOUDFLARE.COM\n DNSSEC: signedDelegation\n DNSSEC DS Data: 2371 13 2 C988EC423E3880EB8DD8A46FE06CA230EE23F35B578D64E78B29C3E1C83D245A\n URL of the ICANN Whois Inaccuracy Complaint Form: https://www.icann.org/wicf/\n>>> Last update of whois database: 2026-07-13T08:13:09Z <<<\n\nFor more information on Whois status codes, please visit https://icann.org/epp\n\nNOTICE: The expiration date displayed in this record is the date the\nregistrar's sponsorship of the domain name registration in the registry is\ncurrently set to expire. This date does not necessarily reflect the expiration\ndate of the domain name registrant's agreement with the sponsoring\nregistrar. Users may consult the sponsoring registrar's Whois database to\nview the registrar's reported date of expiration for this registration.\n\nTERMS OF USE: You are not authorized to access or query our Whois\ndatabase through the use of electronic processes that are high-volume and\nautomated except as reasonably necessary to register domain names or\nmodify existing registrations; the Data in VeriSign Global Registry\nServices' (\"VeriSign\") Whois database is provided by VeriSign for\ninformation purposes only, and to assist persons in obtaining information\nabout or related to a domain name registration record. VeriSign does not\nguarantee its accuracy. By submitting a Whois query, you agree to abide\nby the following terms of use: You agree that you may use this Data only\nfor lawful purposes and that under no circumstances will you use this Data\nto: (1) allow, enable, or otherwise support the transmission of mass\nunsolicited, commercial advertising or solicitations via e-mail, telephone,\nor facsimile; or (2) enable high volume, automated, electronic processes\nthat apply to VeriSign (or its computer systems). The compilation,\nrepackaging, dissemination or other use of this Data is expressly\nprohibited without the prior written consent of VeriSign. You agree not to\nuse electronic processes that are automated and high-volume to access or\nquery the Whois database except as reasonably necessary to register\ndomain names or modify existing registrations. VeriSign reserves the right\nto restrict your access to the Whois database in its sole discretion to ensure\noperational stability. VeriSign may restrict or terminate your access to the\nWhois database for failure to abide by these terms of use. VeriSign\nreserves the right to modify these terms at any time.\n\nThe Registry database contains ONLY .COM, .NET, .EDU domains and\nRegistrars."
}
}
]
}One request, one response, one place for error handling. Credits are charged per successful domain and refunded per domain on failure, so a bad entry in the batch doesn't cost you and doesn't sink the rest of the request.
This is the part neither of the other methods gives you. The library and raw RDAP both leave you looping single lookups and writing your own concurrency, backoff, and retry logic, and it's the piece most WHOIS tutorials skip entirely. One bulk call also collapses 100 request/response round trips into one, so you pay the connection setup and network latency once instead of per domain. Batching is the difference between a script you babysit and one you can schedule and forget.
The Bulk Domain WHOIS Lookup API is the endpoint that makes this practical at scale.
How Each Method Handled the Same Five Domains
Here's how the three approaches handled the same five domains in a single run, so the tradeoffs are concrete instead of abstract.
Checked 5 Aug 2026. WHOIS output and RDAP coverage shift over time, so treat this as a dated snapshot.

| Domain | python-whois | RDAP (direct) | WHOIS API |
|---|---|---|---|
| google.com (.com) | Full record | 200, registrar only | Structured; org "Google LLC", personal fields redacted |
| wikipedia.org (.org) | Full record | 200, registrar only | Structured; org "Wikimedia Foundation", personal fields marked REDACTED FOR PRIVACY |
| svelte.dev (.dev) | Empty, no data returned | 200 via Google's RDAP | Structured; contact data redacted |
| socket.io (.io) | Full record | No bootstrap entry, can't route | Structured; registrant shows a privacy proxy |
| heise.de (.de) | Partial, no registrar or dates | No bootstrap entry, can't route | Structured; full organization contact |
The pattern is the point. python-whois breaks on .dev, RDAP can't route .io or .de, and the API returns one consistent schema for all five. heise.de is worth a second look too: a German ccTLD outside the gTLD redaction rules, so its record carries full organization contact where the .com records are trimmed to a company name.
Which Method Should You Use?
A common production pattern is RDAP-first with a WHOIS fallback, which is effectively what a WHOIS API runs for you server-side.
| python-whois | RDAP (direct) | WHOIS API | |
|---|---|---|---|
| Setup | pip install python-whois, one call |
requests plus your own bootstrap and routing |
requests plus an API key |
| Output | Parsed object; field gaps on unsupported TLDs | Protocol-native JSON you normalize yourself | Uniform JSON across TLDs, plus raw WHOIS |
| TLD coverage | Strong on common TLDs, generic fallback elsewhere | All gTLDs; many ccTLDs missing from the bootstrap | Broad TLD and SLD coverage |
| Transport and limits | Raw port 43, inconsistent and often unsignaled rate limits | HTTPS, per-server limits with 429 + Retry-After |
Managed; one rate and credit model instead of per-registry limits |
| Bulk | Loop it yourself | Loop it yourself | Up to 100 domains per request |
| Contact data | Redacted | Redacted | Redacted (labeled in the response) |
| Cost | Free | Free | Paid; most offer a free tier |
Use python-whois for prototyping, debugging one domain, or scripts against common TLDs where an occasional empty field won't hurt you. Use direct RDAP when you want standards-based JSON without a paid dependency and you're ready to own the bootstrap routing, rate limits, and ccTLD gaps. Use a WHOIS API when uptime, uniform output, and bulk throughput matter more than avoiding a per-query cost, which is the usual case once a lookup lives inside a product instead of a script.
Conclusion
A WHOIS lookup in Python is only simple until it isn't. The python-whois library returns a result in one call and holds up for interactive use, but its per-TLD regex parsing and raw port 43 queries make it fragile across newer extensions and high-volume jobs. RDAP is the right long-term protocol now that ICANN has retired WHOIS for gTLDs, and it hands you clean JSON as long as you handle the bootstrap routing, respect each server's rate limits, and accept patchy ccTLD coverage. A WHOIS API takes those problems off your plate, and with a bulk endpoint it turns a hundred fragile loops into one request.
Match the method to the scale of the job, and you'll spend your time on the work that matters instead of on parser edge cases.
When you're ready to move past scripts, the Domain WHOIS Lookup API and Bulk Domain WHOIS Lookup API start with 10,000 free credits and no credit card. Grab a key and run the code above.
FAQ
Why do I get "module 'whois' has no attribute 'whois'"?
You installed the wrong package. pip install whois pulls an unrelated PyPI package; the library in this guide installs as pip install python-whois and imports as import whois. Uninstall whois, install python-whois, and the attribute error goes away.
What is RDAP and why was it created?
RDAP (Registration Data Access Protocol) is the modern replacement for WHOIS. It returns domain registration data as structured JSON over HTTPS instead of free-form text over port 43. The IETF standardized it in RFC 9082 and RFC 9083 to add encryption, consistent formatting, internationalization, and differentiated access, none of which the 1980s-era WHOIS protocol was built to support.
Is WHOIS being phased out?
For gTLDs, yes. As of 28 January 2025, ICANN removed the requirement for gTLD registries and registrars to run a WHOIS service, making RDAP the mandated protocol. Many operators have already shut their WHOIS servers down. Country-code TLDs set their own rules and some still run WHOIS, but the industry direction is clearly toward RDAP.
How does an RDAP client know which server to query?
Through the IANA bootstrap registry at data.iana.org/rdap/dns.json. That file maps each TLD to its authoritative RDAP base URL. A client loads the file, matches the domain's TLD, and sends the query to the listed server. If the TLD isn't in the file, there's no server to route to and a bootstrap-based lookup can't proceed.
Does RDAP show the domain owner's name and address?
Usually not for individuals. Since GDPR and ICANN's Temporary Specification, personal registrant details are redacted from public RDAP and WHOIS by default, whether or not privacy protection is enabled. A query returns registrar info, dates, name servers, and EPP status codes; the registrant's name, address, phone, and email are withheld. Organization names sometimes remain. No method, free or paid, can legally return redacted personal data.
Is RDAP supported for every TLD?
No. Every ICANN gTLD is required to offer RDAP, but many country-code TLDs are not, since they sit outside ICANN's contracts. Domains like t.co (.co) and notion.so (.so) have no entry in the IANA bootstrap even though both resolve in DNS and serve live sites. For those TLDs, a bootstrap-based RDAP client has no server to query.
Can I do bulk WHOIS lookups for free?
You can loop the python-whois library or direct RDAP calls at no cost, but you'll run into port 43 rate limits, per-TLD coverage gaps, and parser inconsistencies as volume grows. For consistent JSON at scale, the APIFreaks Bulk WHOIS API accepts up to 100 domains in a single POST and starts with 10,000 free credits.
