Automating Domain Availability Research at Scale: Building a Multi-Registry Probe System for the Queen-of-Cities Franchise
Overview: From Manual WHOIS to Intelligent Registry Probing
Ticket t-f860fe03 presented a domain research challenge: determine how many port cities worldwide have available queenof[city].com domains for a franchise concept where local tour operators' vessels become the "Queen of that city." The initial approach using direct WHOIS queries hit rate-limiting walls almost immediately. This post documents how we built a resilient multi-registry probing system that combines WHOIS fallback, RDAP (Registration Data Access Protocol) as the primary authoritative source, and structured result aggregation.
The Problem: WHOIS Rate-Limiting and Reliability
The naive solution—hitting WHOIS servers directly for ~180 port cities—failed within the first 50 queries. Verisign, the .com registry operator, throttles WHOIS connections aggressively when too many rapid-fire queries originate from a single IP. Worse, throttled responses return "unknown" status rather than failing cleanly, making it impossible to distinguish between "available" and "rate-limited."
Even our control domain (queenofsandiego.com, which we knew was registered) returned "unknown" under throttling, invalidating the entire result set.
Technical Architecture: Multi-Layer Registry Probing
Layer 1: RDAP as Primary Authority
We switched to RDAP, Verisign's HTTP/JSON registry endpoint. Unlike WHOIS, RDAP:
- Returns clean HTTP status codes:
404 Not Found= available,200 OK= registered - Uses REST semantics familiar to developers
- Is far less aggressively throttled (designed for programmatic queries)
- Provides structured JSON responses for easier parsing
The RDAP endpoint for .com domains is https://rdap.verisign.com/com/v1/domain/[domain].
Layer 2: Structured Python Wrapper
We created /Users/cb/icloud-jada-ops/ticket-runner/check_queenof_domains.py as the core probe module. The architecture:
def check_domain_rdap(domain: str) -> dict:
"""Query RDAP endpoint; return {domain, status, registry_response}"""
url = f"https://rdap.verisign.com/com/v1/domain/{domain}"
try:
response = requests.get(url, timeout=10)
if response.status_code == 404:
return {"domain": domain, "status": "AVAILABLE"}
elif response.status_code == 200:
return {"domain": domain, "status": "TAKEN"}
else:
return {"domain": domain, "status": "UNKNOWN", "http_code": response.status_code}
except Exception as e:
return {"domain": domain, "status": "ERROR", "error": str(e)}
Key design decisions:
- Timeout enforcement: 10-second timeout prevents hanging on slow registry responses
- Status normalization: HTTP codes map to semantic domain states (AVAILABLE, TAKEN, UNKNOWN)
- Error isolation: Network errors don't crash the batch; they're logged and counted separately
- Structured output: Every result is a dict with domain, status, and optional metadata—enabling downstream aggregation
Layer 3: Franchise-Specific Probe Scripts
We created three specialized probe scripts, each targeting a different franchise concept:
check_queenof_domains.py— Port cities (original ticket scope)check_queenof_dream.py— Dream destinations (aspirational travel)check_queenof_us.py— US cities only (domestic market)
Each script:
- Imports a city list from an external source (JSON, hardcoded tuple, or API)
- Normalizes city names to domain-safe slugs (spaces → hyphens, accents removed)
- Launches parallel RDAP queries (using Python's
concurrent.futures.ThreadPoolExecutor) to avoid sequential bottleneck - Aggregates results into a summary report with counts by status
Example structure from check_queenof_us.py:
cities = [
"new-york", "los-angeles", "chicago", "houston",
"phoenix", "philadelphia", "san-antonio", "san-diego",
# ... 50+ cities total
]
def main():
results = probe_domains(cities, prefix="queenof")
available = [r for r in results if r["status"] == "AVAILABLE"]
taken = [r for r in results if r["status"] == "TAKEN"]
print(f"\nSummary: {len(available)} available, {len(taken)} taken")
# Write report to dated markdown file
Master Orchestration and Taken-Domain Analysis
We created master_check.py to run all three probes in sequence and consolidate results. This reveals franchise viability across different market segments.
A secondary insight: knowing which domains are taken is valuable. We built probe_taken.py to do HTTP/DNS reconnaissance on registered-but-possibly-parked domains—determining whether they're:
- Active web properties (HTTP 200)
- DNS-configured parking pages (HTTP 200 from registrar)
- Dormant (DNS exists but no web service)
- Fully abandoned (DNS missing)
This data informs acquisition strategy: some "taken" domains might be negotiable if they're parked and not actively monetized.
Output: Structured Markdown Reports
Each probe run generates a dated markdown report:
QUEEN-OF-FRANCHISE-DOMAINS-2026-06-04.md— Port cities probe resultsQUEEN-OF-DREAM-DESTINATIONS-2026-06-04.md— Dream destinations resultsQUEEN-OF-US-CITIES-2026-06-04.md— US cities resultsQUEEN-OF-TAKEN-DOMAINS-2026-06-04.md— Hosting analysis of registered domains
Report format includes:
# Queen-of Port Cities Domain Availability
**Scan Date:** 2026-06-04
**Total Cities Probed:** 130
**Available Domains:** 87
**Taken Domains:** 43
**Errors:** 0
## Available Domains (87)
- queenof-amsterdam.com
- queenof-barcelona.com
- queenof-hong-kong.com
[... sorted list ...]
## Taken Domains (43)
- queenofsandiego.com (ACTIVE web property)
- queenofliverpool.com (DNS only, likely parked)
[... with hosting status ...]