/v1/ip
Screens an IP address for reserved/private ranges, cloud-hosting origin, VPN ranges, and Tor exit nodes.
- METHOD
- POST
- BODY CAP
- 4 KiB
- CHECKS
- 5
- FINDING CODES
- 4
POST /v1/ip screens one IP address, IPv4 or IPv6. Use it on the signup or contact-form
IP a caller already has — most integrations send it alongside an email address via
/v1/emails’s optional ip field or /v1/signups, rather than calling /v1/ip on its
own, but it’s a real gate in its own right.
Request#
{ "ip": "8.8.8.8" }
| Field | Type | Required | Notes |
|---|---|---|---|
ip |
string | yes | IPv4 or IPv6. An unparseable value is a 400 invalid_request, not a finding — this is the gate’s one required input. |
There are no optional fields yet.
Request body cap: 4 KB — far more than a single address needs, with headroom for future optional fields.
Checks this gate runs#
Every check here is pure rule-based computation — no network calls, no model inference — so all of them are free on every plan (CLAUDE.md design rule 4: “Never withhold a zero-marginal-cost check”).
| Check | What it does |
|---|---|
ip.not_public |
Private, loopback, link-local, carrier-grade NAT, documentation/benchmarking and reserved ranges — see ip.not_public. |
ip.hosting_provider |
Match against published AWS/GCP/Oracle/DigitalOcean/Azure IP ranges — see ip.hosting_provider. Also contributes data.hosting_provider. |
ip.tor_exit |
Match against the Tor Project’s bulk exit-node list — see ip.tor_exit. |
ip.vpn_provider |
Match against a list of known commercial VPN ranges — see ip.vpn_provider. |
ip.asn |
Contributes data.asn (the autonomous system number) and data.asn_org (its registered name) for routed addresses. Emits no findings. |
The VPN list is open-source and incomplete by nature; it catches the common providers, not all of them. Paid residential-proxy and VPN reputation is a separate, later check.
Example#
curl -s -X POST https://api.stormdoor.dev/v1/ip \
-H "Authorization: Bearer sk_live_..." \
-H "content-type: application/json" \
-d '{"ip":"3.5.140.1"}'
{
"ok": true,
"verdict": "suspect",
"findings": [
{
"code": "ip.hosting_provider",
"severity": "medium",
"message": "\"3.5.140.1\" is in a published aws IP range"
}
],
"data": { "hosting_provider": "aws", "asn": 16509, "asn_org": "AMAZON-02" },
"checks_run": ["ip.asn", "ip.hosting_provider", "ip.not_public", "ip.tor_exit", "ip.vpn_provider"],
"checks_skipped": [],
"meta": { "gate": "ip", "ruleset": "reference-..." }
}
See response format for how checks_skipped reasons work, and don’t
proxy that array to your own end users.
Finding codes#
Each code links to its own page with what it looks at, why it matters and what to do.