Why Your Domain Checker Breaks on .org and .dev
March 15, 2026
You build a domain checker. It works on .com. Then a user queries a .dev domain and every field comes back empty. A .org lookup works, but your parser chokes on a comma in the registrar name that .com did not have.
Your code is not broken. WHOIS is broken — not by design, but by the absence of one. There is no standard format, no discovery mechanism, and no way to know which server handles which TLD without a hardcoded list that goes stale.
The root cause: WHOIS has no discovery mechanism
When you run whois example.com, your WHOIS client needs to know which server to ask. For .com, it is whois.verisign-grs.com. For .org, it is whois.pir.org. For .dev, the WHOIS server is whois.nic.google — but many libraries do not have it in their hardcoded list.
How does your client know this? It relies on a hardcoded list compiled from various sources — IANA's root zone database, community-maintained files, or the library author's manual testing. This list goes stale. New TLDs launch, registries change servers, and some TLDs are missing entirely.
Here is python-whois on a .dev domain:
import whois # pip install python-whois
w = whois.whois("web.dev")
# stderr: Error trying to connect to socket: closing socket - nodename nor servname provided
w.registrar # None
w.expiration_date # None
The library tried to connect to a WHOIS server that does not exist, printed a warning to stderr, and returned None for every field. No exception — just empty data for a domain that is clearly registered.
The python-whois library has dozens of open issues for specific TLDs failing. Each fix is a patch for one TLD that might break another.
Every TLD returns different text
Even when you reach the right server, the response format differs. Here is the registrar name field from four different registries:
.com (Verisign):
Registrar: MarkMonitor Inc.
.org (PIR):
Registrar: MarkMonitor, Inc.
.uk (Nominet):
Registrar:
MarkMonitor Inc. [Tag = MARKMONITOR]
.au (auDA):
Registrar Name: MarkMonitor Inc.
Same data. Four different formats. A regex that matches one breaks on the others. Your parser needs to handle all of them — and the hundreds of other variations across 1,200+ TLDs.
Dates are worse:
# .com
Registry Expiry Date: 2028-09-14T04:00:00Z
# .uk
Expiry date: 14-Sep-2028
# .br
expires: 20280914
# .ru
paid-till: 2028-09-14T00:00:00Z
Four different field names, three different date formats. And this is just the expiration date — every field has the same problem.
How RDAP fixes discovery
RDAP solves the discovery problem with a bootstrap registry published by IANA at https://data.iana.org/rdap/dns.json. This file maps every TLD to its RDAP server URL:
{
"services": [
[["com", "net"], ["https://rdap.verisign.com/com/v1/"]],
[["org"], ["https://rdap.org/"]],
[["io"], ["https://rdap.nic.io/"]],
[["dev", "app"], ["https://pubapi.registry.google/rdap/"]]
]
}
IANA maintains this file. When a new TLD launches or a registry changes its server, the bootstrap file gets updated. Your code fetches it, caches it, and always has the current mapping. No hardcoded lists.
How RDAP fixes parsing
Every RDAP server returns JSON following RFC 9083. The structure is the same regardless of TLD:
curl -s https://rdap.verisign.com/com/v1/domain/shopify.com | jq '.events'
curl -s https://rdap.org/domain/wikipedia.org | jq '.events'
curl -s https://pubapi.registry.google/rdap/domain/web.dev | jq '.events'
All three return:
[
{"eventAction": "registration", "eventDate": "..."},
{"eventAction": "expiration", "eventDate": "..."},
{"eventAction": "last changed", "eventDate": "..."}
]
Same field names. Same date format (ISO 8601). Same structure. One parser handles every TLD.
Skip the plumbing
RDAP fixes the protocol-level problems — discovery and format. But querying RDAP servers directly still means handling bootstrap caching, per-server rate limits, thin-registry referrals, and vcardArray parsing yourself. RDAP API handles all of that behind one endpoint:
curl -H "Authorization: Bearer YOUR_TOKEN" \
"https://rdapapi.io/api/v1/domain/web.dev"
{
"name": "web.dev",
"status": ["active"],
"registered": "2018-10-29T15:57:39.435Z",
"registrar": "Google LLC",
"nameservers": ["ns-cloud-a1.googledomains.com", "ns-cloud-a2.googledomains.com"]
}
The same domain that returned None from python-whois — now flat JSON with every field populated. Same structure whether the domain is .com, .org, .io, or .dev.
1,200+ TLDs. SDKs for Python, Node.js, PHP, Go, and Java.
Further reading
- The RDAP JSON Response Decoded — field-by-field walkthrough of RDAP responses
- WHOIS to RDAP Migration Guide — step-by-step code migration with before/after examples
- WHOIS Rate Limits Are Killing Your App — why WHOIS breaks at scale
- API documentation — endpoint reference