Domain Search API
Query the Whole Domain Index by Any Field.
Send one JSON body. Filter on names, WHOIS, DNS, certificates and web data, and get back full domain records, a page at a time or as an export.
Use it when the question crosses fields. The finder APIs start from one indicator; Domain Search describes a set of domains by everything you know about them.
QueryPOST /discovery/domain-searchall applied
-
must
dns.mx.mail_serverseq "mail.hosting.example" -
must
domain.whois.create_dategte 2026-01-01T00:00:00Z -
must
webdata.connection_statuseq "success" -
sort
domain.whois.create_datedesc
Results2
- harbor-cafe.example2026-09-14 · mail.hosting.example
- oak-studio.example2026-08-30 · mail.hosting.example
Put All Conditions in One Body.
One question across three layers: every domain whose mail goes to one server, registered since the start of 2026, that answers on the web. Newest registrations first.
| Parameter | Type | Description | |
|---|---|---|---|
filters | object | Optional | In the body. Arrays of conditions under must, should and must_, each {name, type, value}. |
sort | array | Optional | In the body. One or more {field, order} pairs, order is asc or desc. |
page | integer | Optional | Page number, from 1. |
page_ | integer | Optional | Results per page. |
export | boolean | Optional | Return the matches as an export instead of a page. Default false. |
export_ | string | Optional | json or csv. |
export_ | string | Optional | How much of each record to include: basic, default or extended. |
Request
curl -X POST "https://api.deepinfo.com/v1/discovery/domain-search?page_size=25" \
-H "apikey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"filters": {
"must": [
{ "name": "dns.mx.mail_servers", "type": "eq", "value": "mail.hosting.example" },
{ "name": "domain.whois.create_date", "type": "gte", "value": "2026-01-01T00:00:00Z" },
{ "name": "webdata.connection_status", "type": "eq", "value": "success" }
]
},
"sort": [ { "field": "domain.whois.create_date", "order": "desc" } ]
}'import requests
body = {
"filters": {
"must": [
{"name": "dns.mx.mail_servers", "type": "eq", "value": "mail.hosting.example"},
{"name": "domain.whois.create_date", "type": "gte", "value": "2026-01-01T00:00:00Z"},
{"name": "webdata.connection_status", "type": "eq", "value": "success"},
],
},
"sort": [{"field": "domain.whois.create_date", "order": "desc"}],
}
r = requests.post(
"https://api.deepinfo.com/v1/discovery/domain-search",
params={"page_size": 25},
json=body,
headers={"apikey": "YOUR_API_KEY"},
)
data = r.json()
print(data["result_count"])
for d in data["results"]:
print(d["punycode"], d["domain"]["whois"]["create_date"])const r = await fetch('https://api.deepinfo.com/v1/discovery/domain-search?page_size=25', {
method: 'POST',
headers: { apikey: 'YOUR_API_KEY', 'Content-Type': 'application/json' },
body: JSON.stringify({
filters: {
must: [
{ name: 'dns.mx.mail_servers', type: 'eq', value: 'mail.hosting.example' },
{ name: 'domain.whois.create_date', type: 'gte', value: '2026-01-01T00:00:00Z' },
{ name: 'webdata.connection_status', type: 'eq', value: 'success' },
],
},
sort: [{ field: 'domain.whois.create_date', order: 'desc' }],
}),
})
const { result_count, results } = await r.json()
console.log(result_count, results.map((d) => d.punycode))
Response
{
"page": 1,
"page_size": 25,
"result_count": 2,
"results": [
{
"punycode": "harbor-cafe.example",
"unicode": "harbor-cafe.example",
"is_idn": false,
"domain": {
"name": { "punycode": "harbor-cafe", "length": 11 },
"whois": {
"create_date": "2026-09-14T08:12:40Z",
"registrar": "…",
"name_servers": ["ns1.hosting.example"]
}
},
"dns": {
"a": { "ip_addresses": ["198.51.100.23"] },
"mx": { "mail_servers": ["mail.hosting.example"] }
},
"webdata": { "connection_status": "success", "…": "…" }
},
{
"punycode": "oak-studio.example",
"unicode": "oak-studio.example",
"is_idn": false,
"domain": {
"whois": { "create_date": "2026-08-30T17:41:02Z" }
},
"dns": {
"mx": { "mail_servers": ["mail.hosting.example"] }
},
"webdata": { "connection_status": "success", "…": "…" }
}
]
}
Where a Condition Sits Decides How It Combines.
All conditions under must have to match. At least one under should has to match. Nothing under must_ may match.
The example on the right takes every domain on one name server, keeps the ones already in use, those that accept mail or answer on the web, and leaves out the domains you own.
{
"filters": {
"must": [
{ "name": "dns.ns.name_servers", "type": "eq", "value": "ns1.hosting.example" }
],
"should": [
{ "name": "dns.mx.mail_servers", "type": "exists", "value": true },
{ "name": "webdata.connection_status", "type": "eq", "value": "success" }
],
"must_not": [
{ "name": "domain", "type": "in", "value": ["acme.example", "acme-group.example"] }
]
}
}- Every one has to match (must)
- At least one has to match (should)
- None may match (must_not)
Workflows That Start With a Filter.
Threat Hunting
Describe an actor’s habits as filters, such as a registrar, a name-server pattern and a registration window, and see what else matches.
Internet Research
Measure a slice of the internet by its configuration: live sites without HSTS, self-signed certificates, domains expiring with WHOIS privacy on.
Phishing Detection
Run a look-alike body on a schedule, fuzzy on domain.name with your own domain in must_, and triage what appeared since the last run, newest registration first.
Brand Impersonation
Sweep names that carry your brand keyword next to words like login, verify or secure, across all extensions.
Before You Send a Query.
| Searchable fields | Grouped by family: name and extension, subdomain, WHOIS, DNS, IP history, SSL, and web data with HTTP headers. The full list is in the reference. |
|---|---|
| Totals | result_ is the total number of matches across all pages. See pagination. |
| Empty body | An empty body {} matches all records in the dataset. Send at least one condition. |
| Export | export=true returns the matches as JSON or CSV instead of a page. Large exports can time out, so filter first. |
| In the platform | EASM custom discovery rules are written in this filter language over Deepinfo’s domain data. A query you test here can become a rule that proposes new assets. See Asset Discovery. |
| Rate limits | Plan-dependent. The default is one request per second per key and endpoint. See rate limits. |
Questions About Domain Search API
Are subdomains in the results as well?
Yes. Each record is a host name, filtered as fqdn, and a filter on domain matches a registered domain together with its subdomains. subdomain_ and subdomain_ filter on the subdomain labels themselves.
Which fields can results be sorted on?
The name and extension (punycode, domain.extension.punycode), the WHOIS create, expiry, update and last-change dates, and dns_ and ssl_. Pass them in sort as {field, order} pairs.
Can we find every host that serves one certificate?
Yes. Filter ssl.fingerprint.sha256 on the certificate’s SHA-256 fingerprint. A fingerprint identifies one certificate, so the result is each host in the index that serves it.
Can we find domains that pointed to an IP in the past?
Yes. ip_ and domain.ip_ keep each address a host or domain has resolved to over time, so a filter on them also matches names that have moved since.
How is the Domain Search API priced?
As part of an API services plan, priced by volume and frequency, separately from the platform. See Data & API pricing, or talk to us about the queries you plan to run.
Does the filter syntax work on other Deepinfo endpoints?
Yes, with two exceptions. The EASM, CTI and BRP searches and their bulk actions take this filters body and these operators, each with its own list of searchable fields. Darkweb Search and Report Search use their own formats.
What happens if a field name is wrong?
The request fails with a 400 application error, and its parameters list points to what was rejected. See errors.
Bring the Question. We’ll Write the Filter With You.
Tell us what you want to find in the dataset. We set up access and walk through the fields with your team on a short call.