API

No key, no meaningful rate limit, CORS open to everything. Every response describes the connection it arrived on, so the client you call it with is the client you measure.

Endpoints

PathReturns
/api/allEverything: decoded TLS, HTTP/2 frames, JA3, JA4
/api/cleanJust the hashes. The one to assert against in CI
/api/tlsClientHello detail only
/api/http2SETTINGS, WINDOW_UPDATE, PRIORITY, pseudo-header order
/api/http1HTTP/1.1 headers in wire order, original casing
/api/ja3JA3 hash, plain text
/api/ja3/fullUnhashed JA3 string
/api/ja4JA4 fingerprint, plain text
/api/ja4/rawJA4 with both hash halves expanded
/api/akamaiAkamai HTTP/2 fingerprint, plain text
/api/ipYour IP

Add ?pretty=0 for compact JSON. Responses are never cached, because a cached fingerprint would belong to someone else.

Check a fingerprint

curl -s https://kittens.sh/api/clean
{
  "ja3_hash": "375c6162a492dfbf2795909110ce8424",
  "ja4": "t13d4907h2_0d8feac7bc37_7395dae3b2f3",
  "akamai_hash": "64a832f547be33249bf4d33e8a46c5dc",
  "user_agent": "curl/8.7.1"
}

Assert in CI

The usual reason to automate this: proving an impersonation library still matches its target after a dependency bump.

EXPECTED="t13d1516h2_8daaf6152771_e5627efa2ab1"
GOT=$(curl -s https://kittens.sh/api/ja4)

if [ "$GOT" != "$EXPECTED" ]; then
  echo "fingerprint drifted: $GOT" >&2
  exit 1
fi

See what changed

/api/ja4/raw returns JA4 with both hashed halves expanded, so a diff points at the cipher or extension that moved instead of just telling you the hash is different.

diff <(curl -s https://kittens.sh/api/ja4/raw) expected.txt

What the fingerprints are

JA4 reads t13d1516h2_8daaf6152771_e5627efa2ab1: TLS over TCP, TLS 1.3, SNI present, 15 ciphers, 16 extensions, ALPN h2. Then a hash of the sorted cipher list, and a hash of the sorted extension list plus signature algorithms. Sorting is what lets it survive the extension shuffling that broke JA3.

JA3 is the older MD5 over unsorted fields. Kept because plenty of tooling still expects it, but Chrome randomises extension order, so it is no longer stable.

Akamai fingerprints HTTP/2 rather than TLS: SETTINGS values in the order sent, the connection WINDOW_UPDATE, PRIORITY frames, and pseudo-header order.

Licensing

JA3 and JA4 are both BSD-3-Clause. The wider JA4+ suite (JA4H, JA4S and the rest) is FoxIO License 1.1, which does not permit monetisation without an OEM licence, so none of it is implemented here.