RDAP vs WHOIS for abuse contacts: what changed
For thirty years the way to find who was behind a domain or an IP address was to open a TCP connection to port 43 and read whatever text came back. Every registry formatted it differently, most changed the format without notice, and anyone who automated it maintained a pile of regular expressions that broke monthly. RDAP replaced that with JSON over HTTPS. For generic top-level domains the replacement is complete: ICANN required registries and registrars to run RDAP from August 2019, and the contractual requirement to run WHOIS ended on 28 January 2025. Tools that still print port 43 output for .com are showing you a cache.
This guide is about using RDAP for the one thing abuse teams need from it: the abuse contact.
The protocol in one paragraph
RDAP is defined in RFC 7480 to 7484, updated by RFC 9082 (queries) and RFC 9083 (responses). A query is an HTTPS GET to <base>/domain/<name>, <base>/ip/<address> or <base>/autnum/<asn>. The response is a JSON object with an objectClassName, some identifying fields, an events array (registration, last changed, expiration), a status array, links, remarks, and an entities array describing the parties involved. Contacts inside entities are expressed as jCard (RFC 7095), which is vCard rendered as nested JSON arrays. Servers may answer with HTTP redirects to a more authoritative server, and clients are expected to follow them.
Finding the right server
There is no single RDAP server. IANA publishes bootstrap files (RFC 9224) that map top-level domains, IP ranges and AS number ranges to base URLs:
https://data.iana.org/rdap/dns.jsonhttps://data.iana.org/rdap/ipv4.jsonhttps://data.iana.org/rdap/ipv6.jsonhttps://data.iana.org/rdap/asn.json
Fetch these once a day, match the longest prefix, and you have the base URL. If you would rather not, the community redirector at rdap.org does the matching and answers with a redirect:
curl -sIL https://rdap.org/ip/193.0.6.139 | grep -i '^location'
Reading a domain response
curl -s https://rdap.org/domain/example.org | python3 -m json.tool
The parts that matter:
entities[]with"roles": ["registrar"]: the sponsoring registrar.publicIdscarries its IANA Registrar ID, a stable number that survives the registrar renaming itself.- Inside that registrar entity,
entities[]with"roles": ["abuse"]: the abuse contact. In itsvcardArray, theemailandtelproperties are the address and phone the registrar is obliged to publish. events[]:registrationtells you the domain's age, which is the single most useful fact for judging whether the domain is malicious or compromised.status[]: values such asclient holdorserver holdmean the domain is already suspended and not resolving.nameservers[]: where the domain's DNS is delegated.
For .com and .net the registry (Verisign) holds only registrar and nameserver data. The abuse contact may be present, or you may need to follow the links entry with "rel": "related" to the registrar's own RDAP server and read the same structure there. Other gTLD registries return the full record directly.
Registrant, administrative and technical contacts are usually redacted under GDPR and equivalent laws, and for abuse routing that does not matter. The registrar's abuse mailbox is the one that acts.
Reading an IP response
curl -sL https://rdap.org/ip/203.0.113.10 | python3 -m json.tool
The object is an ip network: startAddress, endAddress, name (the netname), country, type (allocation or assignment), and entities. The entity with "roles": ["abuse"] is the network's abuse contact, again in jCard form. Where a registry uses the cidr0 extension you will also see cidr0_cidrs with the prefix and length, convenient for logging.
Each RIR adds its own flavour:
- ARIN returns the abuse point of contact as an entity with a handle ending in
-ARIN. ARIN also refuses more than one simultaneous new TLS connection from a single source, so clients that open connections in parallel see connection resets rather than HTTP 429s. Query sequentially over one persistent connection. - RIPE NCC models the contact through
abuse-c, a role object that is mandatory on every allocation. Person and role objects that contain personal data are rate-limited per source address; the abuse role is exempt, but a full/ipresponse embeds administrative and technical persons that are not. If you only want the abuse mailbox, RIPE's REST search withflags=no-referenced&abuse-contact=truereturns it without the personal data and without touching the limit. - APNIC uses an IRT (incident response team) object whose
abuse-mailboxis the address to use, and treats it as commercial rather than personal data. - LACNIC delegates national address space, so
rdap.lacnic.netanswers with a 307 redirect tordap.registro.brfor Brazilian addresses and to NIC Mexico for Mexican ones. Follow the redirect. registro.br also sends thecidr0prefix length as a string rather than a number, which will break a strictly typed parser. - AFRINIC follows the RIPE model closely.
Quirks that break naive parsers
- jCard. A contact is
["vcard", [["version", {}, "text", "4.0"], ["fn", {}, "text", "Abuse Desk"], ["email", {}, "text", "abuse@example.net"]]]. Property values are the fourth element of each inner array, and structured values (addresses) are arrays themselves. - Multiple emails. An abuse entity can carry more than one
emailproperty, sometimes with aprefparameter. Take them all. - Missing roles. Some registrars return the abuse contact as a top-level entity rather than nested under the registrar. Check both places.
- Type inconsistencies. Numbers as strings, as noted for registro.br; booleans as strings elsewhere. Parse leniently.
- Redirects with a different path shape. After a 307, the new URL may use a different path convention. Follow the
Locationheader as given. - 404 for unregistered domains. RDAP returns 404 when a domain does not exist. Distinguish that from a registry that exists but returns no useful data.
Country-code domains
ccTLDs were never bound by ICANN's RDAP mandate. Many have deployed it anyway, but in their own time and on their own terms. Some publish full records, some publish registrar name only, some publish nothing useful about the registrar, and one or two attach terms of use that restrict automated or commercial use of the data. Where a ccTLD has no RDAP, port 43 WHOIS is still the source and still needs a per-registry parser. Where it has neither, the hosting network contact is still available through the RIRs, and that is the contact that removes content anyway.
Being a good client
Registries run RDAP for free and can withdraw it from anyone who abuses it. Three habits keep you welcome:
- Identify yourself. A User-Agent naming your organisation and a URL. Anonymous bulk traffic is what registries block first.
- Cache. Network allocations change rarely; a day is a safe cache for
/ipresponses. Registrar data changes more often but an hour is fine. Never persist and re-serve the data as a database; that crosses from use into compilation, which every registry's terms prohibit. - Go slowly. One request at a time per registry over a kept-alive connection is faster in practice than parallel connections that get refused, and it is what the registries ask for.
Where a registry's terms restrict commercial use, as ARIN's and AFRINIC's do, ask them in writing before relying on the data in a paid service. In our experience they answer, and quickly, when the use is the one the abuse contact was published for.
AbuseRoute is a private API that returns the registrar and hosting abuse contacts for a domain in one call, for security and abuse teams. How it works. For individual manual lookups, use the RrSG's ACID Tool.