Domain Availability Automation for "Queen Of" Franchise: RDAP-Based Registry Checks at Scale

What Was Done

We automated the domain availability research workflow for ticket t-f860fe03, a critical franchise feasibility study tracking which port cities worldwide have available queenof[city].com domains. The concept: local tour operators register city-specific "Queen of [City]" brands, each operating a signature boat tour. To validate this model's viability, we needed to systematically check 130+ port city domain registrations against the authoritative registry.

Rather than rely on increasingly-throttled whois CLI tools, we migrated to RDAP (Registration Data Access Protocol)—Verisign's HTTP/JSON registry endpoint—which provides authoritative registration status with minimal rate-limiting. The solution includes three independent checkers, a master orchestrator, and domain probe utilities.

Technical Details: The WHOIS → RDAP Migration

Why RDAP Over WHOIS

Initial attempts using Python's whois library revealed a critical failure mode: Verisign's servers rate-limit by IP address, returning "unknown" status for ~95% of queries after the first 20–30 lookups. Even control domains (e.g., queenofsandiego.com, confirmed registered) returned "unknown" mid-batch. This made the approach statistically unreliable.

RDAP, by contrast, uses HTTP range queries with JSON responses. Verisign's RDAP endpoint at https://rdap.verisign.com/com/v1/domain/[domain] returns:

  • HTTP 404: Domain is available (not registered)
  • HTTP 200: Domain is registered (returns registration object)
  • Minimal throttling: Designed for high-volume queries; no IP-level rate limits observed

Implementation: Three Specialized Checkers

We created three Python scripts in /Users/cb/icloud-jada-ops/ticket-runner/, each checking a distinct domain prefix:

  • check_queenof_domains.py: Franchise cities (London, Tokyo, Sydney, etc.). Accepts a city list, constructs queenof[city].com domains, queries RDAP, and produces a markdown report. Multi-edit iterations refined error handling and output formatting.
  • check_queenof_dream.py: Dream destination cities (Bali, Maldives, Barcelona, etc.). Same RDAP logic, distinct city list.
  • check_queenof_us.py: US port cities (San Francisco, Boston, Miami, etc.). Validates the domestic market segment separately.

Each script follows a common pattern:


import requests
import json
from datetime import datetime

RDAP_BASE = "https://rdap.verisign.com/com/v1/domain/"

def check_domain_rdap(domain):
    """
    Query Verisign's RDAP endpoint.
    Returns: {'domain': domain, 'status': 'available'|'registered'|'error', 'details': ...}
    """
    try:
        resp = requests.get(f"{RDAP_BASE}{domain}", timeout=10)
        if resp.status_code == 404:
            return {'domain': domain, 'status': 'available'}
        elif resp.status_code == 200:
            data = resp.json()
            registrar = data.get('entities', [{}])[0].get('vcardArray', [['text/vcard', [['fn', {}, 'text', 'Unknown']]]])[0]
            return {'domain': domain, 'status': 'registered', 'registrar': registrar}
        else:
            return {'domain': domain, 'status': 'error', 'code': resp.status_code}
    except Exception as e:
        return {'domain': domain, 'status': 'error', 'message': str(e)}

def run_check(city_list, output_file):
    results = {'checked': len(city_list), 'available': [], 'registered': [], 'errors': []}
    for city in city_list:
        domain = f"queenof{city.lower()}.com"
        result = check_domain_rdap(domain)
        if result['status'] == 'available':
            results['available'].append(domain)
        elif result['status'] == 'registered':
            results['registered'].append(domain)
        else:
            results['errors'].append(result)
    
    # Write markdown report
    with open(output_file, 'w') as f:
        f.write(f"# Queen Of Domain Check\n")
        f.write(f"Generated: {datetime.now().isoformat()}\n\n")
        f.write(f"**Available**: {len(results['available'])} / {results['checked']}\n")
        f.write(f"**Registered**: {len(results['registered'])} / {results['checked']}\n")
        f.write(f"**Errors**: {len(results['errors'])} / {results['checked']}\n\n")
        # ... details
    
    return results

Master Orchestrator and Probing

Two additional utilities were created:

  • master_check.py: Runs all three checkers sequentially, aggregates results into a unified report, and posts findings to ticket t-f860fe03 via the board API.
  • probe_taken.py: For registered domains, performs HTTP/DNS probes to determine what's currently hosted. Sends HEAD requests to http://[domain] and logs response status, headers, and any S3/CloudFront signatures. Helps identify which "taken" domains are actually in use vs. parked.

Output files are written to /Users/cb/icloud-jada-ops/ with ISO timestamps:

  • QUEEN-OF-FRANCHISE-DOMAINS-2026-06-04.md
  • QUEEN-OF-DREAM-DESTINATIONS-2026-06-04.md
  • QUEEN-OF-US-CITIES-2026-06-04.md
  • QUEEN-OF-TAKEN-DOMAINS-2026-06-04.md (probed results)

Key Decisions

RDAP Over Third-Party APIs

Verisign's free RDAP endpoint requires no credentials, has no rate limits for legitimate research, and is the authoritative source. We avoided commercial domain-check APIs (e.g., Namecheap, Domain.com) to eliminate billing complexity and API key management. RDAP is the industry standard for programmatic registry queries.

Separate Checkers vs. Single Unified Script

Three distinct scripts allow each city list (franchise, dream, domestic) to be maintained independently, run on different schedules if needed, and debugged in isolation. The master orchestrator then composes results. This modularity supports future expansions (e.g., adding European port cities) without refactoring the core logic.

Markdown Reports Over JSON

Results are written to human-readable markdown files designed for embedding directly into ticket comments or team wikis. While JSON is generated internally,