# Sublist3r Subdomain Finder & Takeover Scanner (`datacach/sublist3r-subdomain-finder`) Actor

Subdomain enumeration from passive OSINT sources — certificate transparency logs, passive DNS and public APIs — with optional active HTTP/TLS fingerprinting of every live host, scored for takeover risk. Run it only against domains you are authorized to assess.

- **URL**: https://apify.com/datacach/sublist3r-subdomain-finder.md
- **Developed by:** [DataCach](https://apify.com/datacach) (community)
- **Categories:** Developer tools, Automation, Other
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 subdomains

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Sublist3r Subdomain Finder & Takeover Scanner

Find every subdomain of a domain from passive OSINT sources, then probe each live host for subdomain takeovers, expired TLS certificates and exposed non-production environments. Export to JSON, CSV or Excel.

<!-- TODO: add a demo GIF or YouTube URL on its own line here — videos rank the store page higher -->

### What is Sublist3r Subdomain Finder?

**Sublist3r Subdomain Finder & Takeover Scanner discovers a domain's subdomains from passive OSINT sources, then probes each one to surface what actually needs attention: possible subdomain takeovers, expired or self-signed TLS certificates, exposed admin and CI consoles, and non-production environments reachable from the public internet.** It queries **certificate transparency logs**, **passive DNS** databases and public DNS aggregators, merges everything into one **deduplicated list**, resolves and fingerprints each live host over **HTTP and TLS**, and ranks every row with a **0-100 triage score** so the findings that matter surface first.

You give it one root domain — `example.com` — and it returns one **JSON** row per subdomain, ready to **export** as JSON, CSV, Excel or HTML.

Discovery is passive, but the fingerprinting stage is not: it contacts each live host directly, so **only run it against domains you are authorized to assess**. There is no port scanning and no name brute-forcing — see [Is it legal to scan subdomains?](#is-it-legal-to-scan-a-domains-subdomains) below.

It is a modernized port of the classic [Sublist3r](https://github.com/aboul3la/Sublist3r) tool, unmaintained since 2019. Several original engines no longer exist (ThreatCrowd shut down, ptrarchive.com is gone, DNSdumpster moved behind a paid API) and the search-engine scrapers are now blocked. This Actor keeps the sources that still work, adds replacements for the ones that died, and runs them concurrently on the Apify platform.

Try it by entering a domain in the **Input** tab and hitting **Start**.

### What can this subdomain enumeration tool do?

- 🔍 **Enumerate subdomains from 11 sources** — crt.sh, Cert Spotter, AlienVault OTX, HackerTarget, RapidDNS, VirusTotal, plus five search engines, queried concurrently and merged into one deduplicated list.
- 🌐 **Resolve DNS for every result** — A, AAAA and CNAME records through resolvers you choose, with wildcard-DNS detection so you know which hits are real.
- 🎯 **Score subdomain takeover risk** — dangling CNAMEs are matched against known third-party services and confirmed against the response body before anything is called `high` risk.
- 🔐 **Audit TLS certificates** — issuer, SANs, expiry countdown, plus expired, self-signed and hostname-mismatch flags.
- 🖥️ **Fingerprint live hosts** — HTTP status, redirect chain, page title, server header, tech stack, security headers and a Shodan-convention favicon hash for pivoting.
- 🏢 **Attribute network ownership** — ASN, ASN organisation, country and inferred hosting provider for each resolved IP.
- 📊 **Triage automatically** — a 0-100 `interestingnessScore` and machine-readable `findings` labels put what a human should look at first at the top of the table.
- 📁 **Export anywhere** — download as **JSON, CSV, Excel or HTML**, or pull results straight from the **Apify API**. A plain-text subdomain list is written per domain, matching Sublist3r's `-o` output format.
- ⚡ **Run as a live API** — Standby mode answers `GET /?domain=example.com` synchronously, without paying container startup per lookup.
- 🔄 **Use the Apify platform** — **scheduling** for recurring scans, **monitoring** and alerts, **proxy rotation** via Apify Proxy, and **integrations** with Zapier, Make, Slack, Google Sheets, webhooks and more.

### What data does the subdomain scanner extract?

Every discovered subdomain becomes one dataset row with roughly 43 fields. The main ones:

| Field | Type | Description |
|---|---|---|
| `subdomain` | string | The discovered hostname |
| `rootDomain` | string | The input domain it belongs to |
| `sources` | array | Every source that reported this host |
| `resolves` | boolean | Whether it currently resolves (`null` if DNS resolution was off) |
| `ipv4` / `ipv6` | array | Resolved addresses |
| `cname` | array | CNAME targets — the field to check for subdomain takeover |
| `isWildcardSuspect` | boolean | True when the host only resolves to the domain's wildcard addresses |
| `httpStatus` / `httpScheme` / `finalUrl` | — | Result of probing the host, following redirects |
| `pageTitle` / `serverHeader` / `techStack` | — | Fingerprinted from response headers and the first 50 KB of the body |
| `securityHeaders` | object | Presence of HSTS, CSP, X-Frame-Options and X-Content-Type-Options |
| `faviconHash` | integer | mmh3 hash of the favicon (Shodan convention), for pivoting to sibling infrastructure |
| `tlsIssuer` / `tlsSans` / `tlsNotAfter` / `tlsDaysUntilExpiry` | — | Certificate detail from the TLS handshake |
| `tlsIsExpired` / `tlsSelfSigned` / `tlsHostnameMismatch` | boolean | Certificate red flags |
| `asn` / `asnOrg` / `ipCountry` / `provider` / `providerType` | — | Network ownership of the resolved IP |
| `takeoverRisk` / `takeoverService` / `takeoverEvidence` | — | Takeover assessment — see below |
| `tags` / `isNonProd` | — | Keywords detected in the hostname, e.g. `dev`, `admin`, `jenkins` |
| `findings` / `interestingnessScore` | — | Triage output — see below |
| `depth` | integer | Number of labels, e.g. `api.example.com` is 3 |
| `discoveredAt` | string | ISO-8601 timestamp of the run |
| `extraction_datetime` | string | UTC timestamp of the moment this row was extracted, ISO-8601 |
| `extraction_date` | string | The same instant as a date in `mm-dd-yyyy` format |

Alongside the dataset, the key-value store holds per domain:

- `report-<domain>.json` — how many subdomains each source contributed, which sources failed or were skipped, wildcard verdict, run duration, enrichment errors, and how many hosts went unprobed if the time budget ran out (`notProbed`).
- `subdomains-<domain>.txt` — a newline-separated list, the same format as Sublist3r's `-o` flag.

### How do I find all subdomains of a domain?

1. Open the Actor and go to the **Input** tab.
2. Enter one or more root domains, e.g. `example.com`. Pasted URLs, ports and leading wildcards are cleaned up automatically, so `https://example.com/path` and `*.example.com` both work. Maximum **20 domains per run**.
3. Optionally pick which **sources** to query — the defaults are the ones that work without credentials.
4. Leave **Resolve DNS** on if you want IP addresses, CNAMEs and the full HTTP/TLS enrichment. Leave **Include unresolved subdomains** on to keep historical hosts.
5. Click **Start**.
6. Read the results in the **Output** tab — start with the **Findings** view, which sorts by score — or download them as **JSON, CSV, Excel or HTML**.

<!-- TODO: screenshot of the Input tab — save as subdomain-finder-input.png with alt text "Sublist3r Subdomain Finder input form on Apify" -->

### How does subdomain takeover detection work?

Every dataset row is scored and annotated so you don't have to eyeball 43 columns per host. The **Findings** view (the default in the Output tab) sorts by score, descending, and shows only the columns that matter for triage.

#### What do the takeover risk levels mean?

| Value | Meaning |
|---|---|
| `none` | No CNAME points at a known third-party service. No evidence of takeover. |
| `low` | Reserved for future, weaker signals. Not currently emitted. |
| `medium` | The CNAME points at a known third-party service (e.g. an S3 bucket, a GitHub Pages domain), but the response body carries no confirmation that the resource is actually gone. **Verify manually before reporting this to anyone** — the CNAME alone is not proof. |
| `high` | Double confirmation: the CNAME points at a known third-party service **and** the response body contains that service's specific "resource not found" signature. This is the bar the Actor uses before calling something a likely takeover. |

The reasoning behind the split: a `high` finding pasted into a bug bounty report needs to hold up, and a false positive costs the reporter credibility. Requiring two independent signals — CNAME plus response fingerprint — keeps `high` results actionable without you re-verifying them by hand. `medium` still surfaces the lead (a stale CNAME is worth knowing about even without confirmation) but is explicitly a hint, not a verdict. `takeoverEvidence` explains exactly what matched, including the literal substring found in the response for `high` results, so you can check it against the live page yourself.

#### How is the interestingness score calculated?

`interestingnessScore` (0-100) is a triage heuristic, not a risk model — its only job is to put what a human would want to look at first at the top of the table. The weights are constants, not scattered magic numbers, so the ordering is auditable:

| Signal | Points |
|---|---|
| Takeover risk `high` | 40 |
| Takeover risk `medium` | 20 |
| Certificate expired | 25 |
| Certificate expiring within 30 days | 10 |
| Self-signed certificate | 15 |
| Certificate hostname mismatch | 10 |
| Host responds with HTTP 200 | 10 |
| High-value tag present (`admin`, `jenkins`, `grafana`) | 15 |
| Non-production tag present (`dev`, `staging`, `test`, `legacy`) | 15 |
| Missing security headers (HSTS, CSP, X-Frame-Options, X-Content-Type-Options) | 5 |

Points from expired-certificate and expiring-soon-certificate never both apply — only the worse of the two counts — and the total is capped at 100. `findings` lists the same signals as short machine-readable labels (`possible-takeover`, `expired-tls`, `self-signed-cert`, `hostname-mismatch`, `non-prod-exposed`, `missing-hsts`) so you can filter or alert on them downstream without re-deriving them from the raw fields.

### Input

Set inputs in the **Input** tab or pass them to the **API**. Only `domains` is required — everything else has a sensible default.

| Field | Type | Default | Description |
|---|---|---|---|
| `domains` | array | — | **Required.** Root domains to enumerate (max 20 per run; **free plan: 1**) |
| `sources` | array | crt.sh, Cert Spotter, OTX, HackerTarget, RapidDNS | Which sources to query |
| `resolveDns` | boolean | `true` | Resolve A/AAAA/CNAME and run HTTP/TLS enrichment for every result |
| `includeUnresolved` | boolean | `true` | Keep subdomains that no longer resolve |
| `dnsResolvers` | array | `1.1.1.1`, `8.8.8.8` | Resolvers used for lookups |
| `maxSubdomainsPerDomain` | integer | `0` | Cap results per domain (0 = unlimited; **free plan: 50**) |
| `virusTotalApiKey` | string | — | Enables the VirusTotal source |
| `otxApiKey` | string | — | Upgrades OTX to authenticated passive DNS |
| `proxyConfiguration` | object | disabled | Optional Apify Proxy for outbound requests |
| `maxConcurrentDomains` | integer | `3` | Domains enumerated at once (max 10) |
| `requestTimeoutSecs` | integer | `30` | Per-request timeout |
| `maxRequestRetries` | integer | `3` | Retries per source, with exponential backoff |
| `concurrency` | integer | `10` | Concurrent requests (max 50) |
| `probeTimeoutSecs` | integer | `8` | How long to wait for each host to answer the HTTP/TLS probe. Kept short because a hanging host, not a slow one, is the dominant failure mode |
| `probeConcurrency` | integer | `25` | How many hosts to probe at once, per domain (max 30). Each concurrent domain gets its own pool of this size |
| `maxProbeSeconds` | integer | `900` | Hard time budget for the probing stage, per domain. When it runs out, that domain's remaining hosts are reported with empty enrichment fields, and the run report's `notProbed` field says how many were skipped (**free plan: 180**) |

```json
{
    "domains": ["apify.com"],
    "sources": ["crtsh", "certspotter", "otx", "hackertarget", "rapiddns"],
    "resolveDns": true,
    "includeUnresolved": true
}
````

### What's the difference between the free and paid plans?

**Every feature works on the free plan** — all 11 sources, DNS resolution, HTTP and TLS fingerprinting, subdomain takeover detection, ASN attribution and the triage score. Nothing is switched off. The free plan limits **how much** one run enumerates, not **what** it can find, so a free run shows you exactly what the Actor does on a real target.

| | 🆓 Free plan | ⭐ Paid plan |
|---|---|---|
| **Root domains per run** | 1 | Up to 20 |
| **Subdomains per domain** | 50 | Unlimited (or your own cap) |
| **Probe time budget per domain** | 180 seconds | Up to 3,000 seconds |
| Sources available | All 11 | All 11 |
| DNS resolution & wildcard detection | ✅ | ✅ |
| HTTP fingerprinting & tech stack | ✅ | ✅ |
| TLS certificate audit | ✅ | ✅ |
| Subdomain takeover detection | ✅ | ✅ |
| ASN & hosting attribution | ✅ | ✅ |
| Triage score & findings labels | ✅ | ✅ |
| JSON / CSV / Excel / HTML export | ✅ | ✅ |
| API, scheduling & integrations | ✅ | ✅ |
| Standby mode (live API) | ✅ (same caps) | ✅ |

The limits apply at runtime, so you can leave the inputs at their defaults either way — a free run clamps the values and logs a warning saying so, rather than failing. If you pass three domains on a free plan, the first is enumerated and the other two are skipped with a note in the log.

**Why these particular caps:** discovery is cheap and roughly fixed per domain, but enrichment costs a DNS resolution, an HTTP probe, a TLS handshake and two ASN lookups for every live host. The subdomain cap is what actually bounds a run — a single large target can yield tens of thousands of hosts. Upgrading lifts all three ceilings.

### Output example

Each discovered subdomain is one dataset row. You can download the dataset in various formats such as **JSON, HTML, CSV, or Excel**.

```json
{
    "subdomain": "example.com",
    "rootDomain": "example.com",
    "sources": ["crtsh"],
    "resolves": true,
    "ipv4": ["104.20.23.154", "172.66.147.243"],
    "cname": [],
    "httpStatus": 200,
    "httpScheme": "https",
    "finalUrl": "https://example.com/",
    "pageTitle": "Example Domain",
    "serverHeader": "cloudflare",
    "techStack": ["Cloudflare"],
    "tlsIssuer": "Cloudflare TLS Issuing ECC CA 3",
    "tlsNotAfter": "2026-08-29T21:41:26Z",
    "tlsDaysUntilExpiry": 33,
    "tlsIsExpired": false,
    "asn": 13335,
    "asnOrg": "CLOUDFLARENET - Cloudflare, Inc., US",
    "provider": "Cloudflare",
    "providerType": "cdn",
    "takeoverRisk": "none",
    "tags": [],
    "isNonProd": false,
    "findings": ["missing-hsts"],
    "interestingnessScore": 15,
    "depth": 2,
    "discoveredAt": "2026-07-27T06:10:54Z",
    "extraction_datetime": "2026-07-27T06:10:54.812394+00:00",
    "extraction_date": "07-27-2026"
}
```

This is a trimmed excerpt of one real dataset row — a live host produces roughly 43 fields in total, covering DNS, HTTP, TLS, network ownership and triage.

### Use cases

- 🛡️ **Attack surface management** — map your own external footprint and find forgotten staging, admin and legacy hosts before someone else does.
- 💰 **Bug bounty hunting and reconnaissance** — surface takeover candidates and misconfigured hosts across an in-scope domain, with evidence attached to every finding.
- 🔎 **Penetration testing** — build the target host list for an authorized engagement in minutes instead of by hand.
- 📜 **TLS certificate hygiene** — find expired, self-signed and hostname-mismatched certificates across an estate that nobody has inventoried.
- 🏗️ **Shadow IT discovery** — catch dev, test and staging environments that were never meant to be publicly reachable.
- 🤝 **Vendor security and M\&A due diligence** — get a quick picture of an organization's public footprint without touching their systems (set `resolveDns` to `false` for a fully passive run).
- 📈 **Continuous monitoring** — schedule a daily run and diff the dataset to catch new infrastructure the moment it appears.
- 🔗 **Recon pipeline input** — one row per subdomain plus a ready-made plain-text list means results pipe straight into `httpx`, `nuclei` or your own tooling.

### Tips and advanced options

- **Start with the defaults.** crt.sh, Cert Spotter, OTX, HackerTarget and RapidDNS need no credentials and cover most of what is findable passively.
- **Add API keys for depth.** A VirusTotal key enables that source entirely; an OTX key upgrades OTX from its public URL corpus to full passive DNS.
- **Sources fail independently.** crt.sh returns 502s and Cert Spotter's free tier goes down under load fairly often. A failing source is logged as a warning and recorded in the run report — it never fails the run. Check `countsBySource` in the report to see what actually contributed.
- **Search engines need a residential proxy.** Google, Yahoo, Bing, Baidu and Ask are included for parity with the original Sublist3r, but they now serve CAPTCHAs and JavaScript-only pages to cloud IPs and return nothing without a residential proxy. They are off by default for that reason.
- **Size your run from real numbers.** Discovery is fast and roughly fixed per domain; the enrichment stage scales with the number of *live* hosts, so one target with 8,000 resolving hosts is far more work than ten targets with 50 each. Leave `maxProbeSeconds` at its default for the first run against an unfamiliar target, then read `notProbed` and `durationSecs` in `report-<domain>.json` and size the second run from the actual figures.
- **Cap the result set when enumerating several large domains at once.** Every discovered subdomain is held in memory until its domain finishes writing, and an enriched record costs roughly 2-5 KB. Ten domains of 30,000 subdomains each is on the order of 900 MB — past what the container has. `maxSubdomainsPerDomain` is the knob for this, and it is deliberately unlimited by default so nothing is hidden from you without asking.
- **Watch the wildcard flag.** If a domain has a `*` DNS record, hosts resolving only to those addresses are flagged `isWildcardSuspect` — treat them as unconfirmed.
- **Keep unresolved results.** Hosts that no longer resolve are often the most interesting findings.
- **Turn on the proxy if probing comes back mostly empty.** The probing stage opens hundreds of concurrent connections from the same container IP. A WAF in front of the target can start blocking that IP mid-run, which shows up as a wave of hosts with null `httpStatus` even though they resolve fine. Enabling **Apify Proxy** routes each probe through a different IP and avoids that pattern.
- **Favicon hashes skip oversized icons.** `faviconHash` is only computed for icons under 200 KB; larger multi-resolution `.ico` files come back `null` while everything else about the host is reported normally. The same memory budget truncates each probed page's body at 50 KB, which is why `pageTitle` and `techStack` are derived from the top of the document rather than all of it.
- **Standby mode.** The Actor also runs as a live API: `GET /?domain=example.com` returns JSON synchronously without paying container startup per lookup.

### Other OSINT Actors you might like

Subdomain enumeration maps an organization's **infrastructure**. These companion OSINT Actors map the **people and identities** around it — useful when an investigation, red-team engagement or due-diligence review needs both sides of the picture.

| Actor | What it does |
|---|---|
| [Holehe Email OSINT — Email Finder & Digital Footprint Mapper](https://apify.com/datacach/holehe-email-osint-scraper) | Scans 120+ platforms to find which services have an account registered to a target email address, returning platform names, detection methods and masked recovery data. |
| [PhoneInfoga — Phone Number OSINT Scanner](https://apify.com/datacach/phoneinfoga-phone-number-osint-scanner) | Runs reverse lookups on international phone numbers, returning carrier, country of origin, line type and public OSINT footprints from Numverify, Google and OVH Telecom. |
| [Maigret Username OSINT Search](https://apify.com/datacach/maigret-username-osint) | Searches a username across 3,000+ social networks and websites, returning profile URLs, account status and extracted names, bios, avatars and locations. |

Browse [Apify Store](https://apify.com/store) for more scraping and automation tools.

### FAQ

#### Is it legal to scan a domain's subdomains?

**Read this before pointing it at a domain you do not own.** Discovery is passive — it reads public, third-party OSINT databases and sends nothing to the target. **The enrichment stage is not passive.** With `resolveDns` on, which is the default, every subdomain that resolves receives live HTTP requests from an Apify IP address — HTTPS first with HTTP as a fallback, following up to 5 redirects on each attempt, plus one request for `/favicon.ico` when the page answers 200 — and a TLS handshake on port 443. That is a small amount of traffic, and the Actor does not brute-force names or port-scan, but it is unambiguously contact with the target's infrastructure — and the probing queue deliberately goes after the hosts that look abandoned first. **You must be authorized to do that.** Only run it against systems you own or have explicit written permission to assess, and follow the terms of service of the data sources.

If you need a fully passive run — third-party databases only, zero packets to the target — turn `resolveDns` off; the enumeration still works and the enrichment fields simply come back empty.

#### Does this Actor brute-force subdomains?

No. The original Sublist3r bundled subbrute and a port scanner; both are deliberately excluded here. Names are only ever discovered passively. The enrichment stage is the only traffic this Actor sends to a target, and it is bounded: per live host it tries HTTPS and falls back to HTTP, follows at most 5 redirects on each attempt, and fetches `/favicon.ico` once when the page answers 200. It also completes one TLS handshake on port 443. That is a handful of requests per host on a single well-known port — there is no name guessing and no port sweep.

#### Why do I get fewer results than running Sublist3r on my laptop?

Mostly because the original tool's search-engine scrapers no longer work anywhere, and partly because some sources rate-limit cloud IPs. Enabling Apify Proxy and supplying API keys closes most of the gap.

#### Why is a source returning zero subdomains?

Check `report-<domain>.json` in the key-value store. It records per-source counts, errors and skips, so you can tell an outage from a missing API key from a source that genuinely had nothing.

#### Can I use this subdomain finder via API?

Yes. Every Apify Actor is callable through the **Apify API** — start a run, poll it, and pull the dataset as JSON or CSV from any language. This Actor additionally supports **Standby mode**, where `GET /?domain=example.com` returns results synchronously without a cold start per lookup. You can also trigger runs on a **schedule** and route output through **integrations** like Zapier, Make, Slack, Google Sheets or a webhook.

#### How many domains can I scan in one run?

Up to **20 root domains per run** on a paid plan, and **1 per run** on the free plan. If you pass more than 20, the run stops with an error rather than silently truncating the list — split them across several runs, which you can schedule or start in parallel. On the free plan the extra domains are skipped with a warning in the log instead of failing the run.

#### Can I use this subdomain finder for free?

Yes. Every feature is available on the free plan — all 11 sources, DNS resolution, HTTP and TLS fingerprinting, takeover detection and the triage score. The free plan caps the **volume** of a single run at 1 domain, 50 subdomains per domain and a 180-second probe budget. See [the free vs paid comparison](#whats-the-difference-between-the-free-and-paid-plans) above for the full breakdown.

#### What export formats are supported?

The dataset downloads as **JSON, CSV, Excel, HTML, XML or RSS**. A newline-separated plain-text subdomain list is also written to the key-value store per domain, matching the output format of Sublist3r's `-o` flag.

#### Why do some rows have empty HTTP and TLS fields?

Three possible reasons: `resolveDns` was turned off, the host does not resolve or does not answer on ports 80/443, or the per-domain `maxProbeSeconds` budget ran out before that host was reached. The run report's `notProbed` field tells you how many hosts fell into the last category.

### Support

Found a bug, want a source added, or need a field the Actor doesn't currently return? Open an issue in the **Issues** tab of this Actor. Custom versions of this Actor can be built on request.

# Actor input Schema

## `domains` (type: `array`):

The domains you want to find subdomains for, one per line — for example example.com. Enter the root domain, not a subdomain. Pasted URLs, ports and leading wildcards are cleaned up automatically, so https://example.com/path and \*.example.com both work. Maximum 20 domains per run; the run stops with an error if you provide more, so split larger lists across several runs. Free plan is limited to 1 domain per run; extra domains are skipped with a warning in the log.

## `sources` (type: `array`):

The defaults are the sources that return results from cloud infrastructure without credentials. Search engines (Google, Yahoo, Bing, Baidu, Ask) are kept for parity with the original Sublist3r, but they now serve CAPTCHAs and JavaScript-only pages to datacenter IPs and return nothing — enable them only together with a residential proxy.

## `virusTotalApiKey` (type: `string`):

Optional. VirusTotal no longer serves subdomain data without authentication, so the VirusTotal source is skipped entirely when this is empty — the run still succeeds, it just uses one source fewer. Get a free key from <a href="https://www.virustotal.com/gui/my-apikey" target="_blank">your VirusTotal account</a>.

## `otxApiKey` (type: `string`):

Optional. AlienVault OTX has closed anonymous access to its passive DNS records, so without a key this source falls back to the still-public URL corpus — it finds hostnames, just less thoroughly. Supplying a key upgrades it to full passive DNS. Get one from <a href="https://otx.alienvault.com/api" target="_blank">your OTX account</a>.

## `resolveDns` (type: `boolean`):

When on, each subdomain is resolved and the output gains ipv4, ipv6 and cname fields, plus a wildcard-detection flag. Every host that resolves is then probed once over HTTP and TLS, which is what fills in httpStatus, pageTitle, techStack, the certificate fields and the takeover verdict. When off, no traffic reaches the target at all: results come back faster but contain names only, and the resolves and enrichment fields are empty.

## `includeUnresolved` (type: `boolean`):

When on, subdomains that no longer resolve are kept — these are often the most valuable findings, such as decommissioned hosts and dangling DNS records that indicate a possible subdomain takeover. When off, only live hosts are reported. Has no effect unless DNS resolution is on.

## `dnsResolvers` (type: `array`):

The resolvers used for lookups, as IP addresses — for example 1.1.1.1 (Cloudflare) or 8.8.8.8 (Google). Change this only if you need a specific resolver's view of DNS; the defaults are fine for almost every run.

## `maxSubdomainsPerDomain` (type: `integer`):

Caps how many subdomains are reported for each root domain. Leave at 0 for unlimited, which is recommended so nothing is hidden. Set a number as a cost guard on very large targets; when results are clipped, the run report records the full count in its truncatedFrom field so you know what you missed. It is also the knob that governs memory for the result set itself, which is separate from the probing buffers described under "Probe concurrency": every discovered subdomain is held in memory until its domain finishes, and an enriched record costs roughly 2-5 KB (the TLS SAN list dominates). Ten domains of 30,000 subdomains each is on the order of 900 MB, well past the container's 512 MB. If you enumerate several large domains at once, set this to keep (domains run at once) x (this value) x 3 KB comfortably under about 150 MB — roughly 15,000 with the default of 3 concurrent domains. Free plan is capped at 50 subdomains per domain regardless of this setting.

## `maxConcurrentDomains` (type: `integer`):

How many domains to work on simultaneously when you supply several. This does not increase the request rate against the sources — that stays capped by Concurrent requests below — it just fills the time one domain spends waiting on a slow source. Raise it for large batches of small domains; keep it low if your domains are very large, since each one in flight holds its results in memory.

## `concurrency` (type: `integer`):

The run-wide ceiling on outbound requests — to sources and to DNS resolvers — in flight at once, shared across every domain being enumerated. Higher values finish faster but make rate-limiting from sources more likely.

## `requestTimeoutSecs` (type: `integer`):

How long to wait for a single source to respond before treating the request as failed. Raise it if crt.sh is being slow on a large domain.

## `maxRequestRetries` (type: `integer`):

How many times to retry a source that fails with a temporary error, using exponential backoff. Sources that deliberately refuse service — such as a disabled free tier — are skipped immediately without consuming retries.

## `probeTimeoutSecs` (type: `integer`):

How long to wait for each host to answer the HTTP probe. Kept short on purpose: the dominant failure mode is a host that hangs, not one that is merely slow.

## `probeConcurrency` (type: `integer`):

How many hosts to probe at once, PER DOMAIN. Separate from the source concurrency, which throttles free third-party APIs that need to be treated gently. When multiple domains run concurrently (see "Domains enumerated at once"), each gets its own probe pool of this size, so the total probing parallelism across the whole run is this value multiplied by "Domains enumerated at once". The maximum (30) is capped well below what the field alone might suggest, and the reason is memory rather than rate-limiting: at 30 combined with 10 concurrent domains the run holds 300 probes in flight, each peaking at roughly 0.66 MB of response body, favicon and decoded-text buffers (~198 MB), plus about 43 MB of TLS connection buffers for the 650 pooled connections that configuration opens, on top of a ~95 MB baseline — roughly 336 MB of the container's 512 MB, leaving about a third of it free for estimation error. At the defaults (25 x 3 domains) the same arithmetic lands near 155 MB. Note that this figure covers the probing buffers only: the memory held by the results themselves scales with how many subdomains you find and is governed separately by "Max subdomains per domain", where that arithmetic is written out.

## `maxProbeSeconds` (type: `integer`):

Hard ceiling for the enrichment stage, PER DOMAIN — a fresh budget starts for each domain you enumerate, it does not divide across them. When it runs out for a domain, that domain's remaining hosts are reported with empty enrichment fields and its run report says how many were skipped. With several domains and "Domains enumerated at once" above 1, up to that many domains' budgets can be running concurrently, so plan for a real run-time ceiling of roughly (number of domains ÷ Domains enumerated at once) × this value, not this value alone. Free plan is capped at 180 seconds.

## `proxyConfiguration` (type: `object`):

Routes all outbound HTTP requests through a proxy. Leave disabled for normal use. Enable it — ideally with residential proxies — if sources begin rate-limiting you, or if you want to try the search-engine sources, which return nothing from datacenter IPs.

## Actor input object example

```json
{
  "domains": [
    "apify.com"
  ],
  "sources": [
    "crtsh",
    "certspotter",
    "otx",
    "hackertarget",
    "rapiddns"
  ],
  "resolveDns": true,
  "includeUnresolved": true,
  "dnsResolvers": [
    "1.1.1.1",
    "8.8.8.8"
  ],
  "maxSubdomainsPerDomain": 0,
  "maxConcurrentDomains": 3,
  "concurrency": 10,
  "requestTimeoutSecs": 30,
  "maxRequestRetries": 3,
  "probeTimeoutSecs": 8,
  "probeConcurrency": 25,
  "maxProbeSeconds": 900,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `subdomains` (type: `string`):

No description

## `reports` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "domains": [
        "apify.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("datacach/sublist3r-subdomain-finder").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "domains": ["apify.com"] }

# Run the Actor and wait for it to finish
run = client.actor("datacach/sublist3r-subdomain-finder").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "domains": [
    "apify.com"
  ]
}' |
apify call datacach/sublist3r-subdomain-finder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=datacach/sublist3r-subdomain-finder",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Sublist3r Subdomain Finder & Takeover Scanner",
        "description": "Subdomain enumeration from passive OSINT sources — certificate transparency logs, passive DNS and public APIs — with optional active HTTP/TLS fingerprinting of every live host, scored for takeover risk. Run it only against domains you are authorized to assess.",
        "version": "0.1",
        "x-build-id": "VhMGdyHN2FY4wEmAg"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/datacach~sublist3r-subdomain-finder/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-datacach-sublist3r-subdomain-finder",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/datacach~sublist3r-subdomain-finder/runs": {
            "post": {
                "operationId": "runs-sync-datacach-sublist3r-subdomain-finder",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/datacach~sublist3r-subdomain-finder/run-sync": {
            "post": {
                "operationId": "run-sync-datacach-sublist3r-subdomain-finder",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "domains"
                ],
                "properties": {
                    "domains": {
                        "title": "Root domains",
                        "minItems": 1,
                        "maxItems": 20,
                        "type": "array",
                        "description": "The domains you want to find subdomains for, one per line — for example example.com. Enter the root domain, not a subdomain. Pasted URLs, ports and leading wildcards are cleaned up automatically, so https://example.com/path and *.example.com both work. Maximum 20 domains per run; the run stops with an error if you provide more, so split larger lists across several runs. Free plan is limited to 1 domain per run; extra domains are skipped with a warning in the log.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "sources": {
                        "title": "Sources",
                        "type": "array",
                        "description": "The defaults are the sources that return results from cloud infrastructure without credentials. Search engines (Google, Yahoo, Bing, Baidu, Ask) are kept for parity with the original Sublist3r, but they now serve CAPTCHAs and JavaScript-only pages to datacenter IPs and return nothing — enable them only together with a residential proxy.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "crtsh",
                                "certspotter",
                                "otx",
                                "hackertarget",
                                "rapiddns",
                                "virustotal",
                                "google",
                                "yahoo",
                                "bing",
                                "baidu",
                                "ask"
                            ],
                            "enumTitles": [
                                "crt.sh (certificate transparency)",
                                "Cert Spotter (certificate transparency)",
                                "AlienVault OTX (passive DNS)",
                                "HackerTarget",
                                "RapidDNS",
                                "VirusTotal (needs API key)",
                                "Google (needs residential proxy)",
                                "Yahoo (needs residential proxy)",
                                "Bing (needs residential proxy)",
                                "Baidu (needs residential proxy)",
                                "Ask (needs residential proxy)"
                            ]
                        },
                        "default": [
                            "crtsh",
                            "certspotter",
                            "otx",
                            "hackertarget",
                            "rapiddns"
                        ]
                    },
                    "virusTotalApiKey": {
                        "title": "VirusTotal API key",
                        "type": "string",
                        "description": "Optional. VirusTotal no longer serves subdomain data without authentication, so the VirusTotal source is skipped entirely when this is empty — the run still succeeds, it just uses one source fewer. Get a free key from <a href=\"https://www.virustotal.com/gui/my-apikey\" target=\"_blank\">your VirusTotal account</a>."
                    },
                    "otxApiKey": {
                        "title": "AlienVault OTX API key",
                        "type": "string",
                        "description": "Optional. AlienVault OTX has closed anonymous access to its passive DNS records, so without a key this source falls back to the still-public URL corpus — it finds hostnames, just less thoroughly. Supplying a key upgrades it to full passive DNS. Get one from <a href=\"https://otx.alienvault.com/api\" target=\"_blank\">your OTX account</a>."
                    },
                    "resolveDns": {
                        "title": "Resolve DNS",
                        "type": "boolean",
                        "description": "When on, each subdomain is resolved and the output gains ipv4, ipv6 and cname fields, plus a wildcard-detection flag. Every host that resolves is then probed once over HTTP and TLS, which is what fills in httpStatus, pageTitle, techStack, the certificate fields and the takeover verdict. When off, no traffic reaches the target at all: results come back faster but contain names only, and the resolves and enrichment fields are empty.",
                        "default": true
                    },
                    "includeUnresolved": {
                        "title": "Include unresolved subdomains",
                        "type": "boolean",
                        "description": "When on, subdomains that no longer resolve are kept — these are often the most valuable findings, such as decommissioned hosts and dangling DNS records that indicate a possible subdomain takeover. When off, only live hosts are reported. Has no effect unless DNS resolution is on.",
                        "default": true
                    },
                    "dnsResolvers": {
                        "title": "DNS resolvers",
                        "type": "array",
                        "description": "The resolvers used for lookups, as IP addresses — for example 1.1.1.1 (Cloudflare) or 8.8.8.8 (Google). Change this only if you need a specific resolver's view of DNS; the defaults are fine for almost every run.",
                        "default": [
                            "1.1.1.1",
                            "8.8.8.8"
                        ],
                        "items": {
                            "type": "string"
                        }
                    },
                    "maxSubdomainsPerDomain": {
                        "title": "Max subdomains per domain",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Caps how many subdomains are reported for each root domain. Leave at 0 for unlimited, which is recommended so nothing is hidden. Set a number as a cost guard on very large targets; when results are clipped, the run report records the full count in its truncatedFrom field so you know what you missed. It is also the knob that governs memory for the result set itself, which is separate from the probing buffers described under \"Probe concurrency\": every discovered subdomain is held in memory until its domain finishes, and an enriched record costs roughly 2-5 KB (the TLS SAN list dominates). Ten domains of 30,000 subdomains each is on the order of 900 MB, well past the container's 512 MB. If you enumerate several large domains at once, set this to keep (domains run at once) x (this value) x 3 KB comfortably under about 150 MB — roughly 15,000 with the default of 3 concurrent domains. Free plan is capped at 50 subdomains per domain regardless of this setting.",
                        "default": 0
                    },
                    "maxConcurrentDomains": {
                        "title": "Domains enumerated at once",
                        "minimum": 1,
                        "maximum": 10,
                        "type": "integer",
                        "description": "How many domains to work on simultaneously when you supply several. This does not increase the request rate against the sources — that stays capped by Concurrent requests below — it just fills the time one domain spends waiting on a slow source. Raise it for large batches of small domains; keep it low if your domains are very large, since each one in flight holds its results in memory.",
                        "default": 3
                    },
                    "concurrency": {
                        "title": "Concurrent requests",
                        "minimum": 1,
                        "maximum": 50,
                        "type": "integer",
                        "description": "The run-wide ceiling on outbound requests — to sources and to DNS resolvers — in flight at once, shared across every domain being enumerated. Higher values finish faster but make rate-limiting from sources more likely.",
                        "default": 10
                    },
                    "requestTimeoutSecs": {
                        "title": "Request timeout",
                        "minimum": 5,
                        "maximum": 300,
                        "type": "integer",
                        "description": "How long to wait for a single source to respond before treating the request as failed. Raise it if crt.sh is being slow on a large domain.",
                        "default": 30
                    },
                    "maxRequestRetries": {
                        "title": "Max request retries",
                        "minimum": 0,
                        "maximum": 10,
                        "type": "integer",
                        "description": "How many times to retry a source that fails with a temporary error, using exponential backoff. Sources that deliberately refuse service — such as a disabled free tier — are skipped immediately without consuming retries.",
                        "default": 3
                    },
                    "probeTimeoutSecs": {
                        "title": "Probe timeout",
                        "minimum": 1,
                        "maximum": 60,
                        "type": "integer",
                        "description": "How long to wait for each host to answer the HTTP probe. Kept short on purpose: the dominant failure mode is a host that hangs, not one that is merely slow.",
                        "default": 8
                    },
                    "probeConcurrency": {
                        "title": "Probe concurrency",
                        "minimum": 1,
                        "maximum": 30,
                        "type": "integer",
                        "description": "How many hosts to probe at once, PER DOMAIN. Separate from the source concurrency, which throttles free third-party APIs that need to be treated gently. When multiple domains run concurrently (see \"Domains enumerated at once\"), each gets its own probe pool of this size, so the total probing parallelism across the whole run is this value multiplied by \"Domains enumerated at once\". The maximum (30) is capped well below what the field alone might suggest, and the reason is memory rather than rate-limiting: at 30 combined with 10 concurrent domains the run holds 300 probes in flight, each peaking at roughly 0.66 MB of response body, favicon and decoded-text buffers (~198 MB), plus about 43 MB of TLS connection buffers for the 650 pooled connections that configuration opens, on top of a ~95 MB baseline — roughly 336 MB of the container's 512 MB, leaving about a third of it free for estimation error. At the defaults (25 x 3 domains) the same arithmetic lands near 155 MB. Note that this figure covers the probing buffers only: the memory held by the results themselves scales with how many subdomains you find and is governed separately by \"Max subdomains per domain\", where that arithmetic is written out.",
                        "default": 25
                    },
                    "maxProbeSeconds": {
                        "title": "Probe time budget",
                        "minimum": 30,
                        "maximum": 3000,
                        "type": "integer",
                        "description": "Hard ceiling for the enrichment stage, PER DOMAIN — a fresh budget starts for each domain you enumerate, it does not divide across them. When it runs out for a domain, that domain's remaining hosts are reported with empty enrichment fields and its run report says how many were skipped. With several domains and \"Domains enumerated at once\" above 1, up to that many domains' budgets can be running concurrently, so plan for a real run-time ceiling of roughly (number of domains ÷ Domains enumerated at once) × this value, not this value alone. Free plan is capped at 180 seconds.",
                        "default": 900
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Routes all outbound HTTP requests through a proxy. Leave disabled for normal use. Enable it — ideally with residential proxies — if sources begin rate-limiting you, or if you want to try the search-engine sources, which return nothing from datacenter IPs.",
                        "default": {
                            "useApifyProxy": false
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
