Provide current and historical ownership information on domains / IPs. Identify all connections between domains, registrants, registrars, and DNS servers.
Look into all current and historical DNS / IP connections between domains and A, MX, NS, and other records. Monitor suspicious changes to DNS records.
Get detailed context on an IP address, including its user’s geolocation, time zone, connected domains, connection type, IP range, ASN, and other network ownership details.
Access our web-based solution to dig into and monitor all domain events of interest.
Get access to a web-based enterprise-grade solution to search and monitor domain registrations and ownership details for branded terms, fuzzy matches, registrants of interest, and more.
Independent, evidence-based DNS and abuse intelligence for applicants, advisors, governments, and counsel participating in the ICANN 2026 New gTLD Program.
Predictive threat intelligence is your best first line of defense. Subscribe to the feeds to strengthen your cybersecurity posture. Contact us today for more information.
Unlock integrated intelligence on Internet properties and their ownership, infrastructure, and other attributes.
Our complete set of domain, IP, and DNS intelligence available via API calls as an annual subscription with predictable pricing.
Offers complete access to WHOIS, IP, DNS, and subdomain data for product enrichment, threat hunting and more.
Uncover entire attack surfaces with this API to embed asset discovery, vulnerability scanning, and technology metadata into your platform. Now in early access.
Talk to our APIs using LLMs. Connect your preferred LLM to WhoisXML API and simply chat about WHOIS, DNS, threat intelligence, and more.
I’m your Domain Intelligence Assistant. I make it easy to explore WHOIS, DNS, and threat data from WhoisXML API — I’m cloud-based, fast, and always ready to help.
A custom GPT for WHOIS, DNS, IP, and threat intelligence research. Connects ChatGPT directly to WhoisXML API to enable fast, conversational investigations and domain insights.
One Universal API Credit Pool
Simplify API credit purchasing and usage with one balance that works across our product portfolio.
Errors return a JSON body with a detail key:
{ "detail": "Access denied: insufficient credits or invalid subscription" }
Every response, successful or not, carries an X-Request-ID header. Quote it when contacting support.
| Status | Meaning | Retry? | Billed |
|---|---|---|---|
400 |
Unsafe or unusable target | No — fix the request | No |
403 |
Auth, subscription or credit problem | No — fix the key or balance | No |
404 |
Job not found | No | No |
405 |
Wrong HTTP method | No — use POST |
No |
422 |
Request failed validation | No — fix the request | No |
429 |
Rate limited | Yes — honour Retry-After |
No |
503 |
Temporarily unavailable | Yes — honour Retry-After |
No |
No error response is billed. You are charged only for domains that come back with a result.
400 — unsafe target{ "detail": "Refusing to fetch unsafe target: <reason>" }
The requested target resolves somewhere we will not fetch — a private, internal or link-local address. Not retriable: the domain itself is the problem.
On a bulk request this does not fail the batch. The affected row carries an error string instead and the call still returns 200. An async job records the same row error.
Async batch submission returns 400 for two further cases:
domains list cannot be emptywebhook_url rejected: <reason>. Webhook URLs must use http or https and point to a publicly routable address.403 — authentication, subscription or creditsTwo shapes:
{ "detail": "Missing required parameter: apiKey" }
You did not send the apiKey query parameter.
{ "detail": "Access denied: insufficient credits or invalid subscription" }
The key is not recognised, the subscription is not active, or the credit balance is exhausted. Check your balance and subscription — see Account balance information — and contact support@whoisxmlapi.com if both look correct. Do not retry automatically.
404 — job not found{ "detail": "Job abc123 not found" }
Returned by GET /api/v4/jobs/{job_id}. Only the API key that submitted a job can read it, and an unknown job and a job belonging to someone else return the identical 404 by design. Jobs are kept for at least 24 hours after submission (the 24-hour window restarts each time a job is updated); after that they read as not found.
405 — wrong method/api/v4/categorize and /api/v4/categorize/bulk are POST only. A GET returns 405 Method Not Allowed. The domain goes in the JSON body; apiKey stays a query parameter.
422 — validation failedEither a structured validation array:
{
"detail": [
{
"type": "string_too_long",
"loc": ["body", "domain"],
"msg": "String should have at most 253 characters",
"input": "…"
}
]
}
or an explicit message, for example:
{ "detail": "coverage must be a number between 0.01 and 0.99" }
Input limits:
| Field / parameter | Limit |
|---|---|
domain |
253 characters |
url |
2,048 characters |
title |
2,048 characters |
description |
4,096 characters |
body_text |
20,000 characters |
domains (bulk) |
100 entries |
domains (async) |
10,000 entries |
limit (job results) |
between 1 and 10,000 |
limit (job list) |
between 1 and 100 |
offset (jobs) |
0 or more |
output_mode |
top1 or set |
coverage |
between 0.01 and 0.99 |
pack |
a known pack id; an unknown pack is a 422 |
Over-limit input returns 422, not 413. Fix the request — it is never retriable as sent.
429 — rate limited{ "detail": "Rate limit exceeded. Please try again later." }
Requests are limited per API key. A second, independent throttle exists on the billing side and returns a similar 429:
{ "detail": "Rate limit exceeded — please retry shortly" }
Both of these carry a Retry-After header giving the number of seconds to wait — the billing throttle sends Retry-After: 1. Honour it. Do not retry in a tight loop. Rate-limited requests are never billed. See Credits and limits for the limits themselves.
503 — temporarily unavailableSeveral transient conditions share this status. All are retriable with backoff, and all carry a Retry-After header where one applies.
detail |
Cause |
|---|---|
Too many concurrent classifications; retry shortly. |
Concurrency limit reached on the single-domain endpoint. The body also carries limit and stage (inference or crawl_slot, diagnostic only). The bulk endpoint queues instead of shedding. |
Temporary DNS failure (dns_timeout): the domain's name servers did not answer in time. Retry the request. |
The domain's name servers did not answer our lookup in time on the single-domain endpoint. It carries Retry-After: 5. On the bulk endpoint and in an async job the row carries temporary DNS failure (dns_timeout): retry this domain instead (see below). |
Classification pipeline not initialized |
The service is still starting. Retry after a brief delay. |
HCC calibration not available: <reason> |
You requested output_mode=set and the calibration is unavailable. Retry, or fall back to the default output_mode=top1. |
Billing service temporarily unavailable — please retry |
The billing backend is unavailable. Retry with backoff. |
Async batch submission is temporarily unavailable — please retry, or use POST /api/v4/categorize/bulk. |
Async submission cannot be accepted right now. Use the synchronous bulk endpoint, or retry. |
Policy pack not loaded: … / Taxonomy not loaded: … |
An opt-in block you requested is unavailable while the service is degraded. Retry, or drop the opt-in parameter — the base classification still works. |
Job manager not initialized |
The async job service is still starting. Retry. |
A 502 can also reach you from the gateway in front of the service rather than from the API itself. Treat it like a 503: retry with backoff.
POST /api/v4/categorize/bulk does not fail the whole batch when one domain fails. As long as the request itself was valid and authenticated you get 200 with a row for every domain — check each row's error field:
{
"domain": "internal.example",
"categories": [],
"error": "unsafe fetch target: <reason>"
}
A completed async job's rows carry the same per-row error. When a domain's name servers do not answer in time, the row's error is temporary DNS failure (dns_timeout): retry this domain: send that domain again. To retry a domain from an async job, submit it in a new job, with a new Idempotency-Key if you send one.
Rows that fail are not charged.
400, 403, 404, 405, 422. These need a fixed request, key or balance — waiting changes nothing.Retry-After: 429, 503, and a gateway 502.Idempotency-Key header. A repeat within 24 hours is processed again but normally charges credits_charged: 0 if an earlier request with that key was already billed; a retry after a 429 or 503 bills normally. On async batch submission, a repeat with the same key returns the original job.We are here to listen. For a quick response, please select your request type. By submitting a request, you agree to our Terms of Service and Privacy Policy.