# Check a website's security, HTTPS and speed with free APIs

Page: https://free-web-services.com/guides/website-check/

A short Python script that reports on any website: its security headers, its HTTPS set-up and certificate, the host names in the certificate logs, its last copy in the Wayback Machine and, with a free key, its speed. Run it for your own sites, for example once a week.

## What you need

Python 3 with the `requests` package (`pip install requests`). The [MDN HTTP Observatory API](https://free-web-services.com/web-services/web-development/security/mdn-http-observatory-api.html), the [SSL Labs API](https://free-web-services.com/web-services/web-development/security/ssl-labs-api.html), [crt.sh certificate search](https://free-web-services.com/web-services/web-development/security/crt-sh-certificate-search.html) and the [Wayback Machine Availability API](https://free-web-services.com/web-services/web-development/seo/wayback-machine-availability-api.html) need no key. The [Google PageSpeed Insights API](https://free-web-services.com/web-services/web-development/seo/google-pagespeed-insights-api.html) needs a free Google Cloud API key in practice, because the shared quota without a key is often used up.

Run this from a script or a server, not from a web page: SSL Labs refuses calls from browsers. Only check sites you run or have permission to test.

## The checks

The HTTP Observatory scans the security headers, such as Content-Security-Policy and Strict-Transport-Security, and grades them from A+ to F. SSL Labs grades the HTTPS set-up the same way; a fresh test takes a few minutes, so the script asks for a result of the last 24 hours first (`fromCache=on&maxAge=24`) and otherwise checks back every 15 seconds.

The certificate's expiry date comes from the server itself, through Python's `ssl` module: the Certificate Transparency logs behind crt.sh can lag weeks behind, so they are used for something else, the list of host names that have certificates, which often shows a forgotten subdomain.

## The script

Save it as `check_site.py` and run `python3 check_site.py example.org`. Put your own e-mail address in the User-Agent, so the services know who to contact about heavy use. Each check that fails says so and the others carry on.

```python
import os
import socket
import ssl
import sys
import time

import requests

HEADERS = {"User-Agent": "site-check script (you@example.com)"}


def observatory(host):
    # Mozilla's HTTP Observatory: security headers, graded A+ to F
    response = requests.post(
        "https://observatory-api.mdn.mozilla.net/api/v2/scan",
        params={"host": host}, headers=HEADERS, timeout=60)
    data = response.json()
    return f"{data['grade']} ({data['score']}/100, {data['tests_passed']} of {data['tests_quantity']} tests passed)"


def ssl_labs(host):
    # Qualys SSL Labs: the HTTPS set-up, graded A+ to F; a new test takes a few minutes
    url = "https://api.ssllabs.com/api/v3/analyze"
    params = {"host": host, "fromCache": "on", "maxAge": 24}
    for attempt in range(20):
        data = requests.get(url, params=params, headers=HEADERS, timeout=60).json()
        if data["status"] == "READY":
            return ", ".join(sorted({e.get("grade", "no grade") for e in data["endpoints"]}))
        if data["status"] == "ERROR":
            return "error: " + data.get("statusMessage", "unknown")
        time.sleep(15)
    return "not ready yet, try again in a few minutes"


def certificate_expiry(host):
    # the certificate the server sends now, read with Python's own ssl module
    context = ssl.create_default_context()
    with socket.create_connection((host, 443), timeout=30) as connection:
        with context.wrap_socket(connection, server_hostname=host) as tls:
            expires = ssl.cert_time_to_seconds(tls.getpeercert()["notAfter"])
    days = int((expires - time.time()) // 86400)
    return f"valid for {days} more days"


def host_names(host):
    # crt.sh: host names in the public Certificate Transparency logs, which
    # often shows subdomains you forgot; the logs can lag behind by weeks
    response = requests.get("https://crt.sh/", params={"q": "%." + host, "output": "json"},
                            headers=HEADERS, timeout=120)
    names = {name.lower() for cert in response.json() for name in cert["name_value"].split("\n")}
    return f"{len(names)}: " + ", ".join(sorted(names)[:10]) + (" ..." if len(names) > 10 else "")


def wayback(host):
    # Wayback Machine: the most recent saved copy of the home page
    data = requests.get("https://archive.org/wayback/available", params={"url": host},
                        headers=HEADERS, timeout=60).json()
    snapshot = data.get("archived_snapshots", {}).get("closest")
    return f"last saved {snapshot['timestamp'][:8]}" if snapshot else "no saved copy"


def pagespeed(host):
    # PageSpeed Insights: Lighthouse scores; needs your own free key
    key = os.environ.get("PAGESPEED_API_KEY")
    if not key:
        return "skipped (set PAGESPEED_API_KEY)"
    data = requests.get("https://www.googleapis.com/pagespeedonline/v5/runPagespeed",
                        params={"url": f"https://{host}/", "strategy": "mobile", "key": key},
                        headers=HEADERS, timeout=120).json()
    return f"{round(data['lighthouseResult']['categories']['performance']['score'] * 100)}/100 on mobile"


if __name__ == "__main__":
    host = sys.argv[1] if len(sys.argv) > 1 else "example.com"
    for name, check in [("Security headers", observatory), ("HTTPS", ssl_labs),
                        ("Certificate", certificate_expiry), ("Host names", host_names), ("Wayback Machine", wayback),
                        ("Speed", pagespeed)]:
        try:
            print(f"{name}: {check(host)}")
        except (requests.RequestException, OSError, KeyError, ValueError) as error:
            print(f"{name}: failed ({error})")
```

## Example output

For this website, on the day this guide was written:

```text
Security headers: A+ (110/100, 12 of 12 tests passed)
HTTPS: A+
Certificate: valid for 60 more days
Host names: 1: free-web-services.com
Wayback Machine: last saved 20260309
Speed: skipped (set PAGESPEED_API_KEY)
```

## Going further

Run it weekly with cron and send yourself the output only when a grade drops or the certificate has less than 14 days left.

PageSpeed Insights runs a lab test; for the speed real visitors get, the [Chrome UX Report (CrUX) API](https://free-web-services.com/web-services/web-development/seo/chrome-ux-report-crux-api.html) gives 28-day averages of the Core Web Vitals for sites with enough Chrome visitors.

## The APIs in this guide

- [MDN HTTP Observatory API](https://free-web-services.com/web-services/web-development/security/mdn-http-observatory-api.md)
- [SSL Labs API](https://free-web-services.com/web-services/web-development/security/ssl-labs-api.md)
- [crt.sh certificate search](https://free-web-services.com/web-services/web-development/security/crt-sh-certificate-search.md)
- [Wayback Machine Availability API](https://free-web-services.com/web-services/web-development/seo/wayback-machine-availability-api.md)
- [Google PageSpeed Insights API](https://free-web-services.com/web-services/web-development/seo/google-pagespeed-insights-api.md)
- [Chrome UX Report (CrUX) API](https://free-web-services.com/web-services/web-development/seo/chrome-ux-report-crux-api.md)

Updated November 1st, 2026
