Domain Availability at Scale: Building an RDAP-Based Port City Franchise Registry Checker
What We Built
We solved ticket t-f860fe03 by constructing an automated domain-availability research pipeline to validate a "Queen of [City]" franchise concept. The goal: determine how many port cities worldwide have available queenof[city].com domains for a tour-operator boat-naming franchise model.
The solution involved three domain-checking scripts, a master orchestrator, and forensic analysis of taken domains to understand competitive landscape:
/Users/cb/icloud-jada-ops/ticket-runner/check_queenof_domains.py— Core domain batch checker with RDAP fallback/Users/cb/icloud-jada-ops/ticket-runner/check_queenof_dream.py— Dream destination cities (~50 targets)/Users/cb/icloud-jada-ops/ticket-runner/check_queenof_us.py— US port cities (~40 targets)/Users/cb/icloud-jada-ops/ticket-runner/probe_taken.py— WHOIS fingerprinting of registered domains/Users/cb/icloud-jada-ops/ticket-runner/master_check.py— Orchestrator combining all three checks
Why RDAP, Not WHOIS?
Initial attempts used traditional WHOIS queries returned 130+ "unknown" responses from Verisign's registry — even for control domains we knew were registered (like queenofsandiego.com). This is because WHOIS servers employ IP-based rate limiting and throttling. A single IP querying 100+ domains rapidly triggers Verisign's abuse filters, poisoning the result set.
RDAP (Registration Data Access Protocol) solved this elegantly:
- HTTP/JSON endpoint instead of legacy socket protocol
- Status codes are canonical:
404 Not Found= domain available;200 OK= registered - Significantly less rate-limiting (HTTP caching-aware servers)
- Authoritative — queries Verisign's own registry directly
- Parseable structure (JSON) instead of freeform text
Example RDAP query against Verisign's endpoint for a city domain:
curl -s "https://rdap.verisign.com/com/v1/domain/queenofportland.com"
A 404 response means the domain is available for registration. A 200 with object data means it's taken.
Architecture: Three-Tier Checking Strategy
Tier 1: Dream Destinations (check_queenof_dream.py)
Hand-curated list of ~50 globally iconic port cities (Sydney, Barcelona, Vancouver, Rio de Janeiro, etc.). These represent premium franchise opportunities with strong tourism and wealth indicators. Each domain checked via RDAP; results written to /Users/cb/icloud-jada-ops/QUEEN-OF-DREAM-DESTINATIONS-2026-06-04.md.
Tier 2: US Port Cities (check_queenof_us.py)
Programmatically enumerate ~40 US cities with significant marine commerce and tourism (San Diego, New York, Miami, Seattle, New Orleans, etc.). Each checked via RDAP; results to /Users/cb/icloud-jada-ops/QUEEN-OF-US-CITIES-2026-06-04.md.
Tier 3: Taken Domain Forensics (probe_taken.py)
For domains already registered, we ran WHOIS queries to identify:
- Registrant organization (corporate vs. individual squatter)
- Registration date (age of domain)
- Nameserver delegation (active vs. parked)
- HTTP probing of live IPs to fingerprint content (parked landing pages, actual business sites, etc.)
Results saved to /Users/cb/icloud-jada-ops/QUEEN-OF-TAKEN-DOMAINS-2026-06-04.md. This reveals whether "taken" domains are actively competing businesses or speculative registrations that might be negotiable.
Core Implementation: RDAP Batch Query Pattern
The base check_queenof_domains.py implements a resilient batch checker:
def check_domain_rdap(domain):
"""Query Verisign RDAP for registration status"""
try:
response = requests.get(
f"https://rdap.verisign.com/com/v1/domain/{domain}",
timeout=5
)
if response.status_code == 404:
return "AVAILABLE"
elif response.status_code == 200:
return "REGISTERED"
else:
return "UNKNOWN"
except requests.Timeout:
return "TIMEOUT"
except Exception as e:
return f"ERROR: {str(e)}"
# Batch processing with rate limiting
for city in city_list:
domain = f"queenof{city}.com"
status = check_domain_rdap(domain)
results[domain] = status
time.sleep(0.5) # Respectful rate limiting
Key design decisions:
- Timeouts: 5-second timeout per RDAP call prevents hanging on unresponsive servers
- Rate limiting: 0.5-second sleep between requests (2 queries/second) avoids triggering Verisign's abuse filters
- Exception handling: Distinguishes timeouts, network errors, and parse failures for diagnostic purposes
- Idempotency: Results logged with timestamps so runs can be repeated and merged without duplication
Master Orchestrator Pattern
master_check.py coordinates all three tiers:
#!/usr/bin/env python3
import subprocess
import json
from datetime import datetime
checks = [
("Dream Destinations", "check_queenof_dream.py"),
("US Port Cities", "check_queenof_us.py"),
("Franchise Domains", "check_queenof_domains.py")
]
results = {}
timestamp = datetime.utcnow().isoformat()
for label, script in checks:
print(f"[{timestamp}] Running {label}...")
proc = subprocess.run(["python3", script], capture_output=True)
results[label] = json.loads(proc.stdout)
# Aggregate statistics
available_count = sum(
1 for r in results.values()
for v in r.values() if v == "AVAILABLE"
)
print(f"Total available domains: {available_count}")
This pattern ensures:
- All three checks run sequentially (avoiding overload on registry)
- Results are machine-readable JSON for downstream processing
- Timestamp annotations support audit trails and replay
- Aggregate statistics are computed automatically
Data Formats and Reporting