```html

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