Unified RDAP client for Python, powered by Rust
Project description
rdapify
A fast, secure, production-ready RDAP client library for Rust.
RDAP (Registration Data Access Protocol) is the modern replacement for WHOIS, defined in RFC 9083 and RFC 9224.
rdapify ecosystem
Library Language Package rdapify-rs ← you are here Rust rdapifyon crates.ioRDAPify TypeScript / Node.js rdapifyon npmrdapify-nd Node.js (Rust native) rdapify-ndon npmrdapify-py Python (Rust native) rdapify-pyon PyPI
Features
- 5 query types — domain, IP, ASN, nameserver, entity
- IANA Bootstrap (RFC 9224) — automatic server discovery, no manual configuration needed
- SSRF protection — blocks requests to private, loopback, and link-local addresses
- In-memory cache — configurable TTL and capacity, lock-free via
DashMap - IDN support — accepts Unicode domain names, normalises to Punycode automatically
- Retry with back-off — exponential back-off on network errors and 5xx/429 responses
- Zero OpenSSL — uses
rustls(pure Rust TLS) - Async-first — built on
tokio
Installation
[dependencies]
rdapify = "0.2"
Quick Start
use rdapify::RdapClient;
#[tokio::main]
async fn main() -> rdapify::Result<()> {
let client = RdapClient::new();
// Query a domain
let domain = client.domain("example.com").await?;
println!("Registrar: {:?}", domain.registrar);
println!("Expires: {:?}", domain.expiration_date());
// Query an IP address
let ip = client.ip("8.8.8.8").await?;
println!("Network: {:?}", ip.name);
println!("Country: {:?}", ip.country);
// Query an ASN
let asn = client.asn("AS15169").await?;
println!("ASN name: {:?}", asn.name);
Ok(())
}
Usage
Domain Query
let res = client.domain("rust-lang.org").await?;
println!("{}", res.ldh_name.as_deref().unwrap_or("-"));
println!("{:?}", res.status);
println!("{:?}", res.expiration_date());
if let Some(r) = &res.registrar {
println!("Registrar: {}", r.name.as_deref().unwrap_or("-"));
}
IP Address Query
// IPv4
let res = client.ip("1.1.1.1").await?;
// IPv6
let res = client.ip("2606:4700::1111").await?;
println!("CIDR: {:?}", res.cidr);
println!("Country: {:?}", res.country);
ASN Query
// Both formats accepted
let res = client.asn("15169").await?;
let res = client.asn("AS15169").await?;
println!("Name: {:?}", res.name);
Nameserver Query
let res = client.nameserver("ns1.example.com").await?;
println!("IPs: {:?}", res.ip_addresses);
Entity Query
let res = client.entity("ARIN-CHA-1", "https://rdap.arin.net/registry").await?;
println!("Name: {:?}", res.name);
println!("Roles: {:?}", res.roles);
Configuration
use rdapify::{RdapClient, ClientConfig, FetcherConfig, SsrfConfig};
use std::time::Duration;
let client = RdapClient::with_config(ClientConfig {
cache: true,
fetcher: FetcherConfig {
timeout: Duration::from_secs(10),
max_attempts: 3,
..Default::default()
},
ssrf: SsrfConfig {
enabled: true,
..Default::default()
},
..Default::default()
})?;
CLI
Enable the cli feature to build the rdapify binary:
rdapify = { version = "0.1", features = ["cli"] }
Or install it directly:
cargo install rdapify --features cli
rdapify domain example.com
rdapify ip 8.8.8.8
rdapify asn AS15169
rdapify nameserver ns1.example.com
rdapify entity ARIN-CHA-1 https://rdap.arin.net/registry
# Machine-readable JSON output
rdapify domain example.com --raw
Performance
All figures are measured with cargo bench (Criterion) on a Linux x86-64 machine.
The query benchmarks use a local mock HTTP server (mockito) so results reflect
pure Rust overhead — no real network latency is included.
Cache
| Benchmark | Time |
|---|---|
| Cache hit (DashMap read, fresh TTL) | ~124 ns |
| Cache miss (key absent) | ~24 ns |
| Cache insert (single write) | ~780 ns |
| Cache eviction (insert at max capacity) | ~8.8 µs |
| Bulk insert — 100 entries | ~35 µs |
| Bulk insert — 1 000 entries | ~444 µs |
Query pipeline (mock HTTP server)
| Benchmark | Time | Notes |
|---|---|---|
domain() — no cache |
~183 µs | bootstrap lookup + HTTP fetch + normalise |
domain() — cache hit |
~2.3 µs | ~80× faster than uncached |
ip() — no cache |
~176 µs | |
asn() — no cache |
~176 µs |
Cache brings query latency from ~180 µs → ~2 µs — an 80× speedup for repeated queries within the TTL window.
SSRF validation
| Benchmark | Time |
|---|---|
| Public domain URL (allowed) | ~141 ns |
| Public IPv4 URL (allowed) | ~220 ns |
| Private IPv4 URL (blocked at RFC-1918) | ~295 ns |
| Non-HTTPS scheme (blocked immediately) | ~145 ns |
| SSRF disabled (boolean bypass) | ~3 ns |
Run the benchmarks yourself:
cargo bench
# HTML reports → target/criterion/report/index.html
Language Bindings
Node.js — rdapify-nd
A prebuilt native binding for Node.js. No compiler required — binaries ship for Linux x64, macOS x64/arm64, and Windows x64.
npm install rdapify-nd
const { domain, ip, asn, nameserver, entity } = require('rdapify-nd');
// Domain
const d = await domain('example.com');
console.log(d.registrar?.name); // "Example Registrar, Inc."
console.log(d.ldhName); // "example.com"
console.log(d.metadata.timestamp); // "2026-03-21T00:00:00Z"
// IP address
const i = await ip('8.8.8.8');
console.log(i.name); // "GOOGLE"
console.log(i.country); // "US"
// ASN
const a = await asn('AS15169');
console.log(a.name); // "GOOGLE"
// Nameserver
const ns = await nameserver('ns1.google.com');
console.log(ns.ipAddresses.v4); // ["216.239.32.10"]
// Entity (requires explicit server URL — no global bootstrap for entities)
const e = await entity('ARIN-HN-1', 'https://rdap.arin.net/registry');
console.log(e.handle); // "ARIN-HN-1"
Use with the TypeScript rdapify library for automatic native acceleration:
npm install rdapify rdapify-nd
import { RDAPClient } from 'rdapify';
// backend: 'auto' (default) uses rdapify-nd if installed, falls back to TypeScript
const client = new RDAPClient({ backend: 'auto' });
// Or require it — throws at startup if rdapify-nd is not installed
const client2 = new RDAPClient({ backend: 'native' });
const result = await client.domain('example.com');
console.log(result.metadata.source); // RDAP server URL that served the response
Python — rdapify-py
A prebuilt native extension for Python 3.8+. Ships as abi3 wheels for
Linux x64, macOS x64/arm64, and Windows x64.
pip install rdapify-py
import rdapify_py as rdap
# Domain
d = rdap.domain("example.com")
print(d["registrar"]["name"]) # "Example Registrar, Inc."
print(d["ldhName"]) # "example.com"
print(d["meta"]["queried_at"]) # RFC 3339 timestamp
# IP address
i = rdap.ip("8.8.8.8")
print(i["name"]) # "GOOGLE"
print(i["country"]) # "US"
# ASN
a = rdap.asn("AS15169")
print(a["name"]) # "GOOGLE"
# Nameserver
ns = rdap.nameserver("ns1.google.com")
print(ns["ipAddresses"]["v4"]) # ["216.239.32.10"]
# Entity (requires explicit server URL)
e = rdap.entity("ARIN-HN-1", "https://rdap.arin.net/registry")
print(e["handle"]) # "ARIN-HN-1"
All five functions are synchronous and backed by a tokio runtime under the hood.
MSRV
Minimum supported Rust version: 1.75
License
MIT — see LICENSE
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rdapify_py-0.2.1.tar.gz.
File metadata
- Download URL: rdapify_py-0.2.1.tar.gz
- Upload date:
- Size: 80.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c73ff9bf54ba46c9153e2f621efaa8a76c4fcc54869273a390d73e9cd51c48fd
|
|
| MD5 |
b7728357b0435203182b6b849578714d
|
|
| BLAKE2b-256 |
18c346e819977c3a0ef49682998da94dabd896bf5789dc8d9ea5bc5f8e13b7aa
|
File details
Details for the file rdapify_py-0.2.1-cp38-abi3-manylinux_2_34_x86_64.whl.
File metadata
- Download URL: rdapify_py-0.2.1-cp38-abi3-manylinux_2_34_x86_64.whl
- Upload date:
- Size: 2.2 MB
- Tags: CPython 3.8+, manylinux: glibc 2.34+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b2dad5fe7c07424bd0336efe52307ca15a8143b55b38a74ba85a293842e34e91
|
|
| MD5 |
54c5e2667a453d6eb55b39886fb68990
|
|
| BLAKE2b-256 |
c645aa06aacc0407e48ea74024d1dbeb7159299f56dc01c716d34c024e8c42b3
|