Bulk Domain Expiry & WHOIS Lookup in Python with RDAP (and the .io Trap)
WHOIS is on its way out. The registries now publish the same data through RDAP, a JSON API that's free, needs no key, and returns registrar, creation and expiry dates, and status in a consistent format. If you track client domains, buy expiring names, or audit a portfolio, a few lines of Python replace a paid WHOIS API. The script rdap.org is a public bootstrap service: request rdap.org/domain/ and it redirects you to the right registry. Standard library only: """Bulk domain lookup with RDAP (the official successor to WHOIS): registrar, created, expires, status. Usage: python3 domain_expiry.py apify.com python.org some-free-name-123.com""" import json import socket import sys import urllib.error import urllib.request from concurrent.futures import ThreadPoolExecutor def rdap(domain): url = f"https://rdap.org/domain/{domain}" # redirects to the right registry's RDAP server try: with urllib.request.urlopen(urllib.request.Request(url, headers={"Accept": "application/rdap+json", "User-Agent": "domain-expiry-script"}), timeout=20) as r: data = json.load(r) except urllib.error.HTTPError as e: if e.code != 404: return {"domain": domain, "status": f"HTTP {e.code}"} # 404 means "no RDAP record", which is NOT proof it's free: some registries (.io, .co…) have no RDAP at all try: socket.getaddrinfo(domain, None) return {"domain": domain, "status": "registered (no RDAP)"} except socket.gaierror: return {"domain": domain, "status": "maybe free"} events = {e["eventAction"]: e["eventDate"][:10] for e in data.get("events", [])} registrar = next((v[3] for ent in data.get("entities", []) if "registrar" in ent.get("roles", []) for v in ent.get("vcardArray", [None, []])[1] if v[0] == "fn"), "") return {"domain": domain, "status": "registered", "registrar": registrar, "created": events.get("registration", ""), "expires": events.get("expiration", "")} with ThreadPoolExecutor(5) as pool: for row in pool.map(rdap, sys.argv[1:]): print(f"{row['domain']:28} {row['status']:20} {row.get('expires', ''):11} {row.get('registrar', '')}") Real run (October 2026): $ python3 domain_expiry.py apify.com python.org openalex.org github.io zzqxv-free-name-12345.com apify.com registered 2035-06-02 Amazon Registrar, Inc. python.org registered 2033-03-28 Gandi SAS openalex.org registered 2028-03-14 GoDaddy.com, LLC github.io registered (no RDAP) zzqxv-free-name-12345.com maybe free Three traps I hit writing this The .io trap. My first version printed github.io → available?, which is obviously wrong. Some registries (.io, .co, and others) don't run RDAP at all, so a 404 means "no RDAP record", not "free". The fix: on 404, check DNS. If the name resolves, it's registered. 403s from .org. The .org registry rejected requests with no User-Agent. Always send one. "Maybe free" really means maybe. No RDAP record and no DNS still isn't proof: names can be reserved, premium, or registered with no DNS. Confirm at a registrar before you get excited. Also: RDAP servers rate-limit. Keep concurrency low (5 here) for big lists. Thousands of domains, scheduled For portfolio checks or weekly expiry monitoring, I put a fuller version on Apify: WHOIS Domain Lookup. It covers RDAP across 1,200+ endings, DNS records (A, MX, NS, TXT), deduplication, and graceful handling of registries without RDAP. It costs $2 per 1,000 domains, and endings without RDAP are DNS-only and free. It never returns registrant contact details. Either way, the script above is yours. More free scripts and tools from Kramer Labs.
This is a summary aggregated from Dev.to. Read the complete article on the original site:
Read full article at Dev.to