Introduction
The attack surface of a modern organization extends far beyond its IT inventory. Acquired companies bring legacy domains. Shadow IT produces unmanaged subdomains. Domain registrations lapse and are quietly picked up by adversaries. Typosquatted lookalikes go live hours before a credential-harvesting campaign. Each of these represents an entry point that can only be discovered through continuous monitoring of external infrastructure — and each leaves a trace in registration and DNS data.
This is where a whois api attack surface management workflow becomes operational rather than theoretical. WHOIS data answers who registered a domain and when. DNS records reveal where it resolves and who controls its nameservers. SSL certificates expose which hostnames are live and which CA issued the certificate. Together, these three sources form the intelligence layer that makes automated domain monitoring threat intelligence possible — querying at the speed and scale that a manual process can never match.
This guide covers the use cases, the architecture, and the implementation: a production-grade Python asyncio pipeline that enriches a domain list via the WhoisJSON domain intelligence api, scores each domain on a composite risk model, and outputs structured JSON ready for a SIEM or alerting system.
What Is Attack Surface Management (ASM)?
Attack Surface Management is the continuous process of discovering, inventorying, and monitoring the externally exposed assets of an organization — with the goal of identifying exploitable entry points before adversaries do. Unlike traditional vulnerability management, which focuses on known, catalogued assets, ASM must account for unknown and unmanaged exposure: forgotten subdomains, expired domains still referenced in production links, cloud resources provisioned without IT visibility.
Domain-level intelligence sits at the core of any ASM program because every internet-reachable asset is anchored to a domain name. MITRE ATT&CK catalogues domain acquisition as a pre-compromise technique (T1583.001 — Domains) used by threat actors to stage infrastructure before a campaign. Intelligence platforms track campaigns by clustering newly registered domains that share registrant patterns, nameserver configurations, or SSL fingerprints. The data sources are always the same: WHOIS registration records, DNS resource records, and X.509 certificate metadata.
A domain intelligence api makes this data available programmatically — structured JSON in response to a single authenticated HTTPS request — which is the prerequisite for automation. Manual lookups on a web-based WHOIS tool are useful for a one-off investigation; they cannot power a continuous monitoring program across thousands of domains.
Key Domain Intelligence Use Cases in ASM
Typosquatting and lookalike domain detection
Adversaries register domains that visually or phonetically resemble a target brand — transpositions, homoglyphs, hyphenated variants — and activate them for credential harvesting or business email compromise. Detecting these early requires monitoring new domain registrations and querying WHOIS for each candidate.
The WhoisJSON /whois endpoint returns age.isNewlyRegistered (true when the domain is under 30 days old) and age.isYoung (under 365 days). A lookalike domain registered yesterday, with a Cloudflare-proxied A record, no MX, and a Let's Encrypt certificate already provisioned, is a high-confidence phishing infrastructure candidate — the exact signal combination our phishing domain detection guide covers in detail, with composite scoring across WHOIS, DNS, and SSL.
Registrant and nameserver change monitoring
A sudden change in the registrant email, registrar, or nameservers for a domain you own — or a domain similar to yours — is a high-severity signal. Store a WHOIS baseline for each domain under surveillance. On every scheduled run, compare registrar.name, nameserver, and contacts.owner against the baseline. Changes outside a known maintenance window warrant immediate investigation. Nameserver hijacking is how BGP and DNS hijack campaigns typically begin.
Expired domain identification
Domains previously owned by your organization — or by companies you have acquired — that have since expired are a persistent exposure. Once a domain drops into open registration, anyone can register it and immediately weaponize it against former employees, partners, or customers who still have links pointing there. The expiration.daysLeft and expiration.isExpired fields in the RDAP-enriched WHOIS response let you build a watchlist and act before the drop window opens.
WHOIS + DNS + SSL correlation scoring
No single signal is definitive. A newly registered domain with redacted WHOIS contacts, no MX records, no DMARC, an A record pointing to a bulletproof hosting range, and a Let's Encrypt certificate provisioned the same day as registration — each element alone is unremarkable; together they produce a high-confidence phishing infrastructure indicator. Automated correlation across all three data sources is what separates a bulk whois api security workflow from a manual investigation.
How WhoisJSON Powers ASM Pipelines
The WhoisJSON API exposes five endpoints that together constitute a complete domain intelligence stack. All five share the same authentication mechanism (Authorization: TOKEN=YOUR_API_KEY) and return structured JSON:
/whois— Registrant, registrar, creation and expiry dates, nameservers, EPP status codes. RDAP-enriched responses includeage,expiration,statusAnalysis, andnsAnalysisobjects — pre-computed fields that eliminate the need for client-side date parsing or status code interpretation./nslookup— A, AAAA, MX, TXT, NS, CNAME, SOA, CAA, DMARC, BIMI, MTA-STS, TLSRPT — all record types in a single response. DNS anomalies (missing MX, missing DMARC, CAA misconfiguration) are direct ASM signals./ssl-cert-check— Issuer, validity window, Subject Alternative Names, SHA-256 fingerprint, chain validity. Correlate cert issuance date with domain creation date for a high-signal timing indicator. For expiry monitoring, change detection, and Slack alerting patterns on top of this endpoint, see the SSL certificate monitoring API guide./domain-availability— Instant availability check. Use for watchlist monitoring: when a domain on your watchlist becomes available, trigger an immediate alert and consider defensive registration./subdomains— Active subdomain discovery via parallel DNS brute-force (800+ patterns, wildcard detection). Expand the enumerated attack surface from a root domain to its full subdomain inventory.
The typical pipeline architecture is linear:
Trigger (watchlist schedule / threat feed / ad-hoc query) ↓ Enrich (WHOIS + DNS + SSL called in parallel per domain) ↓ Score (composite risk signal from correlated fields) ↓ Alert (score ≥ threshold → Slack / PagerDuty / SIEM ingest) ↓ Store (JSON record → database for baseline comparison & trend analysis)
Building a Domain Intelligence Pipeline (Python)
The pipeline below uses asyncio and aiohttp to call WHOIS, DNS, and SSL in parallel for each domain in the input list, then applies a weighted risk scoring function. Each domain enrichment fires three concurrent requests; the outer loop processes all domains concurrently up to the semaphore limit.
CONCURRENCY to stay within your plan's RPM ceiling — the semaphore caps simultaneous domain enrichments, not individual HTTP requests.# pip install aiohttp
import asyncio, json
import aiohttp
API_KEY = "YOUR_API_KEY"
BASE = "https://whoisjson.com/api/v1"
HEADERS = {"Authorization": f"TOKEN={API_KEY}"}
CONCURRENCY = 15 # concurrent domain enrichments; tune to plan RPM / 3
RISK_WEIGHTS = {
"newly_registered": 30, # age.isNewlyRegistered (< 30 days)
"young_domain": 15, # age.isYoung (< 365 days)
"redacted_contacts": 20, # owner name/org/email all absent
"no_mx": 10, # no MX records
"no_dmarc": 10, # no DMARC record
"free_ca_cert": 5, # Let's Encrypt issuer
"cert_same_day": 15, # cert issued same day as domain creation
}
async def fetch(session: aiohttp.ClientSession, path: str, domain: str) -> dict:
try:
async with session.get(
f"{BASE}/{path}",
params={"domain": domain},
headers=HEADERS,
timeout=aiohttp.ClientTimeout(total=12),
) as resp:
return await resp.json() if resp.status == 200 else {}
except Exception:
return {}
async def enrich(session: aiohttp.ClientSession, domain: str) -> dict:
"""Fetch WHOIS, DNS, and SSL concurrently for a single domain."""
whois, dns, ssl = await asyncio.gather(
fetch(session, "whois", domain),
fetch(session, "nslookup", domain),
fetch(session, "ssl-cert-check", domain),
)
return {"domain": domain, "whois": whois, "dns": dns, "ssl": ssl}
def score(e: dict) -> dict:
"""Compute a composite risk score from the enriched domain object."""
pts = 0
flags = []
w = e.get("whois") or {}
d = e.get("dns") or {}
s = e.get("ssl") or {}
age = w.get("age") or {}
if age.get("isNewlyRegistered"):
pts += RISK_WEIGHTS["newly_registered"]; flags.append("newly_registered")
elif age.get("isYoung"):
pts += RISK_WEIGHTS["young_domain"]; flags.append("young_domain")
owner = ((w.get("contacts") or {}).get("owner") or [{}])[0]
if not any([owner.get("name"), owner.get("organization"), owner.get("email")]):
pts += RISK_WEIGHTS["redacted_contacts"]; flags.append("redacted_contacts")
if not d.get("MX"):
pts += RISK_WEIGHTS["no_mx"]; flags.append("no_mx")
if not d.get("DMARC"):
pts += RISK_WEIGHTS["no_dmarc"]; flags.append("no_dmarc")
issuer_o = (s.get("issuer") or {}).get("O", "")
if "Let's Encrypt" in issuer_o:
pts += RISK_WEIGHTS["free_ca_cert"]; flags.append("free_ca_cert")
ssl_from = (s.get("valid_from") or "")[:10]
whois_crt = (w.get("created") or "")[:10]
if ssl_from and whois_crt and ssl_from == whois_crt:
pts += RISK_WEIGHTS["cert_same_day"]; flags.append("cert_same_day")
pts = min(pts, 100)
return {
"domain": e["domain"],
"score": pts,
"risk": "high" if pts >= 60 else ("medium" if pts >= 30 else "low"),
"flags": flags,
"registrar": (w.get("registrar") or {}).get("name"),
"created": w.get("created"),
"expires": w.get("expires"),
"nameservers": w.get("nameserver", []),
"a_records": d.get("A", []),
"ssl_valid": s.get("valid"),
"ssl_issuer": issuer_o or None,
"ssl_expiry": s.get("valid_to"),
}
async def run(domains: list[str]) -> list[dict]:
sem = asyncio.Semaphore(CONCURRENCY)
connector = aiohttp.TCPConnector(limit=CONCURRENCY * 3)
async def bounded(domain: str):
async with sem:
return await enrich(session, domain)
async with aiohttp.ClientSession(connector=connector) as session:
results = await asyncio.gather(
*[bounded(d) for d in domains],
return_exceptions=True,
)
output = []
for domain, result in zip(domains, results):
if isinstance(result, Exception):
output.append({"domain": domain, "error": str(result)})
else:
output.append(score(result))
return output
if __name__ == "__main__":
domains = [
"example.com",
"examp1e.com", # homoglyph candidate
"example-secure.net", # lookalike candidate
]
results = asyncio.run(run(domains))
print(json.dumps(results, indent=2))Sample output for a high-risk domain:
{
"domain": "examp1e.com",
"score": 80,
"risk": "high",
"flags": ["newly_registered", "redacted_contacts", "no_mx", "no_dmarc", "cert_same_day"],
"registrar": "Namecheap, Inc.",
"created": "2026-04-22 08:14:00",
"expires": "2027-04-22 08:14:00",
"nameservers": ["dns1.registrar-servers.com", "dns2.registrar-servers.com"],
"a_records": ["185.220.101.47"],
"ssl_valid": true,
"ssl_issuer": "Let's Encrypt",
"ssl_expiry": "2026-07-21T08:14:00.000Z"
}
Scaling Domain Intelligence: Rate Limits and Bulk Processing
Each domain enrichment in the pipeline above consumes 3 API credits. The effective domain throughput is therefore floor(plan_rpm / 3) domains per minute. The table below maps each plan to its practical ASM throughput:
| Plan | RPM | Domains / min | Domains / day (continuous) |
|---|---|---|---|
| Basic (free) | 20 | ~6 | ~8,600 |
| Pro | 40 | ~13 | ~18,700 |
| Ultra | 60 | ~20 | ~28,800 |
| Mega | 80 | ~26 | ~37,400 |
| Giga | 200 | ~66 | ~95,000 |
| Tera | 300 | ~100 | ~144,000 |
| Atlas | 900 | ~300 | ~432,000 |
For sustained bulk processing, set CONCURRENCY to floor(plan_rpm * 0.8 / 3) — 80% utilization leaves headroom for retries and avoids triggering HTTP 429 responses. The Remaining-Requests response header gives a live monthly quota balance that can be monitored to pause processing before exhaustion.
For a deeper look at parallel request patterns, retry logic on HTTP 429, and per-domain error isolation — including a Node.js p-limit approach — see Bulk WHOIS: How to Query Multiple Domains at Once.
Three caching strategies reduce API credit consumption significantly for large-scale ASM programs:
- WHOIS result caching (24 h): WHOIS data changes infrequently — registrant updates and nameserver changes happen at most a few times per year for a given domain. Cache the response locally and re-query only when the local TTL expires or a change-detection run identifies a discrepancy.
- Selective force-refresh: when an alert fires on a domain (new registration detected, score threshold crossed), use
?_forceRefresh=1to bypass the API cache for that specific domain and get a fresh response. Pro plan and above; costs 2 credits. - Tiered polling frequency: high-risk domains (score ≥ 60) should be enriched hourly; medium-risk domains daily; low-risk domains weekly. This reduces total API credits consumed by an order of magnitude versus polling every domain at the same interval.
Real-World Scenario: Detecting a Phishing Infrastructure
A threat intelligence feed reports paypa1-secure-login.com as a newly observed domain. Your ASM pipeline picks it up and runs a full enrichment. Here is what each data source returns and how the scorer interprets it.
Step 1 — WHOIS query
{
"name": "paypa1-secure-login.com",
"registered": true,
"created": "2026-04-22 09:41:00",
"expires": "2027-04-22 09:41:00",
"registrar": { "name": "Namecheap, Inc." },
"nameserver": ["dns1.registrar-servers.com", "dns2.registrar-servers.com"],
"contacts": { "owner": [{ "name": null, "organization": null, "email": null }] },
"age": { "days": 2, "isNewlyRegistered": true, "isYoung": true },
"source": "rdap"
}Score so far: +30 (newly_registered) + 20 (redacted_contacts) = 50 pts
Step 2 — DNS query
{
"A": ["185.220.101.47"],
"NS": ["dns1.registrar-servers.com", "dns2.registrar-servers.com"],
"MX": [],
"DMARC": []
}No MX, no DMARC. The A record (185.220.101.47) maps to a hosting range frequently associated with bulletproof providers. Score: +10 (no_mx) + 10 (no_dmarc) = +20 pts → running total: 70 pts
Step 3 — SSL query
{
"domain": "paypa1-secure-login.com",
"issuer": { "O": "Let's Encrypt", "CN": "R11" },
"valid_from": "2026-04-22T09:55:00.000Z",
"valid_to": "2026-07-21T09:55:00.000Z",
"valid": true,
"details": { "subjectaltname": "DNS:paypa1-secure-login.com" }
}Let's Encrypt certificate provisioned the same day as registration (valid_from date matches created date). Score: +5 (free_ca_cert) + 15 (cert_same_day) = +20 pts → final score: 90 / 100 — HIGH RISK
newly_registered, redacted_contacts, no_mx, no_dmarc, free_ca_cert, cert_same_day. Automatic actions: domain added to block list, SOC alert fired, threat intel team notified. Investigation confirmed the domain was serving a PayPal credential-harvesting page within 4 hours of registration.Conclusion
Attack surface management at scale is an automation problem first. The data exists — WHOIS registration records, DNS resource records, SSL certificate metadata — but it only becomes operationally useful when it can be queried programmatically, enriched in parallel, and scored consistently across thousands of domains per day. That is what a domain intelligence api enables.
The pipeline pattern is straightforward: one API call per data source per domain, three concurrent requests per enrichment, a composite risk score from correlated fields, and a tiered alert ladder. The same architecture handles a ten-domain watchlist and a hundred-thousand-domain continuous sweep — the only variable is the plan's RPM ceiling and the concurrency setting. For the operational layer — alert routing, watchlist management, and notification setup — the domain monitoring baseline for security teams is the natural complement to this pipeline guide.
For whois api enterprise deployments at Atlas throughput (900 RPM, ~432,000 domains/day) or above, or for specific requirements around SLA, dedicated infrastructure, or custom integrations, contact the sales team.
Start Building
1,000 free API requests/month — WHOIS, DNS, SSL, availability, and subdomains. No credit card.
Get Your Free API KeyEnterprise & High-Volume
Custom rate limits, dedicated support, SLA guarantee. Talk to the sales team.
Contact Sales