DEV Community

Alex Spinov
Alex Spinov

Posted on

crt.sh Has a Free API — Find Every SSL Certificate for Any Domain

Certificate Transparency logs record every SSL/TLS certificate ever issued. crt.sh gives you free access to search them all — and discover subdomains that even the domain owner forgot about.

No API key. No rate limits. Just add ?output=json\ to any query.

Why This Matters

A bug bounty hunter was testing a company's web application. The main site was hardened. But when they searched crt.sh for the company's certificates, they found 47 subdomains — including staging.company.com\, dev-api.company.com\, and old-admin.company.com\. The staging server had default credentials.

Certificate Transparency reveals your entire attack surface.

The Simplest API You'll Ever Use

\`python
import requests

def find_subdomains(domain):
"""Find all subdomains via Certificate Transparency logs."""

response = requests.get(
    f"https://crt.sh/?q=%25.{domain}&output=json",
    timeout=30
)

if response.status_code == 200:
    certs = response.json()

    # Extract unique subdomains
    subdomains = set()
    for cert in certs:
        name = cert.get("name_value", "")
        for line in name.split("\n"):
            line = line.strip().lower()
            if line and "*" not in line:
                subdomains.add(line)

    subdomains = sorted(subdomains)

    print(f"Domain: {domain}")
    print(f"Certificates found: {len(certs)}")
    print(f"Unique subdomains: {len(subdomains)}")

    for sub in subdomains[:20]:
        print(f"  {sub}")

    if len(subdomains) > 20:
        print(f"  ... and {len(subdomains) - 20} more")

    return subdomains
Enter fullscreen mode Exit fullscreen mode

Find all subdomains for a domain

find_subdomains("github.com")
`\

Certificate History

See every certificate ever issued for a domain:

\`python
def cert_history(domain):
"""Get certificate issuance history."""

response = requests.get(
    f"https://crt.sh/?q={domain}&output=json",
    timeout=30
)

if response.status_code == 200:
    certs = response.json()

    print(f"Total certificates for {domain}: {len(certs)}")

    # Sort by most recent
    certs.sort(key=lambda c: c.get("not_before", ""), reverse=True)

    for cert in certs[:10]:
        issuer = cert.get("issuer_name", "Unknown")
        not_before = cert.get("not_before", "?")
        not_after = cert.get("not_after", "?")

        # Extract issuer organization
        issuer_org = "Unknown"
        if "O=" in issuer:
            issuer_org = issuer.split("O=")[1].split(",")[0]

        print(f"\n  Issued: {not_before}")
        print(f"  Expires: {not_after}")
        print(f"  Issuer: {issuer_org}")
        print(f"  ID: {cert.get('id', '?')}")
Enter fullscreen mode Exit fullscreen mode

cert_history("example.com")
`\

Monitor New Certificates

\`python
from datetime import datetime, timedelta

def monitor_new_certs(domain, days=7):
"""Find certificates issued in the last N days."""

response = requests.get(
    f"https://crt.sh/?q=%25.{domain}&output=json",
    timeout=30
)

if response.status_code == 200:
    certs = response.json()
    cutoff = (datetime.now() - timedelta(days=days)).strftime("%Y-%m-%d")

    recent = [c for c in certs if c.get("not_before", "") >= cutoff]

    print(f"New certificates for *.{domain} in last {days} days: {len(recent)}")

    for cert in recent[:10]:
        name = cert.get("name_value", "?").split("\n")[0]
        issued = cert.get("not_before", "?")
        issuer = cert.get("issuer_name", "")

        issuer_short = "Unknown"
        if "O=" in issuer:
            issuer_short = issuer.split("O=")[1].split(",")[0]

        print(f"  {issued} | {name} | {issuer_short}")
Enter fullscreen mode Exit fullscreen mode

monitor_new_certs("google.com", days=3)
`\

Detect Phishing Domains

\`python
def find_lookalikes(brand):
"""Find certificates for domains that look like a brand (phishing detection)."""

response = requests.get(
    f"https://crt.sh/?q=%25{brand}%25&output=json",
    timeout=30
)

if response.status_code == 200:
    certs = response.json()

    # Extract unique domains
    domains = set()
    for cert in certs:
        for name in cert.get("name_value", "").split("\n"):
            name = name.strip().lower()
            if name and brand.lower() in name and "*" not in name:
                domains.add(name)

    # Filter out the real domain
    suspicious = [d for d in sorted(domains) if not d.endswith(f".{brand}.com")]

    print(f"Potential lookalike domains for '{brand}': {len(suspicious)}")
    for d in suspicious[:15]:
        print(f"  ⚠ {d}")
Enter fullscreen mode Exit fullscreen mode

find_lookalikes("paypal")
`\

What You Can Build

  • Subdomain enumeration — discover the full attack surface
  • Certificate monitoring — alert on unauthorized cert issuance
  • Phishing detection — find lookalike domains abusing your brand
  • Compliance audit — verify all certs are from approved CAs
  • Bug bounty recon — find forgotten subdomains with weak security

Zero cost. Zero authentication. The most underrated OSINT tool.


More free security APIs on my GitHub.

Top comments (0)