For years, developers who needed domain registration data were stuck with WHOIS. Port 43 connections returned unstructured text output that even changed format between registrars. You'd write brittle regex patterns to parse dates, registrar names, and status codes, and those parsers would break the moment a registry changed their text template.

RDAP (Registration Data Access Protocol) fixes this. As the ICANN-mandated successor to legacy WHOIS, RDAP delivers domain data over HTTPS as clean JSON. Instead of regex hell, you get structured responses with predictable field names. Instead of maintaining parser scripts for 1,000+ TLDs, you query a single standard format.

RDAP vs. WHOIS: The Evolution of Domain Lookups

RDAP is a RESTful API standard for accessing domain registration data, designed by the IETF (RFC 7480-7484) and mandated by ICANN to replace WHOIS. Key differences:

Format: RDAP returns structured JSON with predictable field names. Date fields are ISO 8601 timestamps. WHOIS returns freeform text with no standard structure.

Access method: RDAP uses standard HTTPS GET requests. WHOIS requires opening TCP connections to port 43.

Internationalization: RDAP natively supports UTF-8. IDN domains and contact information display correctly without extra encoding layers.

The Architecture of an RDAP Lookup

RDAP solves the WHOIS bootstrap problem with standardized bootstrap registries. IANA publishes machine-readable JSON files that map TLD namespaces to their RDAP servers. The lookup flow: (1) Query IANA's bootstrap registry to find the RDAP server URL for the target TLD, (2) Send an HTTPS GET request to {rdap-base-url}/domain/{domain-name}, (3) Parse the structured JSON response.

python
import requests
from datetime import datetime

def get_rdap_bootstrap():
    """Fetch the IANA RDAP bootstrap file mapping TLDs to servers."""
    response = requests.get('https://data.iana.org/rdap/dns.json')
    response.raise_for_status()
    bootstrap = response.json()
    tld_map = {}
    for service in bootstrap.get('services', []):
        tlds = service[0]
        servers = service[1]
        for tld in tlds:
            if servers:
                tld_map[tld.lower()] = servers[0]
    return tld_map

def query_domain_rdap(domain):
    """Query RDAP data for a specific domain."""
    parts = domain.lower().split('.')
    tld = parts[-1]
    tld_map = get_rdap_bootstrap()

    if tld not in tld_map:
        raise ValueError(f"No RDAP server found for .{tld} domains")

    rdap_server = tld_map[tld]
    query_url = f"{rdap_server}domain/{domain}"

    headers = {
        'Accept': 'application/rdap+json',
        'User-Agent': 'DomainMonitor/1.0'
    }

    response = requests.get(query_url, headers=headers, timeout=10)

    if response.status_code == 404:
        return {'status': 'not_found', 'domain': domain}
    elif response.status_code == 429:
        raise Exception('Rate limited')

    response.raise_for_status()
    return response.json()

Parsing Key Fields

python
def parse_rdap_response(rdap_data):
    """Extract useful fields from RDAP response."""
    if not rdap_data:
        return {'domain': None, 'status': 'available', 'expiration': None, 'registrar': None}

    domain = rdap_data.get('ldhName')
    events = rdap_data.get('events', [])
    expiration_date = None
    registration_date = None

    for event in events:
        action = event.get('eventAction')
        date_str = event.get('eventDate')
        if action == 'expiration':
            expiration_date = date_str
        elif action == 'registration':
            registration_date = date_str

    registrar_name = None
    for entity in rdap_data.get('entities', []):
        if 'registrar' in entity.get('roles', []):
            vcard = entity.get('vcardArray', [None, []])
            for field in vcard[1]:
                if field[0] == 'fn':
                    registrar_name = field[3]
                    break

    return {
        'domain': domain,
        'status': 'registered',
        'expiration': expiration_date,
        'registration': registration_date,
        'registrar': registrar_name,
        'status_codes': rdap_data.get('status', [])
    }

The Privacy Barrier: GDPR and Redaction

In 2018, GDPR changed WHOIS forever. Registrars started redacting contact information, and the practice spread globally. Registrar information and domain dates remain public, but registrant and admin contact details are typically redacted. Modern RDAP responses do include an abuse contact that isn't redacted — required to be visible under ICANN policy.

Best Practices: Rate Limiting and Caching

RDAP servers implement aggressive rate limiting — typically 100-500 requests per minute per IP. WHOIS data is slow-moving, so caching is essential. Use TTLs based on monitoring needs: 24 hours for general monitoring, 4-6 hours for expiration monitoring, 1 hour for high-value targets you're trying to acquire, and 7 days for historical analysis datasets.

Real-World Use Case: Brand Protection

Build a monitoring tool that generates common typo variations of your domain (character substitutions, hyphen insertions, common suffixes) and checks their RDAP status daily. When a variation goes from "domain not found" to "registered," you get an alert. Once you find valuable domains through RDAP queries, you can act on that intelligence using the name.com API to register or backorder those domains.