> ## Documentation Index
> Fetch the complete documentation index at: https://wiki.darknetsearch.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Map an organization's internet-facing hosts

> Passively discover the hosts, IP addresses, open ports and known CVEs under a domain, then read them back as a host list and a three-number summary.

Infra search maps what a domain exposes to the internet. You give it `acme.com`. It collects the hosts under that domain from DNS records, certificate logs and internet-wide scan data, resolves each one to an IPv4 address, and attaches the open ports, known CVEs and network owner of that address.

The scan is passive. It reads public datasets and live DNS, and never connects to services on your hosts. In the dashboard the same scan is **Exposure → Attack Surface Discovery**, and scans started in either place share one history and one allowance.

The flow is asynchronous: start a scan, poll it until it ends, then read the hosts and the summary.

### How it differs from other searches

Infra search is not quick search. Pick by what you need back.

| You want to… | Use |
| - | - |
| Inventory the hosts, IPs, ports and CVEs under a domain you own | **Infra search** (this page) |
| Find a domain, email or IP in leaked and breach data | [Quick search](https://wiki.darknetsearch.com/api/guides/wide-search.md) |
| Read the raw certificates issued for a domain | [SSL Transparency Logs](https://wiki.darknetsearch.com/api/guides/expert-ssl-logs.md) |
| Get a full external assessment with active port verification, delivered as a report | **Exposure → Risk Assessments** in the dashboard |

### Availability and cost

**Availability:** Attack Surface Mapping, included in every edition.

**Cost:** 1 unit of your monthly Attack Surface Mapping allowance for each scan you start. The unit is taken before the scan is created and refunded if `create_infra_search_request` returns an error. Polling, reading hosts, statistics and deletes use no scan units. A scan that ends `failed` keeps its unit, and so does a scan you delete.

Without the feature, or when your license is not active, the four scan endpoints (`create_`, `list_`, `get_` and `delete_infra_search_request`) return `403`:

```json theme={"dark"}
{
  "detail": "This feature is not included in your plan."
}
```

The host and statistics endpoints have no plan or license check and keep serving existing results.

When the month's allowance is spent, `create_infra_search_request` returns `429` and no scan is created:

```json theme={"dark"}
{
  "detail": "You have reached your Infra Search Scan limit for this month.",
  "resource": "infra_search_scan",
  "period": "month",
  "limit": 10
}
```

`limit` is your monthly scan allowance. A `429` whose body reads `"message": "Rate limit exceeded"` is the request rate limit, not the scan allowance. Retry after `retry_after_seconds`.

### What a scan collects

The scan runs four sources in this order. Each host name and IP pair it keeps becomes one result, and the result's `type` names the source that found it.

| `type` | Where the hosts come from |
| - | - |
| `DNSDumpster` | DNS records for the domain from DNSDumpster: hosts with A records, mail servers (MX) and name servers (NS). Answers are cached for 24 hours per domain, so a repeat scan within a day reuses them. |
| `CrtSH` | Certificate common names (not SAN entries) in certificate-transparency logs on crt.sh. |
| `KaduuLeakCenter` | Host names in DarknetSearch's own certificate index. |
| `ZoomEye` | Hosts in ZoomEye's internet-wide scan data whose reverse-DNS name falls under the domain. |

Four rules decide what comes back:

* **Only the domain and its subdomains.** `acme.com` and `vpn.acme.com` are kept. `acme-login.com` is not.
* **IPv4 only.** Certificate names are resolved to their first A record. A name that does not resolve is dropped.
* **Each source adds new addresses.** A source skips any IP an earlier source already reported.
* **Enrichment comes from datasets, not probes.** Every IP is looked up in Shodan's InternetDB for open ports and CVEs. `CrtSH`, `KaduuLeakCenter` and `ZoomEye` hosts also get reverse DNS and network registration (ASN, provider, country). `DNSDumpster` hosts carry DNSDumpster's own network data.

### Run a scan

**1. Start the scan**

[`create_infra_search_request`](https://client-api.leak.center/scalar-docs/#tag/infra-search/POST/api/service/create_infra_search_request/) takes one field, `search_term`: required, 1 to 255 characters.

```bash theme={"dark"}
curl https://client-api.leak.center/api/service/create_infra_search_request/ \
  -X POST \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"search_term": "acme.com"}'
```

> [!WARNING]
>
> **Send the bare root domain.** Use `acme.com`, not `https://www.acme.com`. Every source keeps only the term itself and the names under it, so a URL, a keyword or an IP address returns no hosts. The API does not check the term: any of these is accepted, uses a unit and finds nothing.

The call returns `201` with the scan:

```json theme={"dark"}
{
  "id": "3f6c1d2e-8a4b-4c7d-9e1f-2a3b4c5d6e7f",
  "search_term": "acme.com",
  "create_date": "2026-10-07T09:14:02.481203Z",
  "update_date": "2026-10-07T09:14:02.481203Z",
  "is_monitoring": false,
  "alert_cycle": "month",
  "is_email": false,
  "is_reporting": false,
  "status": "in_progress",
  "report_id": null
}
```

Keep the `id`. A scan you start here runs once. `is_monitoring`, `alert_cycle` and `is_email` are monitoring settings this endpoint does not set. `is_reporting` and `report_id` are filled only on scans that a report started.

**2. Poll until it ends**

Fetch the scan with [`get_infra_search_request`](https://client-api.leak.center/scalar-docs/#tag/infra-search/GET/api/service/get_infra_search_request/\{id}/), passing its `id` in the path, until `status` is no longer `in_progress`.

```bash theme={"dark"}
curl https://client-api.leak.center/api/service/get_infra_search_request/3f6c1d2e-8a4b-4c7d-9e1f-2a3b4c5d6e7f/ \
  -H "X-API-Key: YOUR_API_KEY"
```

`status` takes three values:

* **`in_progress`** — still running.
* **`finished`** — done. Read the hosts.
* **`failed`** — done, with an error. This is final: the platform does not retry it. It can still hold the hosts found before the failure. Start a new scan to try again, which uses another unit.

Poll every 10 seconds or slower. Each source saves its hosts as soon as it finishes, so the host list grows while the scan is `in_progress`.

> [!NOTE]
>
> **Every scan ends.** A scan stops itself 50 minutes after it starts, keeps the hosts found so far, and ends `finished`. The response does not mark a scan that hit this cap. A scan that stalls is set to `failed` 65 to 75 minutes after it starts. A scan that never starts is set to `failed` within 10 minutes of turning 24 hours old.

**3. Read the hosts**

[`list_infra_search_result`](https://client-api.leak.center/scalar-docs/#tag/infra-search/GET/api/service/list_infra_search_result/) returns the hosts of one scan. Pass the scan `id` as `searchrequest`.

```bash theme={"dark"}
curl -G https://client-api.leak.center/api/service/list_infra_search_result/ \
  -H "X-API-Key: YOUR_API_KEY" \
  --data-urlencode "searchrequest=3f6c1d2e-8a4b-4c7d-9e1f-2a3b4c5d6e7f" \
  --data-urlencode "page_size=1000"
```

* `searchrequest` is required and must be a UUID. Without it the call returns `400`.
* `page` is 1-based. `page_size` defaults to `100`, max `1000`.
* There is no other filter, no sort parameter and no guaranteed order. Page through the list after `status` leaves `in_progress`: paging a running scan can skip or repeat hosts.
* Page by raising `page` until `next` is `null`. The `next` and `previous` URLs point at the internal scanning service, not at `client-api.leak.center`, so don't call them.

```json theme={"dark"}
{
  "count": 2,
  "next": null,
  "previous": null,
  "results": [
    {
      "id": "9b2e7c41-5d3a-4f8e-a1c6-0d7e8f9a1b2c",
      "date_created": "2026-10-07",
      "domain": "vpn.acme.com",
      "ipv4": "203.0.113.24",
      "type": "CrtSH",
      "metadata": {
        "reverse_dns": "vpn.acme.com",
        "as": "ACME-AS, US",
        "provider": "ACME-NET",
        "country": "US",
        "header": "",
        "type": "dns"
      },
      "shodan_data": {
        "ip": "203.0.113.24",
        "ports": [443, 4443],
        "cpes": ["cpe:/a:acme:ssl_vpn"],
        "hostnames": ["vpn.acme.com"],
        "tags": [],
        "vulns": ["CVE-2026-0000"]
      },
      "searchrequest": "3f6c1d2e-8a4b-4c7d-9e1f-2a3b4c5d6e7f"
    },
    {
      "id": "c4d5e6f7-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
      "date_created": "2026-10-07",
      "domain": "mail.acme.com",
      "ipv4": "198.51.100.7",
      "type": "DNSDumpster",
      "metadata": {
        "reverse_dns": "mail.acme.com",
        "as": "64500 198.51.100.0/24",
        "provider": "ACME-NET",
        "country": "US",
        "header": "",
        "type": "mx"
      },
      "shodan_data": {},
      "searchrequest": "3f6c1d2e-8a4b-4c7d-9e1f-2a3b4c5d6e7f"
    }
  ]
}
```

### What each host gives you

* **`domain`** — the host name found.
* **`ipv4`** — the IPv4 address it resolves to. Can be empty.
* **`type`** — the source that found it. See [What a scan collects](#what-a-scan-collects).
* **`shodan_data`** — the Shodan InternetDB record for the IP: open `ports`, known `vulns` as CVE IDs, `cpes`, `hostnames` and `tags`. These are what Shodan last observed, not a live check. It is `{}` when Shodan has no record for the IP or the lookup failed.
* **`metadata`** — network details for the IP: `reverse_dns`, `as` (the AS number and range on `DNSDumpster` hosts, the AS description on other hosts), `provider` (the network owner) and `country`. `metadata.type` is the DNS record kind on `DNSDumpster` hosts: `dns` for an A record, `mx` for a mail server, `host` for a name server. On other hosts it is `dns` when the network lookup ran and empty when it was skipped, for example for a private IP. `header` is always empty.
* **`date_created`** — the day the host was saved.
* **`id`** — the result ID, for the single-host and delete calls.
* **`searchrequest`** — the scan the host belongs to.

To fetch one host again, call [`get_infra_search_result`](https://client-api.leak.center/scalar-docs/#tag/infra-search/GET/api/service/get_infra_search_result/\{id}/) with its result `id`, not the scan `id`.

### Summary statistics

[`get_infra_search_stats`](https://client-api.leak.center/scalar-docs/#tag/infra-search/GET/api/service/infra_search_stats/) returns three counts for one scan. Pass the scan `id` as `searchrequest`.

```bash theme={"dark"}
curl -G https://client-api.leak.center/api/service/infra_search_stats/ \
  -H "X-API-Key: YOUR_API_KEY" \
  --data-urlencode "searchrequest=3f6c1d2e-8a4b-4c7d-9e1f-2a3b4c5d6e7f"
```

```json theme={"dark"}
{
  "servers_with_unusual_ports": 1,
  "servers_with_reported_vulns": 1,
  "servers_with_unusual_names": 1
}
```

* **`servers_with_unusual_ports`** — hosts with at least one open port other than 25, 53, 80, 443, 465 and 587.
* **`servers_with_reported_vulns`** — hosts whose `shodan_data` lists at least one CVE.
* **`servers_with_unusual_names`** — hosts whose name contains a fragment that hints at admin, dev or test, database, internal, repository, monitoring or other sensitive use, such as `admin`, `staging`, `db`, `vpn`, `intranet`, `repo` or `zabbix`.

Counts are per result row, so one IP behind three host names counts three times and a host name with two IPs counts twice. Name matching is a case-insensitive substring match on the whole host name, your domain included. Short fragments such as `ci`, `db`, `server` and `static` also match inside longer words. If your domain itself contains a fragment, for example `ci` inside `financial`, every host counts.

A scan with no hosts returns `404` with `{"message": "No search results found for the given search request ID"}`, whether it is still running or finished with nothing found. Read it as all three counts being zero.

### List past scans

[`list_infra_search_request`](https://client-api.leak.center/scalar-docs/#tag/infra-search/GET/api/service/list_infra_search_request/) returns your organization's scans, newest first, including the ones started from Attack Surface Discovery in the dashboard.

```bash theme={"dark"}
curl -G https://client-api.leak.center/api/service/list_infra_search_request/ \
  -H "X-API-Key: YOUR_API_KEY" \
  --data-urlencode "page_size=100"
```

* `page` is 1-based. `page_size` defaults to `20`, max `100`.
* `ordering` takes `create_date`, `-create_date`, `update_date` or `-update_date`. The default is `-create_date`.
* Page the same way as the host list: raise `page` until `next` is `null`.

A scan with `is_reporting: true` was started by a report and did not use your Attack Surface Mapping allowance.

### Clean up

Both deletes are permanent and return `204`. Delete a scan only after it leaves `in_progress`. A scan deleted while running keeps running and is saved again when it ends, so it reappears in the scan list as `failed` or `finished` with no hosts.

* [`delete_infra_search_result`](https://client-api.leak.center/scalar-docs/#tag/infra-search/DELETE/api/service/delete_infra_search_result/\{id}/) removes one host. Statistics recount from the hosts that remain.
* [`delete_infra_search_request`](https://client-api.leak.center/scalar-docs/#tag/infra-search/DELETE/api/service/delete_infra_search_request/\{id}/) removes the scan and all of its hosts.

```bash theme={"dark"}
curl -X DELETE https://client-api.leak.center/api/service/delete_infra_search_request/3f6c1d2e-8a4b-4c7d-9e1f-2a3b4c5d6e7f/ \
  -H "X-API-Key: YOUR_API_KEY"
```

### Errors

| Status | When | Body |
| - | - | - |
| `400` | `search_term` is missing, blank or over 255 characters | `{"search_term": [...]}` with `This field is required.`, `This field may not be blank.` or `Ensure this field has no more than 255 characters.` |
| `400` | `searchrequest` is missing or not a UUID on `list_infra_search_result` | `{"searchrequest": [...]}` with `This field is required.` or `Must be a valid UUID.` |
| `400` | `searchrequest` is missing on `get_infra_search_stats` | `{"error": "searchrequest_id parameter is required"}` |
| `403` | Your plan lacks Attack Surface Mapping, or your license is not active | See [Availability and cost](#availability-and-cost) |
| `404` | No scan or host with that `id` | — |
| `404` | Statistics for a scan with no hosts | `{"message": "No search results found for the given search request ID"}` |
| `404` | `page` is past the last page on either list call | `{"detail": "Invalid page."}` |
| `429` | The monthly scan allowance is spent, or the request rate limit is hit | See [Availability and cost](#availability-and-cost) |
| `502` | The scanning service failed, or `searchrequest` is not a UUID on `get_infra_search_stats` | `{"detail": "The requested service is temporarily unavailable."}` |
| `504` | The scanning service timed out | `{"detail": "The requested service timed out."}` |

### Endpoints

| Action | Endpoint |
| - | - |
| Start a scan | [`POST /api/service/create_infra_search_request/`](https://client-api.leak.center/scalar-docs/#tag/infra-search/POST/api/service/create_infra_search_request/) |
| Get a scan and its status | [`GET /api/service/get_infra_search_request/{id}/`](https://client-api.leak.center/scalar-docs/#tag/infra-search/GET/api/service/get_infra_search_request/\{id}/) |
| List scans | [`GET /api/service/list_infra_search_request/`](https://client-api.leak.center/scalar-docs/#tag/infra-search/GET/api/service/list_infra_search_request/) |
| Delete a scan and its hosts | [`DELETE /api/service/delete_infra_search_request/{id}/`](https://client-api.leak.center/scalar-docs/#tag/infra-search/DELETE/api/service/delete_infra_search_request/\{id}/) |
| List a scan's hosts | [`GET /api/service/list_infra_search_result/`](https://client-api.leak.center/scalar-docs/#tag/infra-search/GET/api/service/list_infra_search_result/) |
| Get one host | [`GET /api/service/get_infra_search_result/{id}/`](https://client-api.leak.center/scalar-docs/#tag/infra-search/GET/api/service/get_infra_search_result/\{id}/) |
| Delete one host | [`DELETE /api/service/delete_infra_search_result/{id}/`](https://client-api.leak.center/scalar-docs/#tag/infra-search/DELETE/api/service/delete_infra_search_result/\{id}/) |
| Get summary statistics | [`GET /api/service/infra_search_stats/`](https://client-api.leak.center/scalar-docs/#tag/infra-search/GET/api/service/infra_search_stats/) |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.