Scaling Domain Availability Checks: From Whois Rate-Limiting to RDAP Registry Queries
Overview
Ticket t-f860fe03 required determining how many port cities worldwide could support a "Queen of [City]" domain franchise — a tour operator concept where local boat operators could claim queenof[city].com domains. The challenge: checking availability across 130+ potential port cities while working around whois rate-limiting and designing a scalable, authoritative domain-lookup pipeline.
What Was Done
We built a multi-stage domain-availability verification system:
- Initial whois approach: Created
/Users/cb/icloud-jada-ops/ticket-runner/check_queenof_domains.pyto batch-query the `.com` registry via whois CLI - Pivoted to RDAP: Switched from Verisign whois (throttled, returns "unknown" under load) to RDAP HTTP/JSON registry lookups (authoritative, rate-limit friendly)
- Built master aggregator: Created
master_check.pyto orchestrate three concurrent domain-check jobs across different city categories (franchise ports, dream destinations, US cities) - Infrastructure probing: Built
probe_taken.pyto probe what's actually hosted on taken domains (CloudFront, S3, nginx, etc.) — key for understanding competitive landscape - Deployed web infrastructure: Stood up
dragonbodyguards.comand related domains on AWS CloudFront + S3 using lambda routing, ACM certificates, and Namecheap DNS management
Technical Details: Registry Lookup Pipeline
Whois Approach (Initial, Rate-Limited)
The first implementation used Python's whois library wrapping the CLI:
#!/usr/bin/env python3
import whois
domains = ["queenofbangkok.com", "queenofdubai.com", ...]
for domain in domains:
try:
result = whois.whois(domain)
status = "TAKEN" if result.domain_name else "AVAILABLE"
except Exception as e:
status = "UNKNOWN" # Verisign rate-limit response
Problem: Across 130 domains, whois returned ~130 "unknown" statuses. Verisign (the `.com` registry operator) throttles by IP when queried rapidly. Even our control domain queenofsandiego.com (known to be registered) returned "unknown" — a clear signal that whois was unreliable at scale.
RDAP Solution (Authoritative, HTTP-Based)
Switched to RDAP (Registration Data Access Protocol), Verisign's HTTP/JSON endpoint. RDAP is:
- Authoritative: Direct registry query, not cached or intermediated
- Rate-limit friendly: HTTP 429 is explicit; no silent throttling
- JSON responses: Structured data, easier to parse than whois text
- Deterministic: 404 = available, 200 = registered, 429 = backoff and retry
#!/usr/bin/env python3
import requests
import time
RDAP_BASE = "https://rdap.verisign.com/com/v1/domain/"
def check_domain_rdap(domain):
"""Query Verisign RDAP for domain status."""
url = f"{RDAP_BASE}{domain}"
try:
resp = requests.get(url, timeout=5)
if resp.status_code == 404:
return "AVAILABLE"
elif resp.status_code == 200:
return "TAKEN"
elif resp.status_code == 429:
time.sleep(2) # Explicit backoff
return check_domain_rdap(domain) # Retry
except requests.exceptions.Timeout:
return "TIMEOUT"
return "ERROR"
# Test control domain
print(check_domain_rdap("queenofsandiego.com")) # Output: TAKEN ✓
Result: 0 unknowns, clean categorization. Control domain correctly identified as TAKEN.
Infrastructure: Multi-Check Orchestration
File Structure
/Users/cb/icloud-jada-ops/ticket-runner/
├── check_queenof_domains.py # Franchise port cities
├── check_queenof_dream.py # Dream destinations (scenic, romantic)
├── check_queenof_us.py # US port cities only
├── master_check.py # Orchestrator: runs all three, aggregates results
└── probe_taken.py # Probes hosting details for taken domains
Master Orchestrator Pattern
master_check.py uses subprocess + file aggregation:
#!/usr/bin/env python3
import subprocess
import json
from datetime import datetime
checks = [
("check_queenof_domains.py", "QUEEN-OF-FRANCHISE-DOMAINS"),
("check_queenof_dream.py", "QUEEN-OF-DREAM-DESTINATIONS"),
("check_queenof_us.py", "QUEEN-OF-US-CITIES"),
]
results = {}
for script, label in checks:
print(f"Running {script}...")
subprocess.run(["python3", script])
# Aggregator expects output file named "{LABEL}-{DATE}.md"
output_file = f"{label}-{datetime.now().strftime('%Y-%m-%d')}.md"
with open(output_file, 'r') as f:
results[label] = f.read()
# Write consolidated report
with open("CONSOLIDATED-REPORT.md", 'w') as f:
for label, content in results.items():
f.write(f"\n## {label}\n{content}\n")
Infrastructure Probing: What's Actually Hosted?
Created probe_taken.py to understand the competitive landscape — for domains we can't register, what's their current use case?
#!/usr/bin/env python3
import socket
import subprocess
def probe_domain(domain):
"""HTTP HEAD request to determine hosting provider."""
try:
# Resolve to IP
ip = socket.gethostbyname(domain)
# Check CloudFront (CNAME pattern)
result = subprocess.run(["host", domain], capture_output=True, text=True)
if "cloudfront" in result.stdout.lower():
return "CloudFront"
# Reverse-lookup to detect S3 / nginx / other
result = subprocess.run(["host", ip], capture_output=True, text=True)
if "amazonaws" in result.stdout.lower():
return "AWS S3 / ELB"
# HTTP header inspection
import requests
resp = requests.head(f"https://{domain}", timeout=3, allow_redirects=False)
if "Server" in resp.headers:
return resp.headers