DomainIntel
Public API

DomainIntel API

Open, unauthenticated REST API. CORS is enabled for any origin. Responses are JSON, cached for one hour per domain. A single request returns WHOIS/RDAP, DNS, mail, TLS, web security headers, technology fingerprinting, exposure scanning and a unified Security Posture score.

Overview

DomainIntel aggregates a passive, read-only view of a domain's public surface. It calls reputable upstream sources (Cloudflare DNS-over-HTTPS, RDAP, crt.sh, Mozilla Observatory, Shodan InternetDB, HackerTarget, dns.google) and merges them into a single, additive JSON payload.

  • Read-only. No brute force, no port scanning, no exploitation.
  • Single GET request returns the full report.
  • Failures from upstream sources never break the response — they appear in meta.errors.
  • Edge-cached for 1 hour per domain (meta.cached indicates a hit).

Endpoint

GET
/api/public/domain-intel?domain=example.com
POST
/api/public/domain-intel · body: { "domain": "example.com" }

GET and POST return the exact same payload. Use POST if your client can't percent-encode the domain or you want to avoid query-string logging.

Parameters

NameLocationTypeRequiredDescription
domainquery / bodystring
yes
Domain to analyze. Sanitized server-side (lowercased, no http(s)://, no path, Punycode preserved).

Status codes

CodeMeaning
200
Successful analysis of the domain.
400
Missing domain parameter or invalid format.
429
Rate limit exceeded. Retry after the seconds listed in Retry-After.
500
Unexpected server error.

Rate limiting

30 requests per minute per IP. Exceeding the limit returns 429 with body { "error": "rate_limit_exceeded", "retryAfter": N }.

Headers present on every response:

  • X-RateLimit-Limit — max requests per window (30).
  • X-RateLimit-Remaining — requests left in the current window.
  • X-RateLimit-Reset — seconds until the window resets.
  • Retry-After — only on 429 responses.

Cached responses (meta.cached: true) still count toward your quota but skip upstream calls entirely.

Examples

curl "https://domainintel.michaelpickman.com/api/public/domain-intel?domain=stripe.com"

Try it live

GET /api/public/domain-intel?domain=stripe.com

Click Run to fetch a real response.

Example response

Trimmed for readability; arrays such as posture.findingsand technology.all are usually longer.

{
  "domain": "stripe.com",
  "summary": {
    "ageYears": 15,
    "ageDays": 5630,
    "status": "active",
    "daysUntilExpiry": 412,
    "healthScore": 92,
    "grade": "A",
    "recommendations": [],
    "posture": {
      "score": 88,
      "grade": "A",
      "categories": {
        "email": {
          "key": "email",
          "label": "Email",
          "score": 100,
          "grade": "A",
          "weight": 20
        },
        "tls": {
          "key": "tls",
          "label": "TLS / Certificate",
          "score": 95,
          "grade": "A",
          "weight": 15
        },
        "headers": {
          "key": "headers",
          "label": "Web Headers",
          "score": 85,
          "grade": "B",
          "weight": 20
        },
        "dns": {
          "key": "dns",
          "label": "DNS",
          "score": 70,
          "grade": "C",
          "weight": 10
        },
        "exposure": {
          "key": "exposure",
          "label": "Exposure",
          "score": 90,
          "grade": "A",
          "weight": 25
        },
        "hygiene": {
          "key": "hygiene",
          "label": "Domain Hygiene",
          "score": 100,
          "grade": "A",
          "weight": 10
        }
      },
      "findings": [
        {
          "id": "dns.caa",
          "severity": "medium",
          "category": "dns",
          "title": "No CAA records",
          "description": "Any Certificate Authority can issue certificates for your domain.",
          "recommendation": "Publish CAA records limiting which CAs may issue certificates for you."
        }
      ]
    }
  },
  "registration": {
    "createdDate": "2009-09-10T00:00:00Z",
    "updatedDate": "2024-08-12T00:00:00Z",
    "expiryDate": "2027-09-10T00:00:00Z",
    "registrar": "MarkMonitor Inc.",
    "statuses": [
      "clientTransferProhibited"
    ],
    "dnssec": true
  },
  "whois": {
    "registrantOrg": "Stripe, Inc.",
    "country": "US",
    "exposedContacts": []
  },
  "dns": {
    "provider": "AWS Route 53",
    "nameservers": [
      "ns-1471.awsdns-55.org",
      "ns-2049.awsdns-67.co.uk"
    ],
    "a": [
      "52.84.150.39"
    ],
    "aaaa": []
  },
  "mail": {
    "provider": "Google Workspace",
    "mx": [
      {
        "priority": 1,
        "host": "aspmx.l.google.com"
      }
    ]
  },
  "emailSecurity": {
    "spf": {
      "present": true,
      "record": "v=spf1 include:_spf.google.com ~all"
    },
    "dmarc": {
      "present": true,
      "policy": "reject",
      "record": "v=DMARC1; p=reject;"
    }
  },
  "hosting": {
    "provider": "Amazon Web Services",
    "ip": "52.84.150.39",
    "asn": "AS16509",
    "country": "US"
  },
  "ssl": {
    "issuer": "Let's Encrypt",
    "validFrom": "2026-04-01T00:00:00Z",
    "validTo": "2026-07-01T00:00:00Z",
    "daysUntilExpiry": 16,
    "expired": false,
    "sans": [
      "stripe.com",
      "*.stripe.com"
    ],
    "certificatesFound": 124
  },
  "reputation": {
    "ip": "52.84.150.39",
    "listed": false,
    "lists": [],
    "checked": [
      "zen.spamhaus.org",
      "bl.spamcop.net"
    ]
  },
  "subdomains": {
    "checked": [
      "www",
      "api",
      "mail"
    ],
    "found": [
      {
        "name": "api.stripe.com",
        "ips": [
          "52.84.150.40"
        ]
      }
    ]
  },
  "technology": {
    "server": "nginx",
    "language": null,
    "cms": null,
    "cdn": "Cloudflare",
    "ecommerce": null,
    "frameworks": [
      "React"
    ],
    "analytics": [
      "Google Analytics"
    ],
    "all": [
      {
        "name": "Cloudflare",
        "category": "cdn",
        "confidence": 100,
        "evidence": "server: cloudflare"
      }
    ]
  },
  "security": {
    "headers": {
      "score": 85,
      "grade": "B",
      "checks": [
        {
          "id": "hsts",
          "label": "Strict-Transport-Security",
          "status": "pass",
          "detail": "max-age=63072000; includeSubDomains",
          "why": "Forces HTTPS in browsers and prevents SSL downgrade attacks."
        }
      ],
      "missing": [],
      "raw": {
        "strict-transport-security": "max-age=63072000; includeSubDomains"
      }
    },
    "cookies": {
      "total": 1,
      "insecure": []
    },
    "httpsRedirect": {
      "redirects": true,
      "finalUrl": "https://stripe.com/",
      "chain": []
    },
    "tls": {
      "source": "mozilla-observatory",
      "grade": "A+",
      "score": 110,
      "protocols": [
        "TLSv1.2",
        "TLSv1.3"
      ],
      "weakProtocols": [],
      "vulnerabilities": [],
      "raw": null
    },
    "dnsSecurity": {
      "dnssec": true,
      "caa": {
        "present": false,
        "records": []
      },
      "dkim": {
        "present": true,
        "selectorsTested": [
          "google",
          "default"
        ],
        "selectorsFound": [
          "google"
        ]
      },
      "mtaSts": {
        "dnsPresent": true,
        "policyPresent": true,
        "mode": "enforce"
      },
      "bimi": {
        "present": false,
        "record": null
      },
      "spf": {
        "present": true
      },
      "dmarc": {
        "present": true,
        "policy": "reject"
      }
    }
  },
  "exposure": {
    "publicFiles": {
      "securityTxt": {
        "present": true,
        "contact": [
          "mailto:security@stripe.com"
        ],
        "policy": [],
        "expires": null
      },
      "robotsTxt": {
        "present": true,
        "sitemaps": [
          "https://stripe.com/sitemap.xml"
        ]
      },
      "sitemapXml": {
        "present": true
      },
      "humansTxt": {
        "present": false
      }
    },
    "sensitiveFiles": [],
    "directoryListing": {
      "found": false,
      "urls": []
    },
    "subdomains": {
      "source": "crt.sh",
      "total": 1240,
      "alive": [
        {
          "name": "api.stripe.com",
          "ips": [
            "52.84.150.40"
          ],
          "cname": null
        }
      ],
      "sample": [
        "api.stripe.com",
        "dashboard.stripe.com"
      ]
    },
    "takeoverRisks": [],
    "ports": {
      "ip": "52.84.150.39",
      "ports": [
        80,
        443
      ],
      "cves": [],
      "tags": [
        "cdn"
      ],
      "hostnames": [
        "stripe.com"
      ]
    }
  },
  "meta": {
    "queriedAt": "2026-06-15T10:00:00.000Z",
    "sources": [
      "cloudflare-dns",
      "rdap.org",
      "crt.sh",
      "hackertarget",
      "mozilla-observatory",
      "internetdb.shodan.io"
    ],
    "cached": false,
    "errors": []
  }
}

Security Posture model

summary.posture is the unified verdict. Each of six categories produces a 0-100 sub-score and an A-F grade. The global score is the weighted average; the global grade is derived from the global score using the same thresholds (A ≥ 90, B ≥ 75, C ≥ 60, D ≥ 40, F < 40).

CategoryWeightWhat it measures
email20%SPF, DMARC policy strength, DKIM selectors, MTA-STS.
tls15%CT log presence, expiry windows, obsolete TLS protocols.
headers20%HSTS, CSP quality, XFO/XCTO, Referrer-Policy, Permissions-Policy, cookie flags, HTTPS redirect.
dns10%DNSSEC enabled and CAA records published.
exposure25%Sensitive files, directory listing, takeover risks, Shodan CVEs, DNSBL listings.
hygiene10%Domain expiry runway, WHOIS privacy, registrar visibility.

Sub-scores start at 100 and are decremented per finding. Findings are sorted by severity in the API response, so a UI can render them as a prioritized to-do list.

Finding severities

SeverityWhen it's used
criticalActive exposure or guaranteed breakage (expired cert, exposed .env, takeover ready).
highSignificant risk with a clear short-term fix (no DMARC, missing HSTS, cert expiring in <30d).
mediumHardening gap that lets attacks succeed when paired with other weaknesses.
lowBest-practice nice-to-have; safe to defer.
infoContextual note, no action required.

Data sources

SourceUsed for
cloudflare-dns / dns.googleDNS resolution over HTTPS (A, AAAA, MX, TXT, CAA, CNAME, DNSSEC AD bit).
rdap.orgWHOIS/RDAP registration data: dates, registrar, statuses, secureDNS.
crt.shCertificate Transparency logs for SSL/TLS history and CT-based subdomain discovery.
mozilla-observatoryHolistic TLS and security-header grade.
internetdb.shodan.ioOpen ports, tags and known CVEs for the primary IP. No API key required.
hackertargetHosting/ASN lookups and supplementary DNS data.
DNSBL queriesReputation checks (Spamhaus, SpamCop, etc.).
Live HTTP fetchHomepage retrieval for headers, cookies, redirects and technology rules.

Full response schema

FieldTypeDescription
domainstringSanitized domain that was analyzed.
summary.ageYearsnumber | nullDomain age in whole years.
summary.ageDaysnumber | nullTotal domain age in days.
summary.status"active" | "expiring_soon" | "expired" | "unknown"Derived lifecycle status of the domain.
summary.daysUntilExpirynumber | nullDays until expiry (negative if expired).
summary.healthScorenumber (0-100)Legacy health score (kept for compatibility).
summary.grade"A" | "B" | "C" | "D" | "F"Legacy health grade.
summary.recommendationsstring[]Legacy plain-text recommendations list.
summary.posturePostureUnified Security Posture report (categories + findings).
summary.posture.scorenumber (0-100)Weighted global posture score.
summary.posture.grade"A"–"F"Posture grade derived from the score.
summary.posture.categoriesRecord<6 keys, PostureCategory>Per-category sub-score, grade and weight: email, tls, headers, dns, exposure, hygiene.
summary.posture.findingsPostureFinding[]Findings sorted by severity (critical → info), each with id, category, title, description and recommendation.
registration.createdDateISO string | nullOriginal registration date.
registration.updatedDateISO string | nullLast registry update.
registration.expiryDateISO string | nullDomain expiry date.
registration.registrarstring | nullRegistrar detected via RDAP.
registration.statusesstring[]EPP domain statuses.
registration.dnssecbooleanDNSSEC enabled flag from RDAP secureDNS.
whois.registrantOrgstring | nullRegistrant organization.
whois.countrystring | nullRegistrant country code.
whois.exposedContacts{ kind, value }[]Public contact addresses found in registration data.
dns.providerstring | nullDNS provider inferred from nameservers.
dns.nameserversstring[]Authoritative nameservers.
dns.a / dns.aaaastring[]A / AAAA records.
mail.providerstring | nullMail provider inferred from MX hosts.
mail.mx{ priority, host }[]MX records ordered by priority.
emailSecurity.spf{ present, record }SPF status and TXT record.
emailSecurity.dmarc{ present, policy, record }DMARC status, policy (p=) and raw record.
hosting.providerstring | nullHosting provider inferred from ASN.
hosting.ipstring | nullPrimary IP address.
hosting.asnstring | nullProvider ASN.
hosting.countrystring | nullServer country code.
ssl.issuerstring | nullLatest certificate issuer (CT logs / crt.sh).
ssl.validFrom / validToISO | nullValidity window of the latest certificate.
ssl.daysUntilExpirynumber | nullDays until the latest cert expires.
ssl.expiredbooleanWhether the latest cert has expired.
ssl.sansstring[]Subject Alternative Names on the latest cert.
ssl.certificatesFoundnumberTotal certificates seen in Certificate Transparency logs.
reputation.listedbooleanWhether the primary IP appears on a checked DNSBL.
reputation.listsstring[]DNSBLs that listed the IP.
reputation.checkedstring[]DNSBLs that were queried.
subdomains.checked / foundstring[] / { name, ips }[]Probed common subdomains and those that resolved.
technologyTechnology | nullDetected tech stack (null if homepage unreachable).
technology.server / language / cms / cdn / ecommercestring | nullBest-match labels per category.
technology.frameworks / analyticsstring[]Lists of matched frameworks and analytics tools.
technology.all{ name, category, confidence, evidence }[]Every match with category, 0-100 confidence and the rule that fired.
security.headers{ score, grade, checks[], missing[], raw } | nullPer-header analysis, 0-100 score and grade.
security.cookies{ total, insecure[] } | nullCookies from the homepage with missing flags.
security.httpsRedirect{ redirects, finalUrl, chain }HTTP→HTTPS redirect trace.
security.tls{ source, grade, score, protocols[], weakProtocols[], vulnerabilities[], raw }TLS grade and vulnerabilities from Mozilla Observatory.
security.dnsSecurity{ dnssec, caa, dkim, mtaSts, bimi, spf, dmarc }DNS-level security controls: DNSSEC, CAA, DKIM (selector probes), MTA-STS, BIMI.
exposure.publicFiles{ securityTxt, robotsTxt, sitemapXml, humansTxt }Informational public files. security.txt is parsed for contact/policy.
exposure.sensitiveFiles{ path, url, status, severity, evidence }[]Sensitive files detected (.env, .git/config, backup.zip…) with content validation.
exposure.directoryListing{ found, urls[] }Open 'Index of /' directories found on common paths.
exposure.subdomains{ source, total, alive[], sample[] } | nullCertificate Transparency subdomain discovery with DNS liveness probes.
exposure.takeoverRisks{ subdomain, cname, service, reason }[]Dangling CNAMEs that match known takeover-prone services.
exposure.ports{ ip, ports, cves, tags, hostnames } | nullShodan InternetDB summary for the primary IP (ports, known CVEs, tags).
meta.queriedAtISO stringServer-side timestamp of the analysis.
meta.sourcesstring[]Upstream sources that were queried (cloudflare-dns, rdap.org, crt.sh, mozilla-observatory, internetdb.shodan.io…).
meta.cachedbooleantrue when the response was served from the 1-hour cache.
meta.errors{ source, message }[]Per-source partial failures. Never break the whole response.

Caching & partial failures

Responses are cached at the edge for 1 hour per sanitized domain. When a response is served from cache, meta.cached is true.

Upstream sources fail independently. If RDAP times out, the response still contains DNS, mail, technology, security and exposure — only registration will be null and a record will be appended to meta.errors:

{
  "meta": {
    "errors": [
      { "source": "rdap.org", "message": "fetch timeout after 6s" }
    ]
  }
}

Always check meta.errors when building dashboards — missing fields signal a partial failure rather than "no data".

Responsible use

DomainIntel only queries public sources and the homepage of the analyzed domain. It does not perform port scans, vulnerability exploitation, or authenticated requests.

You are responsible for complying with the terms of service of upstream providers and with local laws when using this API on domains you do not own.