Back

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

GET /ip/raw

Response — text/plain

203.0.113.42
GET /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.

POST /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.

POST /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_securitySPF, DMARC, and DKIM (probes common selectors)
dnssecValidates against trusted resolvers via the AD flag, not just presence of DNSKEY/DS
zone_transferAttempts AXFR against each authoritative nameserver
multi_resolverCompares the A record across Google, Cloudflare, Quad9, and OpenDNS
delegationChecks whether the domain's own nameservers agree on its NS set

CDN Detection

Rate limited to 20 req / min.

POST /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.

POST /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.

POST /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.

POST /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

POST /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

POST /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

POST /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.