Network, DNS, TLS and email intelligence as a paid API. Per-call micropayments over x402 — no account, no API key, no minimum, no subscription.
| Endpoint | Price | What it does |
|---|---|---|
| GET /whoami | FREE | Everything netintel can see about your connection: IP, geo, ASN, TLS version and cipher, HTTP protocol, Cloudflare colo, TCP RTT. |
| POST /dns | $0.002 | Any record type over DNS-over-HTTPS, with TTLs, RCODE, the authoritative flag, DNSSEC validation status from the AD bit, and per-type timing. |
| POST /email | $0.003 | A full deliverability audit of one address without sending anything to it. |
| POST /tls | $0.004 | Certificate and HTTPS posture, graded A–F. |
| POST /ip | $0.002 | An IPv4/IPv6 address, a CIDR block or a hostname in; network classification, reverse DNS, ASN and blocklist status out. |
| POST /propagation | $0.005 | One record, up to eleven public resolvers, all queried at the same time. |
| GET / | FREE | This page. |
| GET /openapi.json | FREE | OpenAPI 3.1 specification of every route. |
| GET /health | FREE | {"ok":true} |
GET /whoami is free, foreverIt is the best possible demo of what an edge-hosted network API can see, it needs no payment and no client library, and it returns exactly the shape of data the paid endpoints return for hosts you name.
curl -s https://netintel.example/whoami
{
"ip": "203.0.113.9",
"ipVersion": "IPv4",
"geo": { "country": "GB", "region": "England", "city": "Slough",
"timezone": "Europe/London", "latitude": 51.50949, "longitude": -0.59541,
"continent": "EU", "postalCode": "SL1", "countryIsEU": false },
"network": { "asn": 2856, "asOrganization": "BTBROADBAND", "colo": "LHR" },
"tls": { "version": "TLSv1.3", "cipher": "AEAD-AES256-GCM-SHA384" },
"http": { "protocol": "HTTP/2", "userAgent": "curl/8.14.1" },
"performance": { "clientTcpRttMs": 7 }
}
Fields Cloudflare did not supply come back null — never guessed. Then point
/ip or /tls at any host you like.
Any record type over DNS-over-HTTPS, with TTLs, RCODE, the authoritative flag, DNSSEC validation status from the AD bit, and per-type timing. Every answer is cross-checked against a second independently-operated resolver and any disagreement is reported with an interpretation — a genuinely useful signal for stale caches, split-horizon DNS and interception.
curl -s -X POST https://netintel.example/dns \
-H 'content-type: application/json' \
-d '{"name":"cloudflare.com","types":["A","AAAA","MX","CAA"],"dnssec":true}'
{
"name": "cloudflare.com",
"records": {
"A": {
"status": "NOERROR",
"authenticatedData": true,
"answers": [{ "type": "A", "ttl": 300, "data": "104.16.132.229" }],
"ms": 41,
"crossCheck": { "resolver": "google", "agrees": true, "ms": 38 }
},
"CAA": {
"answers": [{ "type": "CAA", "data": "0 issue \"letsencrypt.org\"",
"parsed": { "flags": 0, "tag": "issue", "value": "letsencrypt.org" } }]
}
},
"dnssec": { "validation": "secure", "authenticatedData": true },
"disagreements": [],
"warnings": []
}
A full deliverability audit of one address without sending anything to it. Real RFC 5322 parsing, MX reachability, an SPF DNS-lookup count that actually expands every include, DMARC with organizational-domain fallback, DKIM selector probing with key sizes, and disposable/role/free-provider classification — ending in a 0–100 score whose every point is explained.
p= to report algorithm and bit length, so weak 512/1024-bit keys are caughtcurl -s -X POST https://netintel.example/email \
-H 'content-type: application/json' \
-d '{"email":"billing@paypal.com"}'
{
"score": 91, "grade": "A", "verdict": "high",
"syntax": { "valid": true, "domain": "paypal.com" },
"mx": { "present": true, "count": 6, "allResolve": true,
"primaryExchange": "mx1.paypal.com", "provider": "Proofpoint" },
"spf": { "present": true, "allQualifier": "-", "allMeaning": "fail (hard, recommended)",
"dnsLookups": { "totalLookups": 9, "limit": 10, "exceedsLimit": false, "exact": true } },
"dmarc": { "present": true, "policy": "reject", "percent": 100,
"spfAlignment": "strict", "strength": "strong" },
"dkim": { "foundCount": 1, "found": [{ "selector": "default", "keySize": 2048 }] },
"classification": { "disposable": false, "roleAccount": true, "freeProvider": false },
"breakdown": [
{ "signal": "dmarc-reject", "delta": 2, "reason": "DMARC p=reject — the strongest setting." },
{ "signal": "role-account", "delta": -10, "reason": "\"billing\" is a role address…" }
]
}
Certificate and HTTPS posture, graded A–F. netintel performs its own TLS 1.2 handshake over a raw TCP socket and parses the X.509 DER itself — which is why it can show you an expired or self-signed certificate that a normal HTTPS client refuses to connect to at all.
expired.badssl.com and self-signed.badssl.com: a Worker's fetch() cannot, because it validates first and hangs upcurl -s -X POST https://netintel.example/tls \
-H 'content-type: application/json' \
-d '{"host":"expired.badssl.com"}'
{
"grade": "F", "score": 33,
"certificate": {
"subject": { "commonName": "*.badssl.com" },
"issuer": { "commonName": "COMODO RSA Domain Validation Secure Server CA" },
"validity": { "notAfter": "2015-04-12T23:59:59.000Z",
"daysUntilExpiry": -4128, "expired": true },
"serialNumber": "4E5BC1FE0D26F1E38A4B1E4F1F4B24F1",
"signatureAlgorithm": { "name": "sha256WithRSAEncryption", "weak": false },
"publicKey": { "algorithm": "RSA", "keySize": 2048 },
"hostnameMatch": { "matches": true, "viaWildcard": true }
},
"certificateIssues": ["Certificate EXPIRED 4128 day(s) ago…"],
"handshake": { "negotiatedVersion": "TLSv1.2",
"cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256", "forwardSecrecy": true },
"verification": { "trustPathVerified": false, "revocationChecked": false, "note": "…" }
}
An IPv4/IPv6 address, a CIDR block or a hostname in; network classification, reverse DNS, ASN and blocklist status out. Private addresses are welcome input and fully described — netintel just never opens a connection to anything you name here, so it cannot be turned into a network scanner.
curl -s -X POST https://netintel.example/ip \
-H 'content-type: application/json' \
-d '{"ip":"8.8.8.8"}'
{
"ip": "8.8.8.8", "version": "IPv4",
"network": { "class": "public", "isPublic": true,
"description": "globally routable public unicast" },
"reverseDns": { "present": true, "primary": "dns.google" },
"forwardConfirmedReverseDns": { "confirmed": true,
"note": "Forward-confirmed: the PTR hostname resolves back to this address." },
"asn": { "asn": 15169, "asName": "GOOGLE", "organization": "Google LLC, US",
"prefix": "8.8.8.0/24", "countryCode": "US", "registry": "ARIN",
"upstreamPeers": ["AS2914", "AS3257", "AS6461", "AS6939"] },
"blocklists": { "checked": 6, "listedCount": 0, "clean": true,
"unavailable": [{ "zone": "zen.spamhaus.org",
"reason": "Spamhaus blocks queries arriving via public/open resolvers…" }] }
}
One record, up to eleven public resolvers, all queried at the same time. Tells you each resolver's answer, TTL, latency and RCODE, groups them into distinct answer sets, and says whether the record has actually propagated — or whether one operator is filtering it.
cloudflare, google, opendns, adguard, adguardFiltered, dnssb, nextdns, controld, cleanbrowsing, dnspod, alidnswarnings[], never counted as a disagreementcurl -s -X POST https://netintel.example/propagation \
-H 'content-type: application/json' \
-d '{"name":"example.com","type":"A"}'
{
"name": "example.com", "type": "A",
"consistent": true, "agreementRatio": 1,
"resolversQueried": 8, "resolversAnswered": 8, "distinctAnswerSets": 1,
"majorityAnswer": { "answers": ["104.20.23.154", "172.66.147.243"], "resolverCount": 8 },
"resolvers": [
{ "id": "cloudflare", "operator": "Cloudflare", "ok": true, "ms": 38,
"answers": ["104.20.23.154", "172.66.147.243"], "agreesWithMajority": true },
{ "id": "dnspod", "operator": "Tencent", "ok": true, "ms": 210,
"answers": ["104.20.23.154", "172.66.147.243"], "agreesWithMajority": true }
],
"interpretation": "All 8 responding resolvers returned identical data…"
}
Every paid endpoint speaks x402 v2. There is nothing to sign up for.
HTTP 402 and a PAYMENT-REQUIRED header
containing a base64 challenge: the amount, the asset (USDC), the network (eip155:8453, Base mainnet) and the
recipient.X-PAYMENT header.PAYMENT-RESPONSE header with the settlement receipt.A client library does all of this for you:
import { wrapFetchWithPayment } from "@x402/fetch";
const pay = wrapFetchWithPayment(fetch, wallet);
const r = await pay("https://netintel.example/tls", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ host: "github.com" }),
});
console.log((await r.json()).certificate.validity.daysUntilExpiry);
Every external data source used here was tested from inside the Workers runtime before being shipped. Sources that don't work from a Worker are named in the response that would otherwise have used them, with the reason, instead of being quietly dropped or faked.
ocspUrls and crlUrls are returned so you can check yourself. validity.expired and
hostnameMatch are computed locally and are reliable.RCPT TO, Cloudflare blocks outbound port 25, and probing is how a sender gets blocklisted.
catchAll.determinable is always false and carries a reasoned likelihood instead.fetch() does not expose it, so it is
derived from the ALPN protocol in our own handshake and labelled with that source.request.cf.tlsVersion and
friends describe your connection to us. They appear only in /whoami, never in /tls./tls is the only endpoint that makes an outbound connection to a host you name, and it refuses:
private, loopback, link-local, CGNAT, reserved, documentation and multicast address space; cloud-metadata addresses
including 169.254.169.254; any scheme other than http/https; internal-looking suffixes such as
.internal, .local and .localhost; and every port that is not a TLS port.
Crucially, a hostname is resolved first and every address it resolves to is re-checked, which is what defeats DNS
rebinding — localtest.me, 127.0.0.1.nip.io and friends all resolve to loopback and are refused.
The handshake then connects to that pinned, validated address with the hostname sent only as SNI, so no DNS answer can
change between the check and the connection. Redirects are re-validated at every hop.
/ip will happily describe a private address but never connects to one.
Every error is a JSON object with a stable machine-readable code:
{ "error": { "code": "target_not_allowed",
"message": "Target 169.254.169.254 is link-local (RFC 3927 link-local — includes cloud metadata 169.254.169.254); connections to non-public address space are refused.",
"details": { "ip": "169.254.169.254", "class": "link-local", "isCloudMetadata": true } } }
| Status | Meaning | Charged? |
|---|---|---|
400 | Malformed input — validated before the payment challenge. | No |
402 | Payment required; the challenge is in the PAYMENT-REQUIRED header. | No |
403 | Target refused by the abuse guard. | No |
404 | Host or domain does not exist (NXDOMAIN). | No |
413 | Request body too large. | No |
422 | Well-formed but unactionable — e.g. a hostname with no address records. | No |
500 | Our fault. Settlement is skipped. | No |
502 | Payment facilitator unreachable. | No |
Partial success is never silently hidden. Every response carries a warnings[] array, and any
sub-lookup that failed appears there with its field set to null rather than to a misleading empty or negative
result. /email and /ip additionally list every failure in failedLookups[].
A single dead resolver degrades one field; it never fails your request.