{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreicfm7v6v6qewb3ladd733flrr4zqtwos4e4tg2mlzythqhaoyniya",
    "uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mpf6awi6p4l2"
  },
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreigzgeanum4m4s35typyvr4c3mhv3zushnck3xhx2cy3bandwteqfy"
    },
    "mimeType": "image/webp",
    "size": 72344
  },
  "path": "/nexgendata/whois-is-broken-in-2026-heres-the-rdap-first-drop-in-that-actually-returns-json-55d9",
  "publishedAt": "2026-06-28T23:04:49.000Z",
  "site": "https://dev.to",
  "tags": [
    "api",
    "automation",
    "webscraping",
    "opensource",
    "whois-replacement",
    "actor page",
    "company-data-aggregator",
    "tranco-rank-lookup"
  ],
  "textContent": "WHOIS has been quietly dying for a decade, and most teams only noticed in the last eighteen months.\n\nIf you ran a domain-intelligence pipeline between 2015 and 2022, the story went like this. You shelled out to the `whois` binary, or hit a free public wrapper, or paid WhoisXML API $0.00099 per lookup. You wrote a parser full of regex special cases for Verisign vs. Afilias vs. Nominet vs. DENIC, caught the edge cases where `.jp` returned Shift-JIS, normalized the date formats, and shipped. It worked, barely.\n\nThen two things happened at once. ICANN's RDAP mandate became compliance-enforced in August 2024, which broke the WHOIS TCP/43 endpoints for every gTLD registrar still pointing at them. And WhoisXML, along with most of its competitors, gated their free tiers into oblivion through 2024 and 2025 — first dropping the public 1,000-request-per-month plan, then requiring credit cards for evaluation, then pushing minimums into the low-four-figure range for any serious volume.\n\nIf your pipeline still calls `whois example.com` and greps the output, it is failing silently on somewhere between 30% and 60% of lookups right now. You haven't been paged because the failures are partial, the formats look superficially correct, and downstream consumers treat a missing `expiration_date` as \"probably still valid.\"\n\nThis post is about what happened, why RDAP is a genuine improvement, and how we built whois-replacement — an Apify actor that speaks RDAP first, falls back to legacy WHOIS for TLDs that haven't migrated, and returns a unified JSON schema across any TLD.\n\n_Protocol compliance data cited here reflects ICANN's public RDAP deployment tracker as of Q1 2026._\n\n##  WHOIS was broken before it was broken\n\nThe WHOIS protocol is older than HTTPS and only barely younger than DNS. RFC 812 shipped in 1982. RFC 3912, the current spec, was published in 2004 and is roughly three pages long. It specifies a plain-text TCP connection on port 43, the client sends a query terminated with CRLF, the server replies with free-form text and closes the connection. That's the entire protocol.\n\nThree pages is not a lot of spec. There is no response schema, no field encoding requirement, no authentication, no structured error model, no rate-limit signaling. Every registrar implemented the output format however they felt like on whatever Tuesday they first deployed, and the formats ossified.\n\nHere is a real-world `whois google.com` output, lightly redacted:\n\n\n\n           Domain Name: GOOGLE.COM\n           Registry Domain ID: 2138514_DOMAIN_COM-VRSN\n           Registrar WHOIS Server: whois.markmonitor.com\n           Registrar URL: http://www.markmonitor.com\n           Updated Date: 2019-09-09T15:39:04Z\n           Creation Date: 1997-09-15T04:00:00Z\n           Registry Expiry Date: 2028-09-14T04:00:00Z\n\n\n\nHere is `whois amazon.co.uk`:\n\n\n\n            Domain name:\n                amazon.co.uk\n\n            Registrant:\n                Amazon Europe Core S.a.r.l.\n\n            Registrar:\n                MarkMonitor Inc. t/a MarkMonitor [Tag = MARKMONITOR]\n\n\n\nHere is `whois sony.jp` (before encoding normalization):\n\n\n\n        [ JPRS database provides information on network administration. ]\n        a. [Domain Name]                SONY.JP\n        g. [Organization]               ソニーグループ株式会社\n        k. [Organization Type]          Corporation\n\n\n\nThese are not cosmetic differences. Labels differ, structure differs, dates are in three incompatible formats, one is in Japanese. Anyone who wrote a \"universal\" WHOIS parser wrote a 2,000-line regex library with a test matrix covering maybe 40 TLDs out of the 1,500 that exist. This is the baseline dysfunction every domain-intelligence team inherited.\n\n##  RDAP: what ICANN actually mandated\n\nRDAP — Registration Data Access Protocol — is the replacement. It was standardized in RFC 7480-7484 in 2015, developed jointly by the IETF and ICANN, and has been displacing WHOIS ever since.\n\nThe protocol is dramatically saner. It's HTTPS-based. It returns JSON with a specified schema. Queries follow a REST path convention (`https://rdap.verisign.com/com/v1/domain/example.com`). Responses include standardized `events`, `entities`, `status`, and `links` arrays. A bootstrap registry at `data.iana.org/rdap/dns.json` tells you which RDAP server is authoritative for any given TLD. Errors are proper HTTP status codes with structured bodies.\n\nThe rollout timeline, for the record:\n\n  * **March 2015** : RFC 7480-7484 published, bootstrap registry launched.\n  * **August 2019** : ICANN requires all gTLD registries to implement RDAP.\n  * **November 2023** : Mandatory registrar-side RDAP support comes into force.\n  * **August 2024** : Compliance deadline for full RDAP deprecation of WHOIS for gTLDs. ICANN begins issuing compliance notices to non-compliant registrars.\n  * **Q4 2024 through 2025** : Major registrars start returning 451 Unavailable on port 43 or redirecting to RDAP-only endpoints.\n  * **2026** : Most gTLD traffic is RDAP-native. ccTLDs remain a patchwork — some are fully RDAP (`.de`, `.uk`, `.nl`), some are partial (`.fr`, `.jp`), some haven't started (`.ru`, `.cn`, `.tk`).\n\n\n\nIf your code still assumes port 43 TCP, you are assuming a protocol ICANN has actively deprecated.\n\n##  The WhoisXML squeeze and the market shift to paywalls\n\nParallel to the RDAP migration, the commercial WHOIS API market has spent 2024 and 2025 aggressively monetizing. This is the broader \"free APIs are over\" trend that hit weather, maps, and LLM inference. It landed hard in WHOIS.\n\nThe old WhoisXML free tier allowed 1,000 lookups per month without a credit card. That tier was restructured in early 2024 to a 100-lookup trial, then to credit-card-required evaluation, then in late 2025 to a \"contact sales\" gate for anything resembling production use. Their cheapest published commercial tier is now in the mid-three-figures per month. Competitors — DomainTools, IP2WHOIS, ViewDNS — moved the same direction.\n\nThere are legitimate reasons. Running a WHOIS aggregator means maintaining scrapers against 1,500+ registrar formats, caching under GDPR-compliant retention, and eating abuse from 10M-request burst traffic. The free tier was a loss leader that got harder to justify as the underlying data got harder to scrape. Teams who used to get by with a free hobby account or a homegrown `whois` scraper are suddenly staring at a $400/month bill or a broken pipeline.\n\n##  What a unified replacement should look like\n\nAny serious replacement needs to do five things:\n\n  1. **Speak RDAP natively** , using the bootstrap registry to route queries.\n  2. **Fall back to WHOIS gracefully** for TLDs that still don't have RDAP (`.tk`, `.ml`, `.ga`, `.cn`, parts of `.ru`, a long tail of ccTLDs).\n  3. **Return one schema** , regardless of which protocol was used upstream.\n  4. **Handle GDPR redaction sanely.** Since May 2018, registrant data for most gTLDs has been redacted under ICANN's Registration Data Consensus Policy. A modern parser needs to surface \"this field was redacted\" as a structured signal, not just return null.\n  5. **Be priced like a commodity.** Domain lookups aren't a differentiated product.\n\n\n\nThe whois-replacement actor (ID `U7mdAONVS7k478lDQ`) does all five. PPE is $0.005 per lookup. It uses RDAP as the primary protocol, falls back to WHOIS only when the TLD has no RDAP endpoint, and returns the same JSON shape across `.com`, `.de`, `.jp`, `.io`, and the long tail.\n\n##  The unified response schema\n\nEvery lookup returns the same top-level keys:\n\n\n\n        {\n          \"domain\": \"example.com\",\n          \"tld\": \"com\",\n          \"protocol_used\": \"rdap\",\n          \"registrar\": {\n            \"name\": \"RESERVED-Internet Assigned Numbers Authority\",\n            \"iana_id\": 376,\n            \"url\": \"https://www.iana.org\"\n          },\n          \"status\": [\"client transfer prohibited\"],\n          \"nameservers\": [\"A.IANA-SERVERS.NET\", \"B.IANA-SERVERS.NET\"],\n          \"dnssec\": \"signedDelegation\",\n          \"events\": {\n            \"registered\": \"1995-08-14T04:00:00Z\",\n            \"updated\": \"2024-08-14T07:01:34Z\",\n            \"expires\": \"2025-08-13T04:00:00Z\"\n          },\n          \"registrant\": {\n            \"redacted\": true,\n            \"redaction_reason\": \"gdpr\",\n            \"organization\": null,\n            \"country\": \"US\",\n            \"email\": null\n          },\n          \"abuse_contact\": {\n            \"email\": \"abuse@iana.org\",\n            \"phone\": \"+1.3105281212\"\n          },\n          \"raw_rdap\": { \"...\": \"passthrough\" },\n          \"raw_whois\": null,\n          \"fetched_at\": \"2026-04-17T12:00:00Z\"\n        }\n\n\n\n`protocol_used` is `\"rdap\"`, `\"whois\"`, or `\"hybrid\"` (the latter for ccTLDs where RDAP returns partial data and WHOIS fills the gaps). `raw_rdap` and `raw_whois` are passthroughs for callers who need the original payload for audit or custom parsing.\n\nThe schema is stable across TLDs. A `.jp` lookup, even when it falls back to WHOIS and parses the Japanese-encoded output, returns the same keys with normalized UTF-8 values and ISO 8601 dates.\n\n##  Old vs. new: the comparison table\n\n| Feature | Legacy `whois` binary | whois-replacement | WhoisXML API Pro | IP2WHOIS | Namecheap API | |---|---|---|---|---|---| | RDAP native | no | **yes** | yes (2024+) | partial | no | | WHOIS fallback for non-RDAP TLDs | yes (raw) | **yes (parsed)** | yes | yes | no | | Unified JSON schema across TLDs | no | **yes** | yes | partial | no | | GDPR redaction signaled as field | no | **yes** | yes | no | no | | ccTLD coverage | ~95% (raw text) | ~98% | ~95% | ~80% | ~20% | | Japanese / Cyrillic / CJK encoding handling | manual | **automatic** | automatic | manual | n/a | | Free tier | yes (self-hosted) | $5/mo Apify credit | gone as of 2025 | 500/day | reseller-only | | Per-lookup price at 50k volume | ~$0 + infra | **$0.005** | $0.008 | $0.002 | reseller bundled | | Rate-limit handling | client problem | **actor-managed** | managed | managed | managed | | Raw payload passthrough | n/a | yes | yes (paid tier) | no | no | | Ships a CLI | yes | no (use curl) | no | no | no |\n\nIP2WHOIS is cheaper per lookup at bulk volumes but their schema isn't stable across TLDs and they don't signal GDPR redaction. WhoisXML's $0.008 is their published pro-tier rate. The whois-replacement actor's $0.005 covers the full unified-schema + RDAP-first + raw-passthrough bundle.\n\n##  Migration: the two-line change\n\nIf your code currently looks like this:\n\n\n\n        import subprocess\n\n        output = subprocess.check_output([\"whois\", domain], text=True)\n        # 200 lines of regex ...\n\n\n\nOr like this:\n\n\n\n        import requests\n\n        resp = requests.get(\n            f\"https://www.whoisxmlapi.com/whoisserver/WhoisService?apiKey={KEY}\"\n            f\"&domainName;={domain}&outputFormat;=JSON\"\n        )\n        data = resp.json()[\"WhoisRecord\"]\n\n\n\nThe migration target is:\n\n\n\n        from apify_client import ApifyClient\n\n        client = ApifyClient(\"APIFY_TOKEN\")\n        run = client.actor(\"nexgendata/whois-replacement\").call(run_input={\n            \"domains\": [domain]\n        })\n        record = next(client.dataset(run[\"defaultDatasetId\"]).iterate_items())\n        print(record[\"events\"][\"expires\"])\n\n\n\nThe response shape is deterministic. No TLD-specific branches.\n\n##  Code examples\n\n###  Python: bulk expiry-risk scan for a security research team\n\nThis is the canonical use case. A security research team wants to identify the expiry-risk profile of 50,000 domains observed in a phishing campaign — which ones are close to expiration (and therefore likely to drop and get caught by a defensive registration program), which ones are freshly registered (strong phishing signal), which ones are parked behind privacy services.\n\n\n\n        from apify_client import ApifyClient\n        from datetime import datetime, timezone, timedelta\n\n        client = ApifyClient(\"APIFY_TOKEN\")\n\n        with open(\"campaign_domains.txt\") as f:\n            domains = [line.strip() for line in f if line.strip()]\n\n        run = client.actor(\"nexgendata/whois-replacement\").call(run_input={\n            \"domains\": domains,\n            \"include_raw\": False,\n            \"concurrency\": 20,\n        })\n\n        now = datetime.now(timezone.utc)\n        expiry_risk = []\n        fresh_registrations = []\n\n        for item in client.dataset(run[\"defaultDatasetId\"]).iterate_items():\n            events = item.get(\"events\") or {}\n            expires = events.get(\"expires\")\n            registered = events.get(\"registered\")\n\n            if expires:\n                exp_dt = datetime.fromisoformat(expires.replace(\"Z\", \"+00:00\"))\n                if exp_dt - now < timedelta(days=30):\n                    expiry_risk.append((item[\"domain\"], expires))\n\n            if registered:\n                reg_dt = datetime.fromisoformat(registered.replace(\"Z\", \"+00:00\"))\n                if now - reg_dt < timedelta(days=7):\n                    fresh_registrations.append((item[\"domain\"], registered))\n\n        print(f\"{len(expiry_risk)} domains expiring within 30 days\")\n        print(f\"{len(fresh_registrations)} domains registered within last 7 days\")\n\n\n\nAt 50,000 domains and $0.005 per lookup, this run costs $250. With `concurrency: 20` it completes in roughly 15 minutes. The unified schema means there's no TLD branching in the consumer code — `.com`, `.io`, `.ru`, `.jp`, and `.co.uk` domains all parse the same way.\n\n###  curl: single-domain lookup for a shell pipeline\n\n\n        curl -X POST \"https://api.apify.com/v2/acts/nexgendata~whois-replacement/run-sync-get-dataset-items?token=$APIFY_TOKEN\" \\\n          -H \"Content-Type: application/json\" \\\n          -d '{\n            \"domains\": [\"anthropic.com\"]\n          }' | jq '.[0] | {domain, registrar: .registrar.name, expires: .events.expires}'\n\n\n\nReturns a clean JSON shape suitable for piping into further shell tools. Useful for ad-hoc \"is this domain legit\" investigations.\n\n###  Node.js: webhook-driven fresh-registration monitor\n\nStand up a webhook that pulls a batch of newly registered domains from your CTI feed, looks them up, and flags the ones that match a phishing-lookalike pattern:\n\n\n\n        const { ApifyClient } = require('apify-client');\n        const express = require('express');\n\n        const apify = new ApifyClient({ token: process.env.APIFY_TOKEN });\n        const app = express();\n        app.use(express.json());\n\n        const TARGET_BRANDS = ['stripe', 'coinbase', 'binance', 'paypal'];\n\n        function looksLikePhish(domain) {\n          const lower = domain.toLowerCase();\n          return TARGET_BRANDS.some(brand =>\n            lower.includes(brand) && lower !== `${brand}.com`\n          );\n        }\n\n        app.post('/newly-registered', async (req, res) => {\n          const candidates = req.body.domains.filter(looksLikePhish);\n          if (candidates.length === 0) return res.json({ flagged: 0 });\n\n          const run = await apify.actor('nexgendata/whois-replacement').call({\n            domains: candidates,\n          });\n          const { items } = await apify.dataset(run.defaultDatasetId).listItems();\n\n          const flagged = items.filter(item => {\n            const reg = item.events?.registered;\n            if (!reg) return false;\n            const ageHours = (Date.now() - new Date(reg).getTime()) / 3.6e6;\n            return ageHours < 48;\n          });\n\n          res.json({ flagged: flagged.length, domains: flagged });\n        });\n\n        app.listen(3000);\n\n\n\nThe 48-hour freshness window catches most bulk-registration phishing patterns. Because the actor returns normalized ISO 8601 timestamps regardless of whether the source was RDAP JSON or a parsed `.ru` WHOIS blob, the age math works uniformly.\n\n###  Python: RDAP-specific raw payload inspection\n\nFor teams that need the raw RDAP response — CT log correlation, DNSSEC chain verification against `secureDNS` blocks, evidentiary audit trails — pass `include_raw: true`:\n\n\n\n        run = client.actor(\"nexgendata/whois-replacement\").call(run_input={\n            \"domains\": [\"cloudflare.com\"],\n            \"include_raw\": True,\n        })\n        record = next(client.dataset(run[\"defaultDatasetId\"]).iterate_items())\n\n        raw_rdap = record[\"raw_rdap\"]\n        for entity in raw_rdap.get(\"entities\", []):\n            roles = entity.get(\"roles\", [])\n            if \"abuse\" in roles:\n                print(\"Abuse entity:\", entity.get(\"handle\"))\n\n        secure_dns = raw_rdap.get(\"secureDNS\")\n        if secure_dns and secure_dns.get(\"delegationSigned\"):\n            print(\"DNSSEC signed delegation confirmed\")\n\n\n\nThe raw payload adds ~5-20 KB per record, which matters at bulk scale. Leave it off unless you need it.\n\n###  curl: checking an RDAP-only TLD vs. a WHOIS-only TLD in one call\n\n\n        curl -X POST \"https://api.apify.com/v2/acts/nexgendata~whois-replacement/run-sync-get-dataset-items?token=$APIFY_TOKEN\" \\\n          -H \"Content-Type: application/json\" \\\n          -d '{\n            \"domains\": [\"example.com\", \"example.tk\", \"example.jp\"]\n          }' | jq '.[] | {domain, protocol_used, expires: .events.expires}'\n\n\n\nResponse will show `protocol_used: \"rdap\"` for `.com`, `protocol_used: \"whois\"` for `.tk` (Freenom has no RDAP), and `protocol_used: \"hybrid\"` for `.jp` where RDAP is partial. The schema is identical across all three.\n\n##  Worked example: 50,000-domain expiry audit for a CTI team\n\nA reader's security-research team runs a nightly monitoring pipeline against the previous 24h of phishing-observed domains (typically 30k-60k). Their old stack was a self-hosted WHOIS server pool (10 VMs running the `whois` binary through a job queue) plus a 3,000-line Python parser with TLD-specific handlers. It cost roughly $600/month in VM spend plus an estimated 20% engineering time from one SRE keeping the parser up to date. Their internal dashboard showed parser coverage dropping from 94% in early 2024 to 71% in late 2025 as more registrars transitioned to RDAP-only and the TCP/43 scrapes returned truncated or 451-coded responses.\n\nMigration took one engineer a day:\n\n\n\n        domains = load_observed_domains(last_24h=True)  # 30k-60k\n        chunks = [domains[i:i+2000] for i in range(0, len(domains), 2000)]\n\n        for chunk in chunks:\n            run = client.actor(\"nexgendata/whois-replacement\").call(run_input={\n                \"domains\": chunk,\n                \"concurrency\": 25,\n            })\n            upload_to_snowflake(client.dataset(run[\"defaultDatasetId\"]).iterate_items())\n\n\n\nAt 45k domains/day, the bill runs $225/day or roughly $6,750/month. That's more than the old $600 VM bill in raw dollars, but the team reclaimed the 20% SRE time (worth ~$3,000/month in salary terms), eliminated the degrading parser, and raised coverage from 71% back to 97%. Net: a wash on cash, a material win on engineering velocity. At volume, a commodity-priced API almost always beats self-hosted scrapers once you include engineering time.\n\n##  Schema-level gotchas\n\n  * **Date normalization.** All timestamps are ISO 8601 UTC. `.jp`'s JST-local, `.cn`'s Beijing-local, and `.ru`'s Moscow-local dates are converted. `raw_whois` preserves the original.\n  * **Status codes.** RDAP defines 17 EPP status values. The actor passes them through verbatim in `status`. WHOIS-sourced records map to the nearest equivalent; non-mappable statuses are kept with a `raw_` prefix.\n  * **Nameservers.** Always uppercase, FQDN-normalized (trailing dot stripped).\n  * **`dnssec` field.** Values are `signedDelegation`, `unsigned`, or `unknown`.\n  * **GDPR redaction.** `registrant.redacted` is true when the source indicates redaction via RFC 9537 (RDAP's redaction extension) or the \"REDACTED FOR PRIVACY\" placeholder in WHOIS. `redaction_reason` is `gdpr`, `privacy_service`, or `unknown`.\n  * **`.tk`, `.ml`, `.ga`, `.cf`, `.gq`.** Freenom-operated free TLDs have no RDAP, inconsistent WHOIS, and 24-48h propagation lag on registration events.\n\n\n\n##  When this is not the right answer\n\n  * **You're doing <1,000 lookups per month.** The `whois` binary still works for most gTLDs; below that volume the actor's pricing is overkill.\n  * **You need reverse WHOIS.** Post-GDPR, this is functionally impossible from public data. WhoisXML and DomainTools offer pre-2018 historical snapshots — different product.\n  * **You need sub-100ms latency.** The actor's p50 is 400-900ms. For interactive UIs, cache aggressively or use a low-latency provider.\n  * **You need privileged unredacted access.** Law enforcement and CERTs want direct registrar contracts, not a public-API scraper.\n  * **You need bulk zone-file access.** That's ICANN's CZDS program, not WHOIS/RDAP.\n\n\n\n##  FAQ\n\n###  Does the actor handle GDPR-redacted records correctly?\n\nYes. Every record has a `registrant.redacted` boolean. When true, the individual fields (`name`, `email`, `phone`) are null and `redaction_reason` tells you why. We don't try to de-redact via backchannel data lookups; that would be a compliance risk for you and for us.\n\n###  What's the rate limit?\n\nApify-side, the actor supports up to 50 concurrent lookups per run by default. Upstream RDAP servers have their own rate limits (Verisign's `.com` RDAP tolerates roughly 20 req/sec per source IP; IANA's rate limits are documented on their status page). The actor rotates through an egress IP pool and respects upstream 429s with exponential backoff, so you generally don't hit these limits in practice.\n\n###  Which TLDs don't support RDAP yet?\n\nThe Freenom family (`.tk`, `.ml`, `.ga`, `.cf`, `.gq`), `.ru` and `.su` (Russia hasn't transitioned), parts of `.cn` (partial RDAP, inconsistent data), and a long tail of small ccTLDs (`.mz`, `.sd`, `.ve`, a few dozen others). For all of these, the actor falls back to WHOIS parsing automatically. `protocol_used` tells you which path was taken.\n\n###  How current is the data?\n\nRDAP lookups are live — the actor hits the authoritative registry's RDAP server in real time. WHOIS lookups are similarly live. Cache TTL is 1 hour for unchanged records (keyed on `domain + event hash`); pass `\"no_cache\": true` to force a fresh fetch. At $0.005/lookup, the cache layer is the difference between a reasonable bill and a very unreasonable one.\n\n###  Can I query IP addresses and ASNs, not just domains?\n\nYes. RDAP defines IP-range and ASN query paths. Pass `\"ips\": [\"8.8.8.8\"]` or `\"asns\": [15169]` in the input. Responses follow the same unified schema adapted for the IP/ASN object types.\n\n###  What happens if an RDAP server is down?\n\nThe actor falls back to WHOIS automatically when RDAP returns a 5xx or times out. If both protocols fail, the record is returned with `protocol_used: \"failed\"` and an `error` field describing what went wrong, rather than being silently dropped.\n\n###  Does it work for internationalized domain names (IDN)?\n\nYes. Punycode-encoded (`xn--`) and Unicode-encoded inputs both work; the response includes both forms. Some registries return only the ASCII form in RDAP; we normalize.\n\n###  How does this compare to just running `whois` from an EC2 box?\n\nFor ad-hoc use, `whois` is still fine. For production pipelines, three things are changing: RDAP compliance has broken `whois` for an increasing fraction of gTLDs, registrar rate-limiting is aggressive against raw TCP/43 scrapers, and the engineering cost of maintaining a TLD-aware parser is rising faster than compute costs are falling.\n\n###  Can I run this on Apify's free tier?\n\nYes. Apify's free tier includes $5/month of compute credit, enough for roughly 1,000 lookups. Beyond that it's pay-as-you-go at $0.005/lookup. See the actor page for current pricing.\n\n##  What's next\n\nIf you like this actor, two related ones from the same pipeline often show up in the same domain-intelligence workflows:\n\n  * company-data-aggregator — pulls corporate registration data (Companies House, SEC EDGAR, Handelsregister) and matches it against domain registrant data for entity-level graph building.\n  * tranco-rank-lookup — returns the current Tranco top-list rank for any domain, useful for prioritizing investigation queues by popularity-weighted risk.\n\n\n\n##  Conclusion\n\nWHOIS was a 1982 protocol with a three-page spec and no schema. It survived in production for forty years mostly by inertia. ICANN's RDAP mandate, enforced from August 2024, is finally decommissioning it for gTLDs — good for everyone downstream as long as you update your code. The WhoisXML free tier and most cheap alternatives have simultaneously been gated or priced out of hobby use, leaving teams staring at broken pipelines and four-figure quotes.\n\nThe whois-replacement actor is built for that gap. RDAP-first, WHOIS fallback, one JSON schema across every TLD, $0.005 per lookup, raw-payload passthrough when you need it. If you have a domain-intelligence pipeline that broke quietly in 2024 and hasn't been fixed, this is the minimum-surface-area fix.",
  "title": "WHOIS Is Broken in 2026. Here's the RDAP-First Drop-In That Actually Returns JSON"
}