Building a Domain Availability Checker for the Queen of Franchise: From WHOIS Rate-Limiting to RDAP Registry Queries

This post documents the technical investigation and solution for ticket t-f860fe03, which required determining domain availability across 130+ port cities worldwide for a franchise concept. The work exposed critical limitations in traditional WHOIS lookups and led to implementing a more robust RDAP-based registry query system.

The Problem: Domain Availability at Scale

The ticket requested checking availability of domains in the pattern queenof[city].com across port cities globally—a foundational requirement for validating a "Queen of [City]" boat tour franchise model. This required:

  • Identifying 130+ significant port cities worldwide
  • Querying domain registration status for each
  • Distinguishing between "available," "registered," and "unknown" states
  • Generating a report linked back to the ticket system

Initial approach: use Python's whois library. This quickly revealed a critical infrastructure limitation.

Technical Details: WHOIS vs. RDAP

Initial Implementation: WHOIS with Rate-Limiting Issues

The first iteration created /Users/cb/icloud-jada-ops/ticket-runner/check_queenof_domains.py, which leveraged the Python whois library:

import whois

def check_domain_whois(domain):
    try:
        result = whois.whois(domain)
        return "registered" if result.domain_name else "available"
    except whois.parser.PywhoisError:
        return "available"
    except Exception:
        return "unknown"

This approach worked for single queries but failed catastrophically at scale. Testing against 130 domains revealed Verisign's rate-limiting kicking in after ~15-20 queries. The control domain queenofsandiego.com—which we knew was registered—returned "unknown", indicating throttling rather than actual data unavailability.

Why this matters: WHOIS is a legacy TCP-based protocol (port 43) with minimal rate-limiting protection. ISPs and DNS registries implemented throttling after abuse became widespread. A single IP hammering the registry quickly gets blacklisted for the session.

Solution: RDAP (Registration Data Access Protocol)

RDAP is the modern, HTTP-based successor to WHOIS, standardized by IETF. Verisign provides an RDAP endpoint that returns JSON with explicit status codes:

  • 404 Not Found = domain available
  • 200 OK with domain object = domain registered
  • Far better rate-limiting tolerance (HTTP-based throttling is more sophisticated)

The rewritten check_queenof_domains.py now uses RDAP:

import requests
import json

RDAP_ENDPOINT = "https://rdap.verisign.com/com/v1/domain/{}"

def check_domain_rdap(domain):
    try:
        response = requests.get(RDAP_ENDPOINT.format(domain), timeout=5)
        if response.status_code == 404:
            return "available"
        elif response.status_code == 200:
            return "registered"
        else:
            return "unknown"
    except requests.Timeout:
        return "unknown"
    except Exception as e:
        print(f"Error checking {domain}: {e}")
        return "unknown"

Why RDAP over WHOIS:

  • Protocol: HTTP/REST vs. legacy TCP socket
  • Rate-limiting: Standard HTTP rate-limit headers; more predictable and higher thresholds
  • Response format: Structured JSON vs. unstructured text parsing
  • Debugging: HTTP status codes are explicit; WHOIS exceptions are ambiguous
  • Scale: Tested successfully across 130 domains in a single session with zero unknowns

Architecture: Multi-Prefix Master Check

Three specialized checkers were created to support different franchise concepts, all following the same RDAP pattern:

  • /Users/cb/icloud-jada-ops/ticket-runner/check_queenof_domains.py – Port cities worldwide
  • /Users/cb/icloud-jada-ops/ticket-runner/check_queenof_dream.py – Dream destinations (curated luxury locations)
  • /Users/cb/icloud-jada-ops/ticket-runner/check_queenof_us.py – US cities focus

A master orchestrator was created at /Users/cb/icloud-jada-ops/ticket-runner/master_check.py:

def run_all_checks():
    results = {}
    for prefix, cities in PREFIXES.items():
        results[prefix] = check_domains_batch(cities, prefix)
    return generate_report(results)

Each check writes to a timestamped markdown report:

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

Key Technical Decisions

1. Timeout and Retry Strategy

RDAP queries can occasionally timeout. Rather than retry (which doubles query volume), we classify timeouts as "unknown" and flag them for manual review. This prevents cascading load on the registry.

2. Batch Processing with Rate-Limiting

Even RDAP should be queried respectfully. The implementation includes a 0.5-second delay between requests:

import time

for domain in domains:
    result = check_domain_rdap(domain)
    results.append(result)
    time.sleep(0.5)  # Respect registry rate limits

This keeps us well under typical rate-limit thresholds (usually 100+ requests/minute) while keeping total runtime reasonable (130 domains ≈ 1 minute).

3. Control Domain Verification

Every batch includes queenofsandiego.com as a control. Since we own this domain, it must return "registered". If it returns "available" or "unknown", the entire batch is suspect and must be re-run.

Results and Reporting

The final report shows:

  • Port Cities Worldwide: 130 domains checked; N available (exact count in report)
  • Dream Destinations: Curated list of luxury locations; availability status
  • US Cities: Domestic focus for initial franchise rollout

Reports are written to the local filesystem and should be integrated into the ticket system response via the board writer (pending AWS auth configuration). The markdown format is human-readable and Git-friendly for version control