Subdomain Finder API — /api/v1/security/subdomains
Runs two discovery pipelines in parallel — passive Certificate Transparency lookup via crt.sh and active DNS brute-force against a curated top-200 wordlist — then enriches every hit with DNS resolve, HTTP HEAD, TLS peek, and a CNAME cross-check against 15 cloud-provider takeover fingerprints (S3, CloudFront, Heroku, Azure Blob / Websites, GitHub Pages, Cloudflare Pages, Netlify, Vercel, ReadTheDocs, Zendesk, HelpScoutDocs, Tumblr). Non-apex inputs are normalized to their registrable apex via PSL. `sync=true` (default) returns one JSON body; `sync=false` streams SSE (start, host, done events).
What Subdomain Finder does
Runs two discovery pipelines in parallel — passive Certificate Transparency lookup via crt.sh and active DNS brute-force against a curated top-200 wordlist — then enriches every hit with DNS resolve, HTTP HEAD, TLS peek, and a CNAME cross-check against 15 cloud-provider takeover fingerprints (S3, CloudFront, Heroku, Azure Blob / Websites, GitHub Pages, Cloudflare Pages, Netlify, Vercel, ReadTheDocs, Zendesk, HelpScoutDocs, Tumblr). Non-apex inputs are normalized to their registrable apex via PSL. `sync=true` (default) returns one JSON body; `sync=false` streams SSE (start, host, done events).
- Method GET
- Path /api/v1/security/subdomains
- Stability STABLE
- Version v2026-09-03
- Summary Enumerate subdomains via Certificate Transparency logs + DNS brute-force with liveness + takeover-risk enrichment.
Request parameters
Pass these as query-string parameters (or, where indicated, in the request headers). Required params marked required.
- domain (required) — string Apex domain to enumerate (e.g. github.com). Non-apex inputs normalized via PSL.
- sources — string Comma-separated subset of `ct`, `brute`, `passive`. Free tier: `ct,brute`. `passive` requires BIZ tier.
- limit — integer, default 25 Cap total returned hosts. Free 25, PRO 200, BIZ 500.
- liveness — boolean, default true Per-host DNS + HTTP + TLS enrichment. Set false for metadata-only scan.
- sync — boolean, default true `true` returns full JSON body. `false` streams SSE with `start`, `host`, `done` events.
- refresh — boolean, default false Bypass the 6h scan cache and re-run.
Response schema
All successful responses follow the standard `{ success: true, data: {...} }` envelope. Notable fields on the data object for this endpoint:
- data.apex — string Normalized apex (registrable domain).
- data.scannedAt — string ISO timestamp of the scan.
- data.sources — object Per-source stats: `{ct:{count,ms,ok?}, brute:{count,ms,checked}, passive:{count,skipped,reason?}}`.
- data.counts — object `{total, alive, dead}` — aggregate host counts.
- data.subdomains — array One entry per host — see host object shape below.
- data.subdomains[].host — string Fully-qualified hostname.
- data.subdomains[].sources — array Which sources surfaced this host: `ct`, `brute`, `passive`.
- data.subdomains[].firstSeen — string Earliest known timestamp (CT log `min_entry_timestamp` when available).
- data.subdomains[].ips — array A-record IPs.
- data.subdomains[].cname — string Primary CNAME target (nullable).
- data.subdomains[].http — object `{scheme, status, server}` from HEAD probe.
- data.subdomains[].tls — object `{issuer, notAfter, subjectCN}` from TLS peek.
- data.subdomains[].alive — boolean True when DNS resolves AND (HTTP<500 OR TLS handshake succeeded).
- data.subdomains[].takeoverRisk — boolean True when CNAME matches a cloud-provider fingerprint AND the target has no A record.
- data.tier — string Effective tier: `free`, `pro`, or `biz`.
- data.tierProfile — object Applied caps: `{sources, bruteLimit, limit, liveness}`.
- data.degraded — boolean True when every requested source failed (e.g. crt.sh timeout). Not cached.
- data.elapsedMs — integer Wall-clock scan duration.
- data.cached — boolean True when result was served from the 6h scan cache.
Example response
Truncated but representative example for a real lookup:
{
"success": true,
"data": {
"apex": "github.com",
"scannedAt": "2026-09-03T…",
"sources": {
"ct": {
"count": 38,
"ms": 2102
},
"brute": {
"count": 7,
"ms": 360,
"checked": 200
},
"passive": {
"count": 0,
"skipped": true
}
},
"counts": {
"total": 42,
"alive": 31,
"dead": 11
},
"subdomains": [
{
"host": "api.github.com",
"sources": [
"ct",
"brute"
],
"firstSeen": "2018-11-04T…",
"ips": [
"140.82.121.5"
],
"cname": null,
"http": {
"scheme": "https",
"status": 200,
"server": "GitHub.com"
},
"tls": {
"issuer": "DigiCert",
"notAfter": "2026-11-30T…",
"subjectCN": "*.github.com"
},
"alive": true,
"takeoverRisk": false,
"lastSeenAlive": "2026-09-03T…"
}
],
"tier": "free",
"tierProfile": {
"sources": [
"ct",
"brute"
],
"bruteLimit": 25,
"limit": 25,
"liveness": true
},
"elapsedMs": 5843,
"cached": false,
"degraded": false
}
}Error codes
Errors return HTTP status codes paired with stable machine-readable codes. Match on the code field rather than the human-readable message.
- 400 INVALID_DOMAIN `domain` failed PSL apex extraction (bad TLD, malformed input).
- 402 TIER_GATED Requested `passive` source but plan tier does not include it.
- 504 UPSTREAM_TIMEOUT crt.sh timed out and no other source produced hosts. Degraded response is returned but not cached.
Implementation tips
Patterns we recommend when integrating this endpoint:
- Prefer `sync=true` for scripts under 500 hosts — it's cheaper on your side than framing SSE.
- Use `sync=false` (SSE) for progressive UI updates on large enumerations.
- Passive CT results are cached shared across all callers for 6h — repeat scans of the same apex from different clients return instantly.
- Cross-reference `takeoverRisk` hits with your CNAME source-of-truth (Terraform / Route 53 / DNSControl) before filing bug-bounty reports.
- For continuous monitoring instead of ad-hoc scans, use the DomainScan platform — daily re-discovery and `NEW_SUBDOMAIN` alerts are wired in automatically.
Frequently asked questions
- Does this hit the target's servers? Passive CT is a query against crt.sh only. Active brute-force sends one DNS query per prefix. Liveness enrichment sends one HEAD + one TLS handshake per hit — no page fetches, no JavaScript execution.
- Why do I see hosts marked `dead`? CT-only hosts that no longer resolve. Useful for spotting abandoned staging tiers still referenced in code or docs.
- How is `takeoverRisk` computed? CNAME matches one of 15 cloud-provider suffixes AND the CNAME target has no A record. Same fingerprints as Detectify's subdomain-takeover scanner.
- What counts as apex? The registrable domain via the Public Suffix List. `api.staging.example.com` normalizes to `example.com`. `example.co.uk` stays intact.