> ## 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.

# Follow an attack surface assessment

> Poll an assessment from Queued to Completed: statuses, progress phases, the queue, the assessment list and how long an assessment takes.

An assessment runs in the background for minutes to hours after you [start it](https://wiki.darknetsearch.com/api/guides/attack-surface-assessments.md). Poll its progress until `status` is `Completed`. About 3 minutes later its automatic report starts generating when the assessment has data for it, and you track the report from there.

**Availability:** Attack Surface Reports or Targeted Attack Surface Reports. Either one opens the list, progress and queue, for full and targeted assessments alike.

**Cost:** no metered allowance. Each poll is one API call: it counts toward your per-second [rate limit](https://wiki.darknetsearch.com/api/guides/rate-limits.md) and, with an API key, toward your monthly API Calls allowance when it returns `2xx`. Without either plan feature, or when your license is not in force, these endpoints return `403` with `{"detail": "This feature is not included in your plan."}`.

### Assessment statuses

| `status` | Meaning | What to do |
| - | - | - |
| `Queued` | Waiting for a free slot. `queue_reason` says why. | Keep polling. See [Read the queue](#read-the-queue). |
| `Running` | Discovering and testing hosts. | Keep polling. |
| `Finalizing` | Discovery is done, and slower modules are still running, such as the active vulnerability check, look-alike domains and executive exposure. Most report requests are refused with `409` until `Completed`. The Domain Spoofing Risk Report can be requested once look-alike analysis has finished. | Keep polling. |
| `Completed` | Done. The automatic report starts generating about 3 minutes later, when the assessment has data for it. | Stop polling and [track the report](https://wiki.darknetsearch.com/api/guides/attack-surface-reports.md). |
| `Cancelled` | Stopped before it finished. The results found until then are kept. | Stop polling. The API cannot cancel an assessment. |
| `Failed` | Interrupted by a platform deployment. `queue_reason` is `deploy_interrupted`. The platform does not retry it. | Start a new assessment. It uses another unit. |

`Completed`, `Cancelled` and `Failed` are final.

### Poll progress

[`GET /api/hackerview/scans/{scan_id}/progress/`](https://client-api.leak.center/scalar-docs/#tag/attack-surface/GET/api/hackerview/scans/\{scan_id}/progress/) returns the status, every phase, a summary and the queue state. Pass the `uid` from the start response as `scan_id`.

```bash theme={"dark"}
curl https://client-api.leak.center/api/hackerview/scans/6b1f0c2e-4d3a-4f5b-9c8d-7e6f5a4b3c2d/progress/ \
  -H "X-API-Key: YOUR_API_KEY"
```

This example is shortened. The response lists every phase, and every source within a phase:

```json theme={"dark"}
{
  "uid": "6b1f0c2e-4d3a-4f5b-9c8d-7e6f5a4b3c2d",
  "status": "Running",
  "domain": "acme.com",
  "created_at": "2026-10-08T09:14:02.481203Z",
  "elapsed_seconds": 412.6,
  "phases": [
    {
      "name": "resolve_ip",
      "status": "completed",
      "started_at": "2026-10-08T09:14:05.112904Z",
      "completed_at": "2026-10-08T09:14:05.598311Z",
      "duration_seconds": 0.49
    },
    {
      "name": "subdomain_discovery",
      "status": "running",
      "started_at": "2026-10-08T09:14:06.004417Z",
      "completed_at": null,
      "duration_seconds": null,
      "modules": [
        {
          "name": "crt",
          "status": "completed",
          "result_count": 41,
          "started_at": "2026-10-08T09:14:06.210833Z",
          "completed_at": "2026-10-08T09:14:19.734120Z",
          "duration_seconds": 13.52
        },
        {
          "name": "dns_bruteforce",
          "status": "running",
          "result_count": 0,
          "started_at": "2026-10-08T09:14:06.402251Z",
          "completed_at": null,
          "duration_seconds": null
        }
      ],
      "total_results": 41,
      "modules_completed": 1,
      "modules_total": 2
    },
    {
      "name": "domain_enrichment",
      "status": "running",
      "domains_total": 58,
      "domains_resolved": 31,
      "domains_unresolved": 27,
      "lookups_completed": 31,
      "lookups_total": 58,
      "lookups_pending": 27
    }
  ],
  "summary": {
    "total_domains_found": 58,
    "lookups_completed": 31,
    "lookups_pending": 27,
    "lookups_failed": 0,
    "modules_completed": 9,
    "modules_failed": 0,
    "modules_running": 6,
    "modules_pending": 2
  },
  "queue": {
    "waiting": false,
    "queue_position": null,
    "queue_length": 0,
    "queue_reason": null,
    "max_concurrent_scans": 5,
    "global_running": 3,
    "dispatch_policy": "fair_share"
  }
}
```

* **`status`** — the assessment status. It reads `Running` while any module is still running, even after the assessment list already shows `Completed`. Wait for `Completed` here before you request a report yourself.
* **`elapsed_seconds`** — seconds since the assessment was created, queue time included. It keeps counting after the assessment ends. For the run time of a finished assessment, read `elapsed_time_seconds` from the [list](#list-assessments).
* **`phases`** — one entry per phase, in the order below.
* **`summary`** — counts for a progress bar. `total_domains_found` is the hosts discovered so far and `lookups_completed` the hosts resolved to an IP address. The `modules_*` counts cover every phase, source and module, the always-on checks included. `lookups_failed` is always `0`.
* **`queue`** — the same block the [queue endpoint](#read-the-queue) returns.

Poll every 30 seconds. An assessment can run for hours, and a 2-hour assessment polled every 30 seconds uses 240 API calls. Stop when `status` is `Completed`, `Cancelled` or `Failed`.

#### Phases

Each phase has `name`, `status`, `started_at`, `completed_at` and `duration_seconds`. A phase that collects from several sources adds `modules`, one entry per source, with `total_results`, `modules_completed` and `modules_total`. A skipped phase adds `reason`. Phase and module statuses are `pending`, `running`, `completed`, `failed` or `skipped`. A failed module adds `error`.

| Phase | What it does |
| - | - |
| `resolve_ip` | Resolves the domain to its IP address. |
| `whois` | Reads the domain's WHOIS record. |
| `subdomain_discovery` | Collects subdomains from passive sources in parallel, one `modules` entry per source. With `active_dns_enumeration`, the 5,000-name DNS test runs here as `dns_bruteforce`. |
| `same_ip_domains` | Finds other domains on the same IP addresses. Runs only with `domain_mode` set to `all`, and is skipped otherwise. |
| `reverse_whois` | Skipped. |
| `reverse_dns` | Skipped. |
| `domain_enrichment` | Resolves and enriches each discovered host. Instead of timings it reports host counts: `domains_total`, `domains_resolved`, `domains_unresolved` and `lookups_completed`, `lookups_total`, `lookups_pending`. Its `modules` list holds the enrichment steps and every other module, such as `spoofguard` for look-alike domains and the always-on checks. |
| `ip_range_discovery` | Looks up the IP ranges the organization may hold. Runs once host enrichment has settled. |
| `ip_range_validation` | Grades each candidate range from sampled reverse-DNS and certificate checks. |
| `vuln_verification` | Present only when `active_vuln_scan` is on. The read-only verification pass, run after discovery. Adds `hosts_total`, `hosts_scanned`, `findings_count`, `httpx_live_urls`, `current_step`, `current_category` and `step_elapsed_seconds`. |

Modules such as look-alike domains, employee emails and the always-on checks run alongside these phases. Follow them in the `modules` list of `domain_enrichment` and in the `summary` counts.

### Read the queue

Your organization runs one assessment at a time. While it runs, further starts wait as `Queued`, up to 10 open assessments. The platform also caps how many assessments run at once across all organizations. When a slot opens, queued assessments start in fair-share order: one from each waiting organization in turn, oldest first within each organization. A `Finalizing` assessment does not hold your slot, so your next queued one can start while it finishes.

[`GET /api/hackerview/scans/{scan_id}/queue/`](https://client-api.leak.center/scalar-docs/#tag/attack-surface/GET/api/hackerview/scans/\{scan_id}/queue/) returns the queue state on its own. Progress already includes it under `queue`, so you need this call only when you want nothing else.

```bash theme={"dark"}
curl https://client-api.leak.center/api/hackerview/scans/6b1f0c2e-4d3a-4f5b-9c8d-7e6f5a4b3c2d/queue/ \
  -H "X-API-Key: YOUR_API_KEY"
```

```json theme={"dark"}
{
  "waiting": true,
  "queue_position": 2,
  "queue_length": 4,
  "queue_reason": "org_concurrency_limit",
  "max_concurrent_scans": 5,
  "global_running": 3,
  "dispatch_policy": "fair_share"
}
```

* **`waiting`** — `true` while the assessment is `Queued`.
* **`queue_position`** — the assessment's place in the platform-wide fair-share order, starting at 1. `null` unless the assessment is `Queued`.
* **`queue_length`** — queued assessments across the platform.
* **`queue_reason`** — `org_concurrency_limit` when your organization already has one running, `global_capacity` when the platform is full. `deploy_interrupted` on a `Failed` assessment. `null` otherwise.
* **`max_concurrent_scans`** and **`global_running`** — how many assessments the platform runs at once, and how many are running now.
* **`dispatch_policy`** — always `fair_share`.

### List assessments

[`GET /api/hackerview/scans/`](https://client-api.leak.center/scalar-docs/#tag/attack-surface/GET/api/hackerview/scans/) returns your organization's assessments, newest first, including the ones started in the dashboard.

```bash theme={"dark"}
curl -G https://client-api.leak.center/api/hackerview/scans/ \
  -H "X-API-Key: YOUR_API_KEY" \
  --data-urlencode "domain=acme" \
  --data-urlencode "page_size=50"
```

| Parameter | What it does |
| - | - |
| `page` | Page number, starting at 1. A page past the last returns `404` with `{"detail": "Invalid page."}`. |
| `page_size` | Results per page. Defaults to `10`, max `100`. |
| `domain` | Keeps assessments whose domain contains this text, case-insensitive. `acme` matches `acme.com`. |
| `date_sort` | `asc` for oldest first. Any other value, or none, gives newest first. |

Page by raising `page` until `next` is `null`. The `next` and `previous` URLs point at the internal assessment service, not at `client-api.leak.center`, so don't call them.

```json theme={"dark"}
{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "uid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
      "status": "Completed",
      "domain": "acme.com",
      "original_domain": "",
      "scan_domains": ["acme.com"],
      "ip": "203.0.113.10",
      "subdomain_lookups_completed": true,
      "same_ip_lookups_completed": true,
      "ip_range_lookups_completed": true,
      "reverse_whois_lookups_completed": true,
      "created_at": "2026-10-07T13:02:44.118020Z",
      "updated_at": "2026-10-07T14:09:31.507712Z",
      "completed_at": "2026-10-07T14:08:58.902114Z",
      "elapsed_time_seconds": 3974,
      "servers_with_unusual_ports_count": 3,
      "servers_with_vulns_count": 2,
      "email_domain_info": {
        "email_domains": ["acme.com"],
        "has_separate_email_domain": false
      },
      "params": {
        "domain": "acme.com",
        "domain_mode": "default",
        "auto_generate_report": false,
        "auto_report_types": ["vip_executive_risk"],
        "vip_enumeration": true,
        "email_enumeration": true,
        "vip_skip_email_guessing": true,
        "perform_port_scan": false,
        "report_language": "en",
        "active_scope_namespaces": ["acme.com"]
      }
    }
  ]
}
```

* **`domain`** — the domain assessed. With `swap_to_primary` it can differ from the one you sent, which is then in `original_domain`.
* **`scan_domains`** — the domains the assessment covers, when domain detection ran.
* **`ip`** — the IP address the domain resolved to.
* **`completed_at`** and **`elapsed_time_seconds`** — when the assessment ended, and its run time in seconds from creation. Both are `null` while it runs, and stay `null` for a `Cancelled` assessment.
* **`servers_with_unusual_ports_count`** and **`servers_with_vulns_count`** — hosts with unusual open ports and hosts with known vulnerabilities, counted when the assessment finishes.
* **`email_domain_info`** — what domain detection found about the organization's email domains.
* **`params`** — the options the assessment was started with. This example is shortened: `params` holds every module flag, `domain_mode` and the report options. `active_scope_namespaces` is the final [active scope](https://wiki.darknetsearch.com/api/guides/attack-surface-assessments.md#active-scope).

A targeted assessment has `auto_generate_report` set to `false` and its `report_type` as the only entry of `auto_report_types`. A full assessment has `auto_generate_report` set to `true` and an empty `auto_report_types`.

### How long it takes

Time depends on the modules, the domain mode and the size of the domain. With every module flag off, an assessment typically takes 5 to 15 minutes. With every module on and `domain_mode` set to `all`, plan for 2 to 4 hours. These modules take longest:

| Module | Typical time |
| - | - |
| Executive exposure (`vip_enumeration`) | 15 to 30 minutes |
| Look-alike domains (`dns_typosquatting_enabled`) | Up to 30 minutes |
| Subdomain discovery (`active_dns_enumeration`) | About 7 minutes |
| Leaked code & secrets (`code_exposure_enabled`) | 2 to 5 minutes |

Queue time comes on top. Every assessment ends, whatever happens:

* **No progress for 60 minutes:** discovery stops, and the assessment finishes with the results found so far. The limit is 120 minutes with `dual_domain`, or with `all` when it covers more than one domain.
* **4 hours after it was created:** a `Running` assessment stops discovery and finishes with what it has. The limit is 8 hours with `dual_domain`, or with `all` when it covers more than one domain.
* **4 hours in `Finalizing`:** the assessment is set to `Completed`, and the modules still running are marked `failed`.

A cut-short assessment still ends `Completed`, and its report covers what was found.

### Errors

| Status | When | Body |
| - | - | - |
| `403` | Your plan lacks both Attack Surface features, or your license is not in force | `{"detail": "This feature is not included in your plan."}` |
| `404` | No assessment with that `uid` in your organization | `{"details": "Scan not found."}` |
| `404` | `scan_id` is not a UUID | A non-JSON page |
| `404` | `page` is past the last page of the list | `{"detail": "Invalid page."}` |
| `429` | The per-second rate limit or the monthly API Calls allowance is hit | `message` reads `Rate limit exceeded`. See [Rate limits](https://wiki.darknetsearch.com/api/guides/rate-limits.md). |
| `502` | The assessment engine is unavailable | `{"detail": "The requested service is temporarily unavailable."}` |
| `504` | The assessment engine did not answer in time | `{"detail": "The requested service timed out."}` |

[Handle API errors](https://wiki.darknetsearch.com/api/guides/errors.md) covers each status in more detail.

### Endpoints

| Action | Endpoint |
| - | - |
| Get an assessment's progress | [`GET /api/hackerview/scans/{scan_id}/progress/`](https://client-api.leak.center/scalar-docs/#tag/attack-surface/GET/api/hackerview/scans/\{scan_id}/progress/) |
| Get an assessment's queue position | [`GET /api/hackerview/scans/{scan_id}/queue/`](https://client-api.leak.center/scalar-docs/#tag/attack-surface/GET/api/hackerview/scans/\{scan_id}/queue/) |
| List assessments | [`GET /api/hackerview/scans/`](https://client-api.leak.center/scalar-docs/#tag/attack-surface/GET/api/hackerview/scans/) |
| List reports | [`GET /api/hackerview/reports/`](https://client-api.leak.center/scalar-docs/#tag/attack-surface/GET/api/hackerview/reports/) |


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