Type something to search...
What is RDAP and how is it replacing WHOIS?

What is RDAP and how is it replacing WHOIS?

RDAP (Registration Data Access Protocol) is the modern replacement for WHOIS. It lets you look up registration data for domain names, IP addresses and autonomous system numbers, but instead of returning free-form text over port 43, it returns structured JSON over HTTPS with standard field names, proper support for international characters, and the ability to show different data to different users. ICANN required gTLD registries and registrars to support RDAP from 2019, and on 28 January 2025 it ended the requirement to run WHOIS, making RDAP the authoritative lookup service for gTLDs. You can query it with a simple request such as curl https://rdap.org/domain/example.com.

If you've ever tried to parse WHOIS output in a script, you'll appreciate the difference straight away. This guide explains why WHOIS was replaced, how RDAP works, how servers are discovered through IANA's bootstrap files, how to read an RDAP response, and how to query it from the command line, Python and JavaScript.

Why WHOIS Needed Replacing

WHOIS served the internet for decades, but it was designed for a much smaller network and has fundamental limitations:

  • No standard format. Every registry and registrar formats output differently, so parsing it reliably is painful.
  • No internationalisation. The protocol doesn't define a character encoding, so non-ASCII names and addresses are handled inconsistently.
  • No security. Queries and responses travel in plain text, with no way to verify the server.
  • No access control. Everyone gets the same answer, which makes it impossible to show full data to authorised users while hiding it from the public.
  • No discovery. There's no standard way to know which server to ask for a given domain.

Data protection laws such as GDPR made the last two problems urgent, because registries needed to redact personal data for the public while still meeting legitimate requests.

How RDAP Works

RDAP is a RESTful protocol defined by the IETF in a series of RFCs, first published in 2015 and since updated. The key ideas are simple.

HTTPS and Predictable URLs

Every lookup is an HTTPS GET request to a predictable path:

ObjectPath
Domain/domain/example.com
Name server/nameserver/ns1.example.com
Contact or registrar entity/entity/<handle>
IP network/ip/192.0.2.0
Autonomous system/autnum/64496
Server help/help

Responses use the media type application/rdap+json, and standard HTTP status codes tell you what happened.

Standard Status Codes

StatusMeaning
200Object found, data returned
404Object not found, for a domain usually meaning it isn't registered
400Bad request, such as an invalid domain name
429Rate limited, slow down
301, 302, 303Redirect to another RDAP server

That 404 is useful for availability checks, though a missing record doesn't guarantee you can register a name, because registries may reserve or block some names.

Bootstrapping: Finding the Right Server

RDAP solves WHOIS's discovery problem with bootstrap files published by IANA. For domains, the file at https://data.iana.org/rdap/dns.json maps each TLD to its registry's RDAP base URL. A client reads the file, finds the base URL for the TLD and appends the query path.

You can do this by hand with jq:

curl -s https://data.iana.org/rdap/dns.json \
  | jq -r '.services[] | select(.[0] | index("com")) | .[1][]'
https://rdap.verisign.com/com/v1/

Then query the registry directly:

curl -s https://rdap.verisign.com/com/v1/domain/example.com | jq .

Similar bootstrap files exist for IPv4 (ipv4.json), IPv6 (ipv6.json) and AS numbers (asn.json) under the same IANA path.

The public service at rdap.org does this lookup for you and redirects to the right server, which is handy for quick checks. Use -L with curl so it follows the redirect:

curl -s -L https://rdap.org/domain/example.com | jq .

Not every ccTLD appears in the bootstrap file, because ccTLD registries aren't bound by ICANN's requirements. If a TLD is missing, it either doesn't offer RDAP or hasn't registered its service with IANA, and you may need to fall back to WHOIS or the registry's website.

Differentiated Access

Because RDAP runs over HTTPS, servers can authenticate users and return different levels of detail to each. Anonymous users see redacted public data, while authorised parties, such as law enforcement under an agreed process, could see more. In practice, public RDAP responses today look similar to public WHOIS: personal contact data is redacted. See what is domain privacy protection for what that means for registrants.

Reading an RDAP Domain Response

Here's a trimmed response for a .com domain from the registry:

{
  "objectClassName": "domain",
  "handle": "2336799_DOMAIN_COM-VRSN",
  "ldhName": "EXAMPLE.COM",
  "status": [
    "client delete prohibited",
    "client transfer prohibited",
    "client update prohibited"
  ],
  "events": [
    { "eventAction": "registration", "eventDate": "1995-08-14T04:00:00Z" },
    { "eventAction": "expiration", "eventDate": "2027-08-13T04:00:00Z" },
    { "eventAction": "last changed", "eventDate": "2026-08-14T07:01:34Z" },
    {
      "eventAction": "last update of RDAP database",
      "eventDate": "2026-10-26T08:15:02Z"
    }
  ],
  "entities": [
    {
      "objectClassName": "entity",
      "roles": ["registrar"],
      "publicIds": [{ "type": "IANA Registrar ID", "identifier": "9999" }],
      "vcardArray": [
        "vcard",
        [
          ["version", {}, "text", "4.0"],
          ["fn", {}, "text", "Example Registrar, LLC"]
        ]
      ]
    }
  ],
  "nameservers": [
    { "objectClassName": "nameserver", "ldhName": "NS1.EXAMPLE-DNS.NET" },
    { "objectClassName": "nameserver", "ldhName": "NS2.EXAMPLE-DNS.NET" }
  ],
  "secureDNS": { "delegationSigned": true },
  "links": [
    {
      "rel": "related",
      "href": "https://rdap.example-registrar.com/domain/EXAMPLE.COM",
      "type": "application/rdap+json"
    }
  ]
}

The important parts:

  • ldhName: The domain in ASCII ("letters, digits, hyphen") form. Internationalized domains also have a unicodeName. See what is an internationalized domain name.
  • status: The EPP status codes, written in lowercase words. client transfer prohibited is RDAP's form of clientTransferProhibited, the registrar lock.
  • events: Dated events, including registration, expiration and last changed.
  • entities: Related parties, each with roles such as registrar, registrant, technical or abuse, and contact data in jCard format (vcardArray), a JSON version of vCard.
  • nameservers: The domain's delegated name servers.
  • secureDNS: Whether the domain is signed with DNSSEC.
  • links: Related URLs. For thin registries like .com, a related link points to the registrar's RDAP server, which holds the fuller record.
  • notices and remarks: Terms of use and explanatory text, omitted above for brevity.

Querying RDAP From the Command Line

Common jq Recipes

Get the expiry date:

curl -s -L https://rdap.org/domain/example.com \
  | jq -r '.events[] | select(.eventAction == "expiration") | .eventDate'

List status codes:

curl -s -L https://rdap.org/domain/example.com | jq -r '.status[]'

Show the registrar's name:

curl -s -L https://rdap.org/domain/example.com \
  | jq -r '.entities[] | select(.roles | index("registrar")) | .vcardArray[1][] | select(.[0] == "fn") | .[3]'

List name servers:

curl -s -L https://rdap.org/domain/example.com | jq -r '.nameservers[].ldhName'

Compare the name servers in the registration with what DNS actually returns, a quick sanity check when you change nameservers:

dig +short NS example.com

Following the Registrar Link

For .com and .net, the registrar holds contact and abuse details the registry doesn't. Follow the related link:

reg_url=$(curl -s -L https://rdap.org/domain/example.com \
  | jq -r '.links[] | select(.rel == "related") | .href' | head -n 1)
curl -s "$reg_url" | jq '.entities[] | {roles, handle}'

IP Addresses

RDAP also covers IP addresses, served by the Regional Internet Registries such as ARIN, RIPE NCC and APNIC:

curl -s -L https://rdap.org/ip/8.8.8.8 | jq -r '.name, .startAddress, .endAddress'

Dedicated RDAP Clients

If you prefer a WHOIS-like command, open-source clients exist, such as the rdap tool from the OpenRDAP project, which handles bootstrapping for you:

rdap example.com
rdap --json example.com

On Windows, PowerShell can query RDAP without extra tools:

$r = Invoke-RestMethod "https://rdap.org/domain/example.com"
($r.events | Where-Object eventAction -eq "expiration").eventDate

Using RDAP in Your Own Code

Python

This script implements bootstrapping itself instead of relying on rdap.org, caches the bootstrap file in memory, and prints the key fields for each domain:

import json
import sys
import urllib.error
import urllib.request

BOOTSTRAP_URL = "https://data.iana.org/rdap/dns.json"


def fetch_json(url: str) -> dict:
    req = urllib.request.Request(url, headers={"Accept": "application/rdap+json", "User-Agent": "domain-check/1.0"})
    with urllib.request.urlopen(req, timeout=15) as resp:
        return json.load(resp)


def build_bootstrap() -> dict[str, str]:
    data = fetch_json(BOOTSTRAP_URL)
    table = {}
    for tlds, urls in data["services"]:
        https_urls = [u for u in urls if u.startswith("https://")] or urls
        for tld in tlds:
            table[tld.lower()] = https_urls[0]
    return table


def rdap_domain(domain: str, bootstrap: dict[str, str]) -> dict | None:
    tld = domain.rstrip(".").rsplit(".", 1)[-1].lower()
    base = bootstrap.get(tld)
    if base is None:
        raise ValueError(f"No RDAP service registered for .{tld}")
    if not base.endswith("/"):
        base += "/"
    try:
        return fetch_json(f"{base}domain/{domain}")
    except urllib.error.HTTPError as err:
        if err.code == 404:
            return None
        raise


def summarise(domain: str, data: dict | None) -> None:
    if data is None:
        print(f"{domain}: not found (possibly available)")
        return
    events = {e["eventAction"]: e["eventDate"] for e in data.get("events", [])}
    registrar = next(
        (
            prop[3]
            for ent in data.get("entities", [])
            if "registrar" in ent.get("roles", [])
            for prop in ent.get("vcardArray", [None, []])[1]
            if prop[0] == "fn"
        ),
        "unknown",
    )
    print(f"{domain}")
    print(f"  registrar:  {registrar}")
    print(f"  registered: {events.get('registration', '-')}")
    print(f"  expires:    {events.get('expiration', '-')}")
    print(f"  status:     {', '.join(data.get('status', []))}")


if __name__ == "__main__":
    table = build_bootstrap()
    for name in sys.argv[1:]:
        summarise(name, rdap_domain(name, table))
python3 rdap_lookup.py example.com example.org
example.com
  registrar:  Example Registrar, LLC
  registered: 1995-08-14T04:00:00Z
  expires:    2027-08-13T04:00:00Z
  status:     client delete prohibited, client transfer prohibited, client update prohibited

JavaScript (Node.js 18+)

Node's built-in fetch makes RDAP queries straightforward. Save this as rdap.mjs:

const BOOTSTRAP_URL = "https://data.iana.org/rdap/dns.json";

async function getBootstrap() {
  const res = await fetch(BOOTSTRAP_URL);
  const data = await res.json();
  const table = new Map();
  for (const [tlds, urls] of data.services) {
    const url = urls.find((u) => u.startsWith("https://")) ?? urls[0];
    for (const tld of tlds) table.set(tld.toLowerCase(), url);
  }
  return table;
}

async function lookup(domain, table) {
  const tld = domain.split(".").pop().toLowerCase();
  let base = table.get(tld);
  if (!base) throw new Error(`No RDAP service for .${tld}`);
  if (!base.endsWith("/")) base += "/";

  const res = await fetch(`${base}domain/${domain}`, {
    headers: {
      Accept: "application/rdap+json",
      "User-Agent": "domain-check/1.0",
    },
  });
  if (res.status === 404) return { domain, registered: false };
  if (!res.ok) throw new Error(`HTTP ${res.status} for ${domain}`);

  const data = await res.json();
  const expiry = data.events?.find(
    (e) => e.eventAction === "expiration",
  )?.eventDate;
  return { domain, registered: true, expiry, status: data.status ?? [] };
}

const table = await getBootstrap();
for (const domain of process.argv.slice(2)) {
  console.log(await lookup(domain, table));
}
node rdap.mjs example.com example.net

Being a Good RDAP Citizen

  • Cache the bootstrap file. It changes rarely, so download it once a day rather than for every query.
  • Respect rate limits. Registries throttle heavy use and return 429. Add a delay between requests and back off when limited.
  • Don't scrape. RDAP terms of use generally prohibit bulk harvesting and using the data for marketing.
  • Handle missing fields. Not every server returns every field, so code defensively, as the scripts above do.

RDAP vs WHOIS at a Glance

WHOISRDAP
TransportPlain TCP, port 43HTTPS
FormatFree-form text, varies by serverStandardised JSON
International charactersNot definedFull Unicode support
Server discoveryNone, you must know the serverIANA bootstrap files
Access controlNoneSupports authenticated, tiered access
ErrorsAd hoc textHTTP status codes
Status for gTLDsRequirement ended 28 January 2025Required
Status for ccTLDsStill widely usedAdoption varies by registry

FAQ: RDAP

For gTLDs, RDAP is now the required lookup service, and ICANN ended the WHOIS requirement on 28 January 2025. Many registries still run WHOIS voluntarily, and many ccTLDs still rely on it, so both exist side by side for now.

Not for the public. RDAP returns largely the same public data, with personal details redacted. Its advantages are structure, consistency, security and the ability to give authorised users different access.

Yes. Registry and registrar RDAP services are free to query, subject to terms of use and rate limits. Services such as rdap.org and ICANN Lookup are also free.

Download IANA's bootstrap file at data.iana.org/rdap/dns.json, which maps each TLD to its RDAP base URL. Alternatively, use rdap.org, which redirects your query to the right server.

A 404 means the server has no record of that object. For a domain, that usually means it isn't registered, although some names are reserved or blocked by the registry and still can't be registered.

Yes, as a first check. A 404 suggests the name is unregistered and a 200 means it's registered. Confirm with a registrar before buying, because reserved and premium names may behave differently.


Conclusion

RDAP is the modern successor to WHOIS. It answers the same questions, who the registrar is, when a domain expires, which status codes and name servers it has, but does so over HTTPS with standard JSON, proper internationalisation, built-in server discovery and support for differentiated access. Since 28 January 2025 it has been the authoritative lookup service for gTLDs.

For everyday use, nothing much changes: ICANN Lookup and most WHOIS websites already use RDAP behind the scenes. For anyone writing scripts or monitoring domains, it's a big improvement. A single curl and jq command replaces fragile text parsing, and a few lines of Python or JavaScript give you reliable expiry, status and registrar data for every domain you manage.

Tags :
Share :

Related Posts

.com vs .net vs .org: which domain extension should you choose?

.com vs .net vs .org: which domain extension should you choose?

For most businesses, .com is the best choice: it's the extension people type by default, the one they trust most, and the one email users assume

Dive Deeper
Do new domain extensions like .io, .dev, and .ai affect SEO?

Do new domain extensions like .io, .dev, and .ai affect SEO?

No, new domain extensions like .io, .dev and .ai don't directly help or hurt your Google rankings. Google has said it treats new generic TLDs t

Dive Deeper
What is the difference between a domain name and web hosting?

What is the difference between a domain name and web hosting?

A domain name is the address people type to reach your website, such as example.com, while web hosting is the server space where your website's fil

Dive Deeper