ctlog prints every hostname found on a domain’s currently valid certificates, one per line, ready to pipe into httpx, dnsx or nuclei. It reads from the same Certificate Transparency index as the subdomain map, so it answers in well under a second instead of timing out the way crt.sh does on large domains.
ctlog example.com | httpx
The source is on GitHub under the MIT license.
Install
With a recent Go toolchain:
go install github.com/sslboard/ctlog@latest
Or, on Linux and macOS, with the install script. It downloads the latest release, checks it against the published checksums, and puts ctlog in /usr/local/bin (or ~/.local/bin if that is not writable):
curl -fsSL https://raw.githubusercontent.com/sslboard/ctlog/main/install.sh | sh
Prebuilt binaries for Linux, macOS and Windows on amd64 and arm64 are also on the releases page. Download the archive for your platform, extract it, and put ctlog somewhere on your PATH.
Usage
ctlog example.com # one hostname per line
ctlog %.example.com # same result, the crt.sh habit is accepted
ctlog example.com -o json # SSLBoard JSON with counts
ctlog example.com -o crtsh # crt.sh-shaped JSON for existing scripts
ctlog example.com --no-wildcards # leave out *.example.com style entries
ctlog example.com --no-apex # leave out example.com itself
ctlog example.com --all # every page (hostname API plan)
ctlog -f domains.txt # one domain per line, results merged
ctlog login # sign in through the browser
ctlog status # account, plan and usage
ctlog logout
ctlog version
example.com, %.example.com and *.example.com all mean the same thing: this name and every name below it. Patterns in the middle of a name, such as dev%.example.com, are not supported.
Hostnames go to stdout and notices go to stderr, so a pipe or a file only ever receives hostnames. Add -q to silence the notices.
With -f, ctlog reads one domain per line (blank lines and # comments are skipped, -f - reads stdin), looks each one up in turn and prints the merged list with duplicates removed. Batch mode is built for the paid rate limit. On the free plan the second domain hits the one-search-a-minute limit, and ctlog prints what it has so far and stops with exit code 3.
Migrating from crt.sh
# before
curl -s 'https://crt.sh/?q=%.example.com&output=json' | jq -r '.[].name_value' | sort -u
# after
ctlog example.com
If a script expects crt.sh JSON, use -o crtsh and keep the existing jq filter. Each entry holds one hostname in name_value and common_name. The other fields (issuer_name, not_before, not_after, id) are always null, because SSLBoard stores hostnames and not certificates. Names are already lowercase and deduplicated, so sort -u is no longer needed.
What the data covers
- Every hostname on a certificate that is valid today in the public CT logs, kept for 15 days after that certificate expires.
- No history beyond that window. A name whose last certificate expired months ago is not listed, which is usually what recon wants: the names that can still serve traffic.
- Wildcard names such as
*.api.example.comappear exactly as they were issued. Many sites have no certificate forwwwand rely on a wildcard instead, so these entries tell you where services live. Use--no-wildcardsif your next tool cannot handle them. - Results come in reversed-label order, so
api.example.comand everything under it stay together.
A name on a certificate is not proof that something answers on it. To check what those hosts actually serve, run an SSLBoard scan.
Full results
Free searches stop at 100 hostnames. The hostname API plan removes that cap for $9 a month:
- Every hostname for a domain, in pages of up to 10,000.
ctlog --allfollows the pages for you. - 60 requests per minute per API key instead of 1 search a minute per IP. In
--alland-fmode ctlog waits when it reaches the limit and carries on, so long runs finish on their own. - Batch mode (
-f) at that rate. A list of 200 domains finishes in about 4 minutes, where the free limit would need more than three hours.
Sign in from the terminal:
ctlog login
ctlog prints a short code and opens https://sslboard.com/device. Sign in with Google, GitHub or an email link, check that the code matches, and approve. ctlog saves the key in ~/.config/ctlog/config.json, readable only by you. If your account has no plan yet, the page offers to subscribe, and the key you just approved upgrades the moment payment goes through.
For CI and servers, create a key under Account, API and pass it in the environment:
SSLBOARD_API_KEY=sslb_live_... ctlog example.com --all
SSLBOARD_API_KEY wins over a saved login. ctlog status shows which key is in use, your plan and your usage over the last 30 days.
Billing goes through Stripe and you can cancel from the account page. When a plan ends, keys keep working at the free limits, so a lapsed card slows your scripts down without breaking them.
Limits
Without an account, each search returns up to 100 hostnames, and you can run 1 search per minute from one IP address. When a domain has more, ctlog prints the total on stderr and how to get the rest:
100 of 12,432 hostnames (free limit). Run 'ctlog login' to get all of them: https://sslboard.com/docs/ctlog/#full-results
The 100 are the first in reversed-label order, not a sample. Over the rate limit, ctlog prints the wait time and exits with code 3. It never waits or retries by itself, so your script decides what to do next.
Counts stop at 500,000, so a total of 500,000 means “at least”. The listing itself has no cap on the paid plan: --all keeps paging until the last name. Only the largest hosting platforms get anywhere near that count.
Very large shared platforms such as amazonaws.com and public suffixes such as co.uk are refused. Query a domain below them instead.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success, including zero results |
| 1 | Usage error or invalid domain |
| 2 | API or network error |
| 3 | Rate limited |
| 4 | API key rejected, or the browser login was denied or expired |
The HTTP API
ctlog is a thin client for a public endpoint you can also call directly:
curl -s 'https://sslboard.com/api/v1/hostnames?q=example.com&format=text'
| Parameter | Default | Description |
|---|---|---|
q | required | example.com, %.example.com or *.example.com |
include_apex | true | Include the queried name itself |
wildcards | include | exclude drops wildcard entries |
format | json | json, text (one hostname per line) or crtsh |
limit | 10000 | Page size, 1 to 10,000 (paid plan; the free plan always returns 100) |
after | none | Resume after this hostname: pass the previous response’s next (paid plan) |
Send an API key to get the paid limits:
curl -s -H "Authorization: Bearer $SSLBOARD_API_KEY" \
'https://sslboard.com/api/v1/hostnames?q=example.com&format=text'
The default JSON response carries the counts alongside the names:
{
"query": "example.com",
"domain": "example.com",
"hostnames": ["example.com", "admin.example.com", "api.example.com"],
"returned": 100,
"total": 12432,
"total_is_lower_bound": false,
"truncated": true,
"limit": 100,
"tier": "free",
"next": null,
"map_url": "https://sslboard.com/subdomain/example.com",
"upgrade_url": "https://sslboard.com/docs/ctlog/#full-results"
}
On the paid plan, tier is pro and next holds the last hostname of the page while more pages remain. Request again with after=<next> until next is null. Pages follow ct-storage key order, so a hostname that expires between two requests does not shift or repeat anything.
With format=text or format=crtsh, the counts move to the X-Total-Count, X-Total-Is-Lower-Bound, X-Truncated and X-Tier response headers so the body stays clean, and the next cursor is in X-Next-After. Counting stops at 500,000, and total_is_lower_bound is true when a domain goes past that.
Errors return { "error": "..." } with status 400 for an invalid query, 401 for an unknown or revoked key, 402 for after without a paid plan, 422 for a public suffix or a blocked shared domain, 429 when rate limited (with a Retry-After header), and 502 or 503 when the hostname index is unavailable.
GET /api/v1/me with the same Authorization header returns the account email, plan, limits and 30-day usage. It is what ctlog status prints.