API Reference
All endpoints accept and return JSON unless noted. No authentication required.
Rate limits (per IP)
IP checks 60 req / min
PAC validations 20 req / min
DNS queries 30 req / min
WHOIS lookups 10 req / min
CDN detection 20 req / min
TLS scans 10 req / min
Security headers 5 req / min
Screenshots 5 req / min
App Stack scans 5 req / min
App Map crawls 2 req / min
My IP
/ip/raw
Response — text/plain
203.0.113.42
/ip/json
Response — application/json
{"ip": "203.0.113.42",
"city": "San Francisco",
"region": "California",
"country": "US",
"org": "AS12345 Example ISP",
"latitude": 37.7749,
"longitude": -122.4194}
IP Lookup
Rate limited to 60 req / min.
/ip/check
Request — application/json
{"ip_address": "8.8.8.8"}
Response — application/json
{"isp": "Google LLC",
"city": "Mountain View",
"region": "California",
"country": "US",
"latitude": 37.4056,
"longitude": -122.0775}
DNS Resolver
Rate limited to 30 req / min.
/app/dig
Request — application/x-www-form-urlencoded
domain=github.com&record_types=A&record_types=MX
Response — application/json
{"records": {
"A": {"ttl": 60, "values": ["140.82.121.4"]},
"MX": {"ttl": 3600,"values": ["10 aspmx.l.google.com."]}}}
Record types also include CAA, TLSA,
SSHFP, and NAPTR.
For security-oriented analysis rather than raw records, pass one or more checks
values instead of (or alongside) record_types:
Request — application/x-www-form-urlencoded
domain=github.com&checks=dnssec&checks=email_security
Response — application/json
{"records": {},
"dnssec": {"status": "valid", "validated": true, "has_dnskey": true, "has_ds": true, "key_count": 2},
"email_security": {
"spf": {"found": true, "record": "v=spf1 ...", "strength": "soft fail (~all)"},
"dmarc": {"found": true, "record": "v=DMARC1; p=reject; ...", "policy": "reject", "weak": false},
"dkim": [{"selector": "google", "record": "v=DKIM1; ..."}]}}
| email_security | SPF, DMARC, and DKIM (probes common selectors) |
| dnssec | Validates against trusted resolvers via the AD flag, not just presence of DNSKEY/DS |
| zone_transfer | Attempts AXFR against each authoritative nameserver |
| multi_resolver | Compares the A record across Google, Cloudflare, Quad9, and OpenDNS |
| delegation | Checks whether the domain's own nameservers agree on its NS set |
CDN Detection
Rate limited to 20 req / min.
/cdn/check
Request — application/json
{"domain": "example.com"}
Response — application/json
{"cdn_detected": true,
"provider": "Cloudflare",
"confidence": "high",
"headers_found": ["cf-ray", "cf-cache-status"],
"features": {
"compression": "br",
"http2": true,
"http3": true,
"hsts": true,
"cache_status": "HIT",
"waf_indicators": ["cf-ray"]},
"status_code": 200,
"final_url": "https://example.com/"}
TLS Scan
Rate limited to 10 req / min.
/tls/scan
Request — application/json
{"domain": "github.com", "port": 443}
Response — application/json
{"domain": "github.com", "port": 443,
"tls_version": "TLSv1.3",
"cipher": {"name": "TLS_AES_128_GCM_SHA256", "protocol": "TLSv1.3", "bits": 128},
"alpn": "h2",
"ocsp_stapled": false,
"certificate": {
"subject": {"commonName": "github.com"},
"issuer": {"commonName": "DigiCert TLS Hybrid ECC SHA384 2020 CA1",
"organizationName": "DigiCert Inc"},
"sans": ["github.com", "www.github.com"],
"not_before": "2024-03-07T00:00:00+00:00",
"not_after": "2026-03-14T00:00:00+00:00",
"days_remaining": 347,
"expired": false,
"fingerprint_sha256": "AA:BB:CC:...",
"fingerprint_sha1": "11:22:33:..."},
"pqc": {
"pqc_supported": true,
"pqc_group_id": "0x11ec",
"pqc_mechanism": "X25519Kyber768",
"pqc_via_hrr": true,
"pqc_note": null},
"legacy_protocols": {"tlsv1": false, "tlsv1_1": false},
"hsts_preload": {"preloaded": true, "via": "github.com"},
"findings": [],
"grade": {"letter": "A+", "color": "success", "label": "Excellent"}}
PQC is probed via a raw ClientHello advertising hybrid groups (X25519MLKEM768, X25519Kyber768, etc.) at highest priority. A HelloRetryRequest or direct ServerHello selecting a PQC group confirms support. If the server accepts the classical X25519 fallback, PQC is reported as not detected.
legacy_protocols probes TLS 1.0/1.1 acceptance on separate connections (the main connection above always negotiates 1.2+).
hsts_preload checks membership in Chromium's hardcoded preload list (baked into this service at build time), distinct from whether the site's own HSTS header merely advertises a preload intent — see the Security Headers tool for that.
alpn is the negotiated application protocol (h2, http/1.1, or null if none was negotiated).
ocsp_stapled is a separate TLS 1.2 handshake probe for a stapled OCSP response (true/false, or null if the probe itself was inconclusive).
findings/grade also cover certificate signature algorithm, key size, and negotiated-cipher strength/forward-secrecy — same A+–F scale as Security Headers.
User Agent Parser
No rate limit for manual queries.
/ua/check
Request — application/json
{"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"}
Response — application/json
{"browser": {
"family": "Chrome",
"version": "124.0.0",
"engine": "Blink",
"client_type": "browser",
"client_name": "Chrome",
"client_version": "124.0.0"},
"os": {
"family": "Mac OS X",
"version": "10.15.7",
"arch": null},
"device": {
"type": "desktop",
"type_ua": "desktop",
"brand": null,
"model": null},
"capabilities": {
"is_mobile": false,
"is_tablet": false,
"is_pc": true,
"is_bot": false,
"bot_name": null,
"is_touch_capable": false,
"is_email_client": false}}
Parsed using user-agents (ua-parser/uap-core) enriched with device-detector
for brand/model, engine, bot identification, and client type classification.
Send the raw User-Agent string from any HTTP request; omit the header to parse your own browser.
Screenshot
Rate limited to 5 req / min.
/screenshot/capture
Request — application/json
{"url": "example.com",
"full_page": false,
"describe": false}
Response — application/json
{"image_base64": "iVBORw0KGgo...",
"description": null,
"vision_provider": null,
"vision_model": null}
Security Headers
/csp/check
Request body
{"url": "https://example.com"}
Response
{
"url": "https://example.com/",
"status_code": 200,
"has_csp": true,
"has_csp_report_only": false,
"raw": "default-src 'self'; script-src 'self' 'unsafe-inline'; ...",
"directives": {
"default-src": ["'self'"],
"script-src": ["'self'", "'unsafe-inline'"]
},
"findings": [
{
"severity": "high",
"title": "unsafe-inline in script-src",
"detail": "Inline <script> blocks and event handlers are permitted..."
},
{
"severity": "low",
"title": "HSTS missing includeSubDomains",
"detail": "Subdomains are not covered by HSTS enforcement..."
}
],
"grade": { "letter": "D", "color": "error", "label": "High-risk gaps" },
"pci_headers": {
"content-security-policy": { "label": "Content-Security-Policy", "present": true, "value": "..." },
"strict-transport-security": { "label": "Strict-Transport-Security", "present": true, "value": "..." }
}
}
WHOIS / RDAP Lookup
/whois/lookup
Request body
{"domain": "example.com"}
Response
{
"domain": "example.com",
"available": false,
"source": "rdap",
"registrar": "Example Registrar, Inc.",
"creation_date": "1995-08-14T04:00:00Z",
"expiration_date": "2026-08-13T04:00:00Z",
"updated_date": "2025-08-14T07:01:44Z",
"age_days": 11322,
"days_until_expiry": 214,
"expiring_soon": false,
"nameservers": ["a.iana-servers.net", "b.iana-servers.net"],
"status": ["client transfer prohibited"],
"dnssec": "unsigned",
"raw": "{ ...full RDAP JSON or WHOIS text... }"
}
Tries RDAP first via IANA's bootstrap registry; falls back to legacy WHOIS (raw TCP/43, via the standard IANA referral chain) for TLDs without RDAP service yet. When the domain isn't registered, the response is just {"domain": "...", "available": true, "source": "rdap"}.
App Stack
/deps/scan
Request body
{"url": "https://example.com"}
Response
{
"url": "https://example.com",
"final_url": "https://example.com/",
"bot_blocked": false,
"technologies": [
{
"name": "React",
"category": "Framework",
"evidence": "global",
"version": "18.2.0",
"url": null,
"origin": null,
"note": "",
"party": "first-party"
},
{
"name": "Google Tag Manager",
"category": "Analytics",
"evidence": "script",
"version": "",
"url": "https://www.googletagmanager.com/gtm.js?id=GTM-XXXX",
"origin": "www.googletagmanager.com",
"note": "",
"party": "third-party"
},
{
"name": "jQuery",
"category": "Library",
"evidence": "bundled",
"version": "",
"url": "https://example.com/assets/vendor.7f3a2c.js",
"origin": null,
"note": "",
"party": "first-party"
},
{
"name": "Meta Pixel",
"category": "Advertising",
"evidence": "pixel",
"version": "",
"url": "https://www.facebook.com/tr?id=123&ev=PageView",
"origin": "www.facebook.com",
"note": "",
"party": "third-party"
},
{
"name": "WordPress",
"category": "CMS",
"evidence": "meta",
"version": "",
"url": null,
"origin": null,
"note": "WordPress 6.5.2",
"party": "first-party"
}
],
"other_origins": [
{"origin": "ingest.quantummetric.com", "request_count": 9, "resource_types": ["beacon", "xhr"]},
{"origin": "assets.bounceexchange.com", "request_count": 12, "resource_types": ["iframe", "link", "script"]}
]
}
Loads the URL in a real headless browser (the same one behind Screenshot and App Map) and watches every request the page actually makes — not just <script>/<link> tags, which miss most of what a tracking-heavy page does: tracking pixels are <img> loads, most beacons are fetch/XHR/sendBeacon calls, and things like a captcha widget or an ad render live inside an iframe. Matched against a curated set of signatures — window globals, DOM markers, request URLs (by hostname, not a raw substring search, to avoid false-matching on an unrelated tracker's domain showing up inside another URL's query string), the <meta name="generator"> tag, a few response headers, and known library banner text inside same-origin scripts/stylesheets (evidence: "bundled") — plus a generic parser for any package served from jsDelivr's or unpkg's npm-package CDN paths. evidence reflects the actual resource type observed: script, link, pixel (image beacon), xhr, beacon (ping/sendBeacon), iframe, meta, header, or bundled. version and url are only ever populated from something actually observed, never inferred. origin is the hostname url was served from, or null when the evidence wasn't a network request or the resource was first-party. party is "third-party" whenever origin is set, "first-party" otherwise. other_origins lists every third-party host the page contacted that matched no curated signature — surfaced rather than silently dropped, capped at 80, sorted by request count — so a busy page's long tail of ad-tech and analytics vendors this list doesn't yet have a name for still shows up somewhere. If the page returns a bot-protection challenge instead of its real content, the response is {"bot_blocked": true, "technologies": [], "other_origins": [], ...} rather than a misleading partial map.