README.md
May 17, 2026 · View on GitHub
High-performance passive subdomain enumeration engine for effortless asset discovery and rapid reconnaissance
Features • Installation • Configuration • Usage • Examples • Shell • Providers • Output • Docker
Subdominator is a high-performance passive subdomain enumeration engine. It leverages 73 OSINT sources — certificate transparency logs, search engines, threat intelligence feeds, DNS datasets, and more — to map your target's full attack surface in seconds.
Built on Python's async/await stack (asyncio + aiohttp), it handles 100K+ subdomains per run without memory spikes thanks to its disk-backed findings cache.
Features
- 73 passive OSINT sources — CT logs, search engines, threat intel, DNS archives, and more
- Recursive enumeration — automatically scan discovered subdomains to any depth
- Disk-backed cache — 100K+ subdomains per run with stable memory usage
- Concurrent execution — configurable parallelism with async-first architecture
- Interactive audit shell — search, filter, export, and manage historical findings
- Multiple output formats — plain text, JSONL stream, JSON report, HTML report
- SQLite persistence — findings are saved automatically to a local SQLite database; query and export via the interactive shell
- Health diagnostics — verify connectivity and API key status before a run
- Docker-ready — pre-built images on GHCR, no local Python setup required
Installation
Recommended — uv tool (fastest)
uv tool install subdominator
pip (traditional)
pip install --upgrade subdominator
From source
git clone https://github.com/RevoltSecurities/Subdominator.git
cd Subdominator
uv sync
With HTML report support
pip install "subdominator[reports]"
# or
uv tool install "subdominator[reports]"
Requires Python 3.13+
Configuration
Subdominator stores its API key config in a YAML file. On first run it creates the template automatically at:
~/.config/subdominator/provider-config.yaml
Open that file and add your keys under each provider. Each entry is a list so you can supply multiple keys — Subdominator will pick one at random per run:
# ~/.config/subdominator/provider-config.yaml
shodan:
- YOUR_SHODAN_KEY
censys:
- YOUR_CENSYS_ID:YOUR_CENSYS_SECRET
github:
- ghp_token1
- ghp_token2
# Providers that share the same RapidAPI key:
# coderog, rapidfinder, rapidscan → rapidapi
rapidapi:
- YOUR_RAPIDAPI_KEY
# Aliases: whoisxml → whoisxmlapi, zoomeyeapi → zoomeye
whoisxmlapi:
- YOUR_WHOISXMLAPI_KEY
zoomeye:
- YOUR_ZOOMEYE_KEY
Use a custom config path with --config-path / -cp:
subdominator -d example.com -cp /path/to/my-config.yaml
Environment variables
All settings can be driven by environment variables instead of CLI flags. The prefix is SUBDOMINATOR_:
| Environment variable | Equivalent flag | Effect |
|---|---|---|
SUBDOMINATOR_SSL_VERIFY=false | --insecure / -k | Disable SSL certificate verification on all HTTP connections |
SUBDOMINATOR_PROXY=http://host:port | --proxy / -p | Route all HTTP requests through this proxy |
SUBDOMINATOR_TIMEOUT=30 | --timeout / -t | Override default request timeout |
SUBDOMINATOR_CONCURRENCY=16 | --concurrency / -c | Override default concurrency |
Priority: CLI flags always win over environment variables. If both are set, the CLI flag takes effect.
Standard proxy env vars (HTTP_PROXY, HTTPS_PROXY, NO_PROXY) are also respected automatically via aiohttp's trust_env — no prefix needed.
Key aliases
| Config key | Used by providers |
|---|---|
rapidapi | coderog, rapidfinder, rapidscan |
whoisxmlapi | whoisxml |
zoomeye | zoomeyeapi |
Usage
subdominator [flags]
Input
| Flag | Short | Description |
|---|---|---|
--domain | -d | Single target domain |
--domain-list | -dL | File with one domain per line (bulk mode) |
Resource selection
| Flag | Short | Description |
|---|---|---|
--all | Use all 73 resources (includes disabled-by-default ones) | |
--include-resources | -ir | Comma-separated list of resources to use |
--exclude-resources | -er | Comma-separated list of resources to skip |
--list-resources | -ls | Print resource catalog with auth requirements |
--dork | -dk | Custom search dork (Google and supported engines) |
Disabled by default:
commoncrawl,github,virustotal,waybackarchive— these are slow or rate-limited. Use--allor-irto include them explicitly.
Runtime
| Flag | Short | Description |
|---|---|---|
--recursive-depth | -rd | Max recursion depth for discovered subdomains (default: 0) |
--concurrency | -c | Parallel resource slots (default: 8) |
--timeout | -t | HTTP timeout in seconds (default: 20.0) |
--retries | -rt | Retries per failed request (default: 3) |
--retry-backoff | -rb | Backoff multiplier in seconds between retries (default: 1.0) |
--proxy | -p | HTTP proxy URL (e.g. http://127.0.0.1:8080) |
--insecure | -k | Skip SSL certificate verification |
Config
| Flag | Short | Description |
|---|---|---|
--config-path | -cp | Path to custom provider config YAML |
--show-config-path | -scp | Print the active provider config path and exit |
Output
| Flag | Short | Description |
|---|---|---|
--output | -o | Write plain-text results to a file |
--output-directory | -oD | Directory to save per-domain files (bulk mode) |
--html | -oh | Generate an HTML report (requires jinja2) |
--report-json | -rj | Write a JSON summary report |
--json | -j | Stream JSONL to stdout ({"domain":...,"subdomain":...,"resource":...}) |
--table | -tb | Print findings as a formatted terminal table |
--show-summary | -ss | Print a run summary after completion |
--show-resource-stats | -srs | Print per-resource finding counts and durations |
Storage & diagnostics
| Flag | Short | Description |
|---|---|---|
--db-path | -dp | Custom path for the SQLite database file |
--no-db | -nd | Disable automatic DB persistence for this run |
--save-db | -sd | Explicitly enable DB persistence (on by default) |
--shell | -sh | Open the interactive audit shell |
--health-check | -hc | Verify connectivity and API key status |
--update | -up | Update Subdominator to the latest release |
--release | -release | Show release notes for the latest version |
--verbose | -v | Enable debug logging |
--no-color | -nc | Disable colored output |
DB persistence is enabled by default. Every run saves findings to the local SQLite database automatically. Use
--no-dbto skip persistence for a specific run, or--db-pathto write to a different file.
Examples
Basic single-domain scan:
subdominator -d example.com
Save results to a file:
subdominator -d example.com -o results.txt
Recursive enumeration (depth 2):
subdominator -d example.com -rd 2
Stream JSON for pipeline integration:
subdominator -d example.com -j | jq .subdomain
Use only specific sources:
subdominator -d example.com -ir shodan,censys,securitytrails
Exclude slow sources:
subdominator -d example.com -er commoncrawl,waybackarchive
Bulk scan from a file, save per-domain results:
subdominator -dL domains.txt -oD ./output/
Disable DB persistence for a one-off scan:
subdominator -d example.com -nd
Use a custom SQLite database path:
subdominator -d example.com -dp /data/recon.db
Full scan with all sources + HTML report:
subdominator -d example.com --all -oh report.html
Custom Google dork:
subdominator -d example.com -ir google -dk site:example.com -inurl:www
Health check before a run:
subdominator --health-check
Pipe-safe output (no BrokenPipeError):
subdominator -d example.com | head -20
subdominator -d example.com -j | grep "api\."
Interactive Shell
Launch the audit shell with --shell or -sh:
subdominator --shell
The shell provides a persistent terminal interface for querying and managing historical findings stored in the SQLite database.
Shell commands
| Command | Description |
|---|---|
domains | List all stored root domains with finding counts |
domain <root> | Show stats summary for a stored domain |
findings <root> | Show all stored findings for a domain |
runs [root] | Show recent stored runs |
add <root> <file> | Import and merge findings from a text file |
add domain <root> <file> | Legacy-style alias for the above |
export <root> <path> [txt|json|html] | Export findings to a file |
delete <root> | Delete a domain and all its findings |
resources | List resource catalog with auth markers |
config | Show active config and database paths |
update | Update Subdominator to the latest version |
release | Show release notes for the latest version |
clear | Clear the terminal |
exit / quit | Exit the shell |
Resource auth markers in resources output:
*— API key required~— API key optional (works without, more results with)-— No auth needed
Output Formats
Plain text (default)
One subdomain per line. Pipe-safe. Use -o results.txt to save.
api.example.com
mail.example.com
dev.example.com
JSONL stream (-j)
Newline-delimited JSON, one finding per line. Designed for jq, grep, and pipeline tools.
{"domain":"example.com","subdomain":"api.example.com","resource":"shodan"}
{"domain":"example.com","subdomain":"mail.example.com","resource":"crtsh"}
HTML report (-oh report.html)
A full standalone HTML report. Requires the jinja2 optional dependency (pip install "subdominator[reports]").
Summary (-ss / -srs)
--show-summary prints an overview table (total findings, resource counts, duration). --show-resource-stats adds a per-resource breakdown with individual finding counts and durations.
JSON report (-rj report.json)
A full structured JSON summary of the run, including resource execution metadata, useful for feeding into other tools.
Database (automatic)
Findings are automatically saved to SQLite at ~/.local/share/subdominator/subdominator.db on every run. Use --no-db to skip for a specific run, or --db-path to write to a custom path. Query and export from the interactive --shell.
Note on crt.sh and PostgreSQL: The
crtshprovider internally queries the public crt.sh certificate transparency database over a direct PostgreSQL connection (usingasyncpg). This is a read-only query to the crt.sh public server — Subdominator itself does not expose or require any PostgreSQL connection for its own data storage.
Providers
73 sources total. Auth column: key = API key required, optional = works without a key (more results with one), free = no auth needed.
Sources marked † are disabled by default (slow or heavily rate-limited). Include them with
--allor-ir <name>.
Free (no auth)
| Name | URL | Notes |
|---|---|---|
abuseipdb | https://abuseipdb.com/ | |
anubis | https://jldc.me/anubis | |
commoncrawl † | https://index.commoncrawl.org/ | Slow, large dataset |
crtsh | https://crt.sh | Certificate Transparency |
cyfare | https://cyfare.net/ | |
digitorus | https://www.digitorus.com/ | |
hackertarget | https://hackertarget.com/ | |
hudsonrock | https://cavalier.hudsonrock.com/ | |
myssl | https://myssl.com | |
racent | https://face.racent.com/ | |
rapiddns | https://rapiddns.io/ | |
reconcloud | https://recon.cloud/ | |
riddler | https://riddler.io/ | |
shrewdeye | https://shrewdeye.app/api | |
sitedossier | https://www.sitedossier.com/ | |
shodanx | https://github.com/RevoltSecurities/Shodanx | |
thc | https://thc.org/ | |
threatcrowd | https://threatcrowd.org/ | |
threatminer | https://www.threatminer.org/ | |
waybackarchive † | https://archive.org/wayback | Very slow |
Optional API key
| Name | URL | Notes |
|---|---|---|
alienvault | https://otx.alienvault.com | More results with key |
reconeer | https://www.reconeer.com/ | More results with key |
submd | https://api.sub.md/ | More results with key |
Requires API key
Docker
Pre-built images are published to the GitHub Container Registry on every release.
Pull and scan:
docker run --rm -it ghcr.io/revoltsecurities/subdominator:latest -d example.com
Save output to the current directory:
docker run --rm -it \
-v $(pwd):/output \
ghcr.io/revoltsecurities/subdominator:latest \
-d example.com -o /output/results.txt
Use a custom provider config:
docker run --rm -it \
-v /path/to/config:/config \
ghcr.io/revoltsecurities/subdominator:latest \
-cp /config/provider-config.yaml -d example.com
Build the image locally:
docker build -t subdominator -f Docker/Dockerfile .
Development
git clone https://github.com/RevoltSecurities/Subdominator.git
cd Subdominator
uv sync --group dev
python -m pytest tests/ -v
Build a distribution:
uv build
Project structure:
src/subdominator/
├── cli/ # app.py (entry point), shell.py (audit shell)
├── core/ # models, settings, constants, provider_config
├── http/ # retryable.py (aiohttp wrapper)
├── output/ # reports.py (HTML), writers
├── resources/
│ ├── base.py # BaseResource
│ ├── catalog.py # RESOURCE_CATALOG (73 entries)
│ ├── registry.py # ResourceRegistry + DEFAULT_DISABLED_RESOURCES
│ └── providers/ # 73 individual provider modules
├── services/ # enumerator.py (async orchestration)
└── storage/ # database.py, repository.py (SQLite)
Security
Subdominator performs passive reconnaissance only — it queries public APIs and datasets and does not send traffic directly to the target. No self-updates happen without explicit --update / shell update command. All HTTP interactions go through the RetryableHttpClient which logs requests at debug level.
License
MIT © RevoltSecurities
Built with care by the RevoltSecurities team. If Subdominator helps your work, a ⭐ on GitHub goes a long way.