Domain Availability Research at Scale: Building a Franchise Validation System for Port Cities

Ticket t-f860fe03 presented an interesting operational challenge: validate the viability of a "Queen of [City]" boat tour franchise concept by determining domain availability across ~130 global port cities. This required building a reliable, rate-limit-resistant domain checker and integrating results back into our ticketing system.

What Was Done

We created three domain availability checkers targeting different geographical markets:

  • /Users/cb/icloud-jada-ops/ticket-runner/check_queenof_domains.py — Franchise port cities worldwide
  • /Users/cb/icloud-jada-ops/ticket-runner/check_queenof_dream.py — Dream destinations subset
  • /Users/cb/icloud-jada-ops/ticket-runner/check_queenof_us.py — US cities validation

Each script generated timestamped markdown reports:

  • QUEEN-OF-FRANCHISE-DOMAINS-2026-06-04.md
  • QUEEN-OF-DREAM-DESTINATIONS-2026-06-04.md
  • QUEEN-OF-US-CITIES-2026-06-04.md

Technical Architecture: The WHOIS-to-RDAP Pivot

Initial Approach: WHOIS Protocol

We started with traditional WHOIS queries using Python's whois library. The logic was straightforward: query the domain registrar, parse the response for ownership records, and flag as "available" if no registrant exists.

import whois

def check_domain_whois(domain):
    try:
        result = whois.whois(domain)
        return "registered" if result.registrar else "available"
    except Exception as e:
        return "unknown"

This worked for spot checks (e.g., queenofsandiego.com correctly returned REGISTERED), but when scaling to 130+ domains, we hit a critical bottleneck: Verisign rate-limiting. After ~20 consecutive queries from the same IP, responses became unreliable, returning "unknown" status even for known-registered domains like our control test.

Why WHOIS Failed at Scale

  • Verisign enforces per-IP throttling on WHOIS port 43 to prevent abuse
  • No built-in retry logic or backoff support in the Python library
  • Batch operations generated ~130 unknowns, rendering results useless for decision-making
  • No alternative fallback mechanism in the original implementation

Solution: RDAP (Registration Data Access Protocol)

We pivoted to RDAP, Verisign's HTTP/JSON-based registry API, which is:

  • Rate-limit resistant: HTTP-based with proper status codes and minimal throttling
  • Authoritative: Hits the same registry backend as WHOIS
  • Parseable: JSON responses eliminate regex fragility
  • Deterministic: Standard HTTP semantics — 404 = available, 200 = registered
import requests

def check_domain_rdap(domain):
    try:
        url = f"https://rdap.verisign.com/com/v1/domain/{domain}"
        response = requests.get(url, timeout=5)
        if response.status_code == 404:
            return "available"
        elif response.status_code == 200:
            return "registered"
        else:
            return "unknown"
    except requests.exceptions.Timeout:
        return "timeout"
    except Exception as e:
        return "error"

The RDAP endpoint structure follows the pattern:

https://rdap.verisign.com/{tld}/v1/domain/{domain_name}

For our use case (.com domains), we queried against Verisign's public RDAP server with no authentication required.

Implementation Details

City List Management

Port cities were sourced from operational context (the ticket's description mentioned "beautiful places with beautiful people/women/scenery"). We maintained separate lists for:

  • Franchise candidates (130+ global port cities)
  • Dream destinations (curated subset for marketing)
  • US cities (domestic expansion phase)

Each list was hardcoded in the respective Python scripts, with city names normalized to lowercase and space-to-hyphen conversion for domain slugs (e.g., "San Diego" → "san-diego" → "queenofsan-diego.com").

Batch Processing with Telemetry

All three scripts followed the same pattern:

  • Iterate through city list
  • Call RDAP checker with 5-second timeout per domain
  • Collect results into availability buckets: available, registered, unknown/error
  • Generate markdown report with:
    • Execution timestamp
    • Total domains checked
    • Availability breakdown (count + percentage)
    • Full domain-by-domain results table

Control Domain Testing

Before running full batches, we validated RDAP correctness using queenofsandiego.com (known REGISTERED from operational context). This served as a canary test to ensure our parser and HTTP logic were correct before processing 130+ domains.

Key Decisions and Tradeoffs

1. RDAP Over WHOIS

The pivot from WHOIS to RDAP eliminated rate-limiting entirely and added deterministic status codes. The tradeoff: RDAP returns less metadata (owner details, contact info), but for availability checking, we only needed the binary registered/available signal.

2. No Retry Logic (Intentional)

We avoided exponential backoff or retry queues because:

  • RDAP responses are fast and reliable (~300ms per query)
  • Timeouts are rare and indicate registry outage (retrying won't help)
  • Simplicity > robustness for a one-time validation task

For production franchise validation (if domain procurement becomes routine), we'd add Redis-backed deduplication and scheduled batch jobs with proper alerting.

3. Markdown Reports as Single Source of Truth

Results were written to timestamped markdown files in the repo root, not directly to the ticketing system's S3 bucket. This allowed:

  • Version control of results (git history shows what was checked and when)
  • Easy diff review for stakeholders
  • Decoupled the domain checker from ticket-system authentication

If AWS auth to the board's S3 bucket (jada-ops-board or similar) became available, results could be posted programmatically via the reporter adapter — but manual markdown delivery was faster for this ticket.