Built a domain-checking skill for Claude Code using RDAP. String matching with whois kept failing. Structured protocols solve it.
Domain Checking Skill
The Symptom
I wanted Claude Code to check if domains were available. Simple enough—just query WHOIS and grep for “available” or “no match”, right?
Except it kept giving wrong answers. Some domains showed as available when they weren’t. Others showed as registered when they were actually free.
The pattern matching worked fine for .com domains most of the time, but fell apart on .io, .sh, and other TLDs. Different registries return completely different response formats.
The Investigation
I tried improving the regex patterns. Added more variations of “not found” and “no match”. Accounted for case sensitivity. Built a lookup table of registry-specific strings.
# .com domains
$ whois example.com | grep -i "no match"
No match for domain "EXAMPLE.COM".
# .io domains
$ whois example.io | grep -i "no match"
# (returns nothing - uses "NOT FOUND" instead)
# .sh domains
$ whois example.sh | grep -i "no match"
# (returns nothing - uses "No entries found" instead)Here’s what I found across just a handful of TLDs:
| TLD | Available String | Case Sensitive |
|---|---|---|
.com | ”No match for domain” | No |
.net | ”No match for domain” | No |
.org | ”NOT FOUND” | Yes |
.io | ”NOT FOUND” | Yes |
.sh | ”No entries found” | No |
.uk | ”No match for” | No |
.dev | ”Domain not found” | No |
And that’s just for available domains. Registered domains have even more variation—some show “Registrant”, others show “Domain Status”, and some just dump raw registry data.
I could keep building this lookup table for all 1,500+ TLDs, but that’s maintenance hell. Every time a registry changes their format, the grep patterns break.
The Root Cause
WHOIS is unstructured text designed for humans to read. It was created in 1982, long before JSON or structured APIs. Each registry formats responses however they want.
String parsing across heterogeneous systems is fragile. You’re matching against free-form text that can change at any time. There’s no schema, no standardization, no validation.
Even worse, most WHOIS tools query third-party aggregators, not official registries. You’re trusting intermediaries who may cache data, inject ads, or shape responses differently.
RDAP Protocol
RDAP (Registration Data Access Protocol) is the modern replacement for WHOIS. It’s an IETF standard (RFC 7480) that returns structured JSON instead of text.
Instead of parsing “No match for domain”, you get:
{"errorCode": 404, "title": "Not Found"}Instead of grep-ing through paragraphs for “Registrant”, you get:
{
"status": [
"client delete prohibited",
"client transfer prohibited"
]
}The rdap.org bootstrap service handles TLD routing automatically. You query one endpoint, it figures out which registry owns that TLD, and forwards the request. No lookup tables needed.
The Implementation
Here’s the actual RDAP query:
# Check if a domain is available
HTTP_CODE=$(curl -sL -o response.json -w "%{http_code}" "https://rdap.org/domain/example.com")
if [ "$HTTP_CODE" = "404" ]; then
echo "AVAILABLE"
else
jq -r '.status[]' response.json
fiCompare this to the WHOIS nightmare:
# WHOIS: fragile string matching
if whois example.com | grep -qi "no match\|not found\|no entries"; then
echo "AVAILABLE"
else
echo "REGISTERED"
fiThe RDAP approach works identically across all TLDs. No special cases, no regex tuning, no false positives.
Here’s how the flow works:
The bootstrap service eliminates the need for TLD-to-registry mappings. It handles routing, so you don’t have to know that .com goes to Verisign, .io goes to the Internet Computer Bureau, and .sh goes to Afilias.
The Pattern
I built this as a Claude Code skill—instructions-only, no npm packages. It uses curl and jq, which are already installed on macOS/Linux.
Single domain check:
curl -sL "https://rdap.org/domain/example.com" | jq -r '.status // "AVAILABLE"'Batch checking:
for domain in startup.io startup.sh startup.dev; do
(
HTTP_CODE=$(curl -sL -o /dev/null -w "%{http_code}" "https://rdap.org/domain/$domain")
if [ "$HTTP_CODE" = "404" ]; then
echo "✓ $domain is AVAILABLE"
else
echo "✗ $domain is REGISTERED"
fi
) &
done
waitThe & runs each check in the background, and wait waits for all to finish. You can check 10 domains in parallel instead of sequentially.
Integration with Claude Code:
The skill activates when you ask questions like “Check if example.com is available” or “Is startup.io registered?” Claude uses RDAP automatically instead of grep-ing through WHOIS output.
No external APIs, no auth tokens, no third-party services. Just direct queries to official registries via a standardized protocol.
Bulk Checking in Practice
Testing the skill on 10 random domains simultaneously:
for domain in \
random-test-xyz-12345.com \
super-ultra-mega-rare-domain-999.io \
claude-code-rocks.dev \
totally-available-probably.sh \
anthropic.com \
openai.com \
vercel.com \
cloudflare.com \
best-startup-name-ever-2026.ai \
my-cool-project-abc.net; do
(
HTTP_CODE=$(curl -sL -o /dev/null -w "%{http_code}" "https://rdap.org/domain/$domain")
if [ "$HTTP_CODE" = "404" ]; then
echo "✓ $domain is AVAILABLE"
else
echo "✗ $domain is REGISTERED"
fi
) &
done
waitResults in under 2 seconds:
✓ random-test-xyz-12345.com is AVAILABLE
✓ super-ultra-mega-rare-domain-999.io is AVAILABLE
✓ claude-code-rocks.dev is AVAILABLE
✓ totally-available-probably.sh is AVAILABLE
✓ best-startup-name-ever-2026.ai is AVAILABLE
✓ my-cool-project-abc.net is AVAILABLE
✗ anthropic.com is REGISTERED
✗ openai.com is REGISTERED
✗ vercel.com is REGISTERED
✗ cloudflare.com is REGISTEREDFor registered domains, you can get detailed status:
curl -sL "https://rdap.org/domain/anthropic.com" | jq -r '.status[]?'client delete prohibited
client transfer prohibited
client update prohibitedWith registration dates:
curl -sL "https://rdap.org/domain/cloudflare.com" | \
jq -r '.events[]? | select(.eventAction == "registration") | .eventDate'2009-02-17T22:07:54ZThe parallel approach scales to dozens of domains. For bulk checking (50+ domains), add rate limiting:
for domain in "${domains[@]}"; do
# Check domain
curl -sL "https://rdap.org/domain/$domain" | jq -r '.status // "AVAILABLE"'
sleep 0.5 # 500ms delay to avoid rate limits
doneIf you hit HTTP 429 (rate limited), the skill handles it gracefully. RDAP registries typically allow 10-20 requests per second, far more than WHOIS aggregators.
What I Learned
String parsing is fragile across heterogeneous systems. When every data source formats responses differently, pattern matching becomes whack-a-mole. You fix one TLD, break another, repeat forever.
Structured protocols reduce maintenance burden. RDAP returns JSON with standardized fields across all registries. The schema doesn’t drift. The error codes are consistent. You write the parsing logic once.
Privacy and reliability don’t have to trade off. RDAP queries go directly to official registries, not third-party aggregators. You get authoritative data without leaking query patterns to data brokers.
And here’s the part I didn’t expect: the instructions-based skill pattern works better than a CLI package for simple tasks. curl and jq are already installed. No npm install, no version conflicts, no maintenance overhead. The skill just works.
Sometimes the right abstraction is a protocol, not a library.