crt.sh Subdomain Finder - Certificate Search
Pricing
from $2.00 / 1,000 hosts
crt.sh Subdomain Finder - Certificate Search
Find every subdomain of a domain from public Certificate Transparency logs (crt.sh), one row per host: days until certificate expiry, issuer, environment inferred from the name (staging, dev, production) and first-seen date. Asset inventory, no API key.
Pricing
from $2.00 / 1,000 hosts
Rating
0.0
(0)
Developer
Tom Awake
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
20 hours ago
Last modified
Categories
Share
What does crt.sh Subdomain Finder do?
Every subdomain of a domain, from public Certificate Transparency logs — as an inventory, not a log dump.
Since 2018 browsers reject any TLS certificate that was not published to a public Certificate Transparency log (RFC 6962). That makes CT the most complete public record of which hosts exist under a domain. But it is a log: one entry per certificate issuance event, duplicates included, expired certificates included, with no notion of a "host".
Measured on shopify.com: 8,239 log entries → 5,971 distinct
certificates → 627 hosts. A 13:1 reduction. The 627 is the answer; the
8,239 is the raw material.
This Actor does the reduction, and computes the three things the log does not carry: how many days until each host's certificate expires, which environment the hostname belongs to, and when the host was first seen.
No API key. No account. Nothing to break.
What makes this different from querying crt.sh yourself
Four things, each of which cost a measurement to find.
1. One row per host, not per certificate. Certificates are reissued every 90 days, so a host that has existed for three years carries a dozen of them. The inventory merges them: first seen, last issued, certificate count, every issuer ever used, and the expiry date that actually governs the host today.
2. The obvious query silently returns only dead certificates.
This is the trap at the centre of this source. On shopify.com:
| Query | Certificates | Of which currently valid | Hosts |
|---|---|---|---|
?q=shopify.com | 5,252 | 0 (0%) | 335 |
?q=shopify.com&exclude=expired | 2,990 | 2,990 (100%) | 469 |
| Both, merged | 8,239 | 2,990 | 627 |
The two result sets share zero certificate IDs. crt.sh caps each query, and the unfiltered variant returns only historical records. Query it the obvious way and you get an inventory containing not one live host. This Actor runs both and merges them — 627 hosts, 33% more than the better of the two alone.
3. Certificates are logged more than once.
Every certificate is logged first as a pre-certificate, then as the final
certificate, and often in several logs. The duplicate entries differ only
by their crt.sh ID — same serial, same issuer, same dates, same names.
Measured inflation: 1.52× on anthropic.com, 1.38× on shopify.com.
Rows are deduplicated on (issuer, serial number), the real identity of a
certificate, so certificateCount means what it says.
4. Days, not dates.
daysUntilExpiry is the column you sort on when you manage an estate. The
source gives you 2026-09-26; the question is "how long have I got".
Environment classification
Only the part of the hostname before the searched domain is classified.
Without that rule, every host on grafana.com came out as Internal,
because grafana is itself the name of an internal-tooling product — the
company's own name was contaminating the classification of all its hosts.
The root domain says nothing about environment; only the prefix speaks.
The prefix is split into whole tokens — by dots, hyphens and underscores,
with trailing digits stripped so staging3 and shell-staging-10 are
recognised — and matched against known markers. Priority runs
Internal → Staging → Development → Mail, so
api-staging.product-internal.example.com is classified Internal: the
most sensitive marker wins, not the first one.
Anything with no marker is classified Production. That is deliberate. A public host with a valid certificate is production until proven otherwise, which is both the common case and the prudent assumption for an inventory.
Measured on shopify.com, 627 hosts:
| Environment | Hosts | Share |
|---|---|---|
| Production | 448 | 71% |
| Staging | 157 | 25% |
| 9 | 1% | |
| Internal | 8 | 1% |
| Development | 5 | 1% |
170 of 627 hosts are outside production.
What it is for
- Asset inventory. Enumerate what actually exists under your domains, including the hosts nobody remembers provisioning.
- Renewal monitoring. Sort by
daysUntilExpiry. A host expiring soon with a highdaysSinceLastIssueis a forgotten certificate, not a renewal in progress. - Unexpected issuance.
issuerCountandissuersshow every authority that has ever issued for a host. A new issuer you did not authorise is worth a look. - Due diligence. Size an acquisition target's real estate before signing, from public record only.
- Competitive and market research. Product launches and new regions usually get a certificate before they get an announcement.
This Actor reads a public log. It makes no connection of any kind to the hosts it reports — no port scan, no probe, no DNS resolution, no vulnerability check. Everything returned was published by a certificate authority to a public append-only log, by design, so that it could be audited. Point it at domains you are responsible for, or use it for research; the usual rules about what you do with an inventory still apply.
Pricing: per host, never per row
You are charged per unique host in the output, whatever the output shape. The certificate mode returns several times more rows for the same set of hosts — charging by the row would bill you repeatedly for the same discovery. A run returning 400 certificate rows across 93 hosts is charged 93.
Wildcard entries are charged on their base name, so *.example.com and
example.com count once.
Output
One row per host (default)
| Field | Meaning |
|---|---|
hostname | The host, or *.domain for a wildcard |
environment | Production / Staging / Development / Internal / Mail |
status | Valid / Expiring soon (≤30 days) / Expired |
daysUntilExpiry | Days until the host loses coverage; negative if lapsed |
expiresAt | Date the latest covering certificate expires |
lastIssuedAt, daysSinceLastIssue | When a certificate was last issued for this host |
firstSeenAt | Earliest issuance ever logged — the host's age |
certificateCount | Distinct certificates, after deduplication |
currentIssuer, issuers, issuerCount | Current and historical authorities |
subdomainDepth | 0 for the domain itself, 1 for a direct subdomain |
isWildcard, isActive | Flags |
currentSerialNumber, crtShUrl | Identifiers for verification |
One row per certificate
Adds issuedAt, validityDays (90 signals automated Let's Encrypt, 398 a
commercial certificate placed by hand), certificateId, issuerFullName,
and coveredNames — every name on the same certificate, which is where
hosts you were not looking for tend to appear.
Three dataset views ship with the Actor: Inventory, Renewals due, and Certificates.
The Certificates view shows the six fields above, so it is empty in
the default host mode — those fields only exist when there is one row per
certificate. Switch mode to certificates before using that view.
Limits
Stated plainly, because they affect what you can conclude.
- crt.sh caps a single query. The historical half of the result is capped at roughly 5,000 certificates per domain, so for very large domains the expired set is partial. The currently-valid set is retrieved by a separate query and is not affected — if you need completeness, completeness of the live estate is what you get.
- CT covers publicly-trusted certificates only. Hosts behind a private CA, an internal PKI, or no TLS at all leave no trace here. An empty result is not proof a host does not exist.
- A logged host is not necessarily reachable. A certificate was issued; whether anything answers on that name today is a different question, and this Actor does not ask it — that would require contacting the host.
- Environment classification is inferred from the name. It is a heuristic over naming conventions, not ground truth. It is accurate enough to triage a list of 600 hosts and not accurate enough to be evidence.
- crt.sh is intermittently unavailable. It returns HTTP 502 under load; four consecutive 502s before success were observed while building this. Every request is retried up to six times with exponential backoff, which is why runs occasionally take a minute rather than five seconds.
- Wildcards hide their contents.
*.example.comis one row and an unknown number of real hosts.
Use crt.sh Subdomain Finder as an API
Call it from your own code with the Apify client, here in Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("DataIO/crtsh-subdomain-finder").call(run_input={'domains': ['shopify.com'],'mode': 'hosts','activeOnly': True,'maxItems': 500,})for item in client.dataset(run["defaultDatasetId"]).iterate_items():print(item)
It also works from JavaScript, Make, Zapier, n8n, and from AI agents through the Apify MCP server.
Other actors you might like
- npm Downloads Scraper: Package Trends & Growth
- WordPress Plugin Scraper: Installs & Ratings
- Domain Expiry Checker: RDAP Registrar & Status
FAQ
Is it legal to use this data?
The actor reads public data from its official source, without logging in and without bypassing any access control. What you do with the data, for example contacting people listed in it, is your responsibility under the laws that apply to you, such as GDPR in Europe.
Can I run it on a schedule?
Yes. Create a schedule in Apify Console, daily or weekly for example, and each run delivers a fresh dataset, which you can send by email, webhook or integration.
Can AI agents use it?
Yes. It is available through the Apify MCP server, and every input field is described in its input schema, so an agent can call it directly.
Source
crt.sh, operated by Sectigo, aggregating the public Certificate Transparency logs. Public, no authentication, no rate limit published. This Actor is not affiliated with Sectigo.