IPure IP purity check

Developers / agents

API docs

Everything on a report page is available as JSON: one call returns the score, proxy / VPN / Tor detection, blocklists and abuse history, per-use-case suitability, every deduction with its source, and the limits of the data. No API key, no sign-up. The machine-readable description is at /openapi.json; the site summary for models is at /en/llms.txt.

Endpoints

v1 endpoints, plus the legacy shapes

GET /api/v1/ip/{ip}
Recommended for new integrations. The report is in data; the envelope carries request_id, schema_version, checked_at, confidence, cache, score_model_version, a summary for agents, sources and unknowns. Add ?lang=en for English.
GET /api/v1/ip/{ip}/history?limit=20
Current report + previous checks, newest first. Read-only: no new lookup and no data-source cost. limit defaults to 20, maximum 200; has_more means older records exist. change_type separates evidence changes from score-model changes; add ?lang=en for English labels.
GET /en/ip/{ip}
The report as a web page, for people, search engines and AI browsing tools.
GET /en/ip/{ip}.json
Legacy bare report as JSON. Identical to /api/lookup?ip={ip}&lang=en — same limits, same allowance.
GET /en/ip/{ip}.txt
A 72-column plain-text report. No HTML or JSON parsing needed.

v1 envelope: every response carries request_id, schema_version, checked_at, confidence, cache (fresh / cache / store), score_model_version (null for reports generated before version tracking), summary (the same decision summary the MCP tools return), sources and unknowns. Errors use a stable error.code; read that instead of matching the message. The report itself is in data, without a second copy of sources / unknowns / scoreModelVersion. History items add score_model_version and change_type (evidence_change / model_change / mixed / unknown / none). If your agent browsing environment blocks /api/ paths, keep using /en/ip/{ip}.json; the data is the same.

Examples: /en/ip/8.8.8.8 · /en/ip/8.8.8.8.json · /en/ip/8.8.8.8.txt. Drop the /en prefix for the Chinese versions. Some AI browsing environments refuse API-style URLs with query parameters; the path-style addresses above are not affected, and every report page also embeds the same JSON in script#ipure-report. GET /api/myip?format=text echoes the caller's public IP.

MCP

Hosted MCP server for agents

Endpoint: https://ipure.dev/mcp (Streamable HTTP, no auth). Three tools: inspect_ip, assess_ip_for_use_case and compare_egress_ips. They return a compact summary plus report_url; use the v1 API when you need every field. Tool calls share the same gate, rate limits and unverified allowance as the HTTP API.

Claude Code

claude mcp add --transport http ipure https://ipure.dev/mcp

Other MCP clients: set the URL to https://ipure.dev/mcp and use HTTP / Streamable HTTP transport. Or use this config:

mcp.json

{
  "mcpServers": {
    "ipure": { "type": "http", "url": "https://ipure.dev/mcp" }
  }
}

Limits

Who can call it, and how much

  • IPs already in the database return instantly, cost nothing and can be requested by any client.
  • IPs not yet in the database trigger live queries to several upstream sources. Scripts and agents get 5 such lookups per source IP per day without verification (there is also a site-wide daily total); the remainder is in the x-open-budget-remaining header. When it runs out the API returns 403 with code = verification_required and a reportUrl: open it in a browser once and the IP is in the database from then on. Site-wide, 15 unverified lookups are admitted per minute; beyond that you get 429 with code = open_rate_limited — wait for Retry-After and retry.
  • Rate limits: 30 requests / minute; 10 first-time lookups of new IPs / minute. Over the limit returns 429.
  • Freshness: stored reports carry queriedAt and a stale flag. An IP's nature rarely changes, so a stored report is usually fine to use as is.
  • Bulk or scheduled checks: please get in touch via the feedback button on ipure.dev rather than scanning ranges.

Fields

What the response contains

ip / versionNormalised address and IP version (4 / 6)
queriedAtWhen the report was generated. With source = store this is the original check time; read it together with stale
sourcefresh (live lookup) · cache (in-process) · store (database)
usageTyperesidential · mobile · business · hosting · education · government · unknown
nativeTypenative · broadcast (registered in one country, used in another) · unknown
flagsisProxy / isVpn / isTor / isHosting / isMobile / isRelay / isBogon / isSearchEngineBot. Binary fields, kept for compatibility
flagAgreementHow many data sources agree on each flag. A single-source flag counts at half weight in the score
proxyAssessmentEvidence strength for proxy / VPN: status (confirmed / suspected / not_detected / unknown), kind (tor / vpn / proxy / none), confidence, positive_sources. Prefer this in summaries; never present a single-source suspected flag as confirmed
vpnOperatorFor known commercial VPNs: name, anonymity, popularity, protocols, logging, freeAccess. Otherwise null
risk.purityPurity 0-100; higher is cleaner
risk.level / label / verdictSix bands: pristine · clean · neutral · suspicious · risky · dangerous, with a one-line verdict
risk.confidencelow / medium / high — how much data source coverage backs the score. Read score and confidence separately
risk.factors[]Where every point came from: label, category, points (positive deducts, negative adds back), detail, floor (a minimum risk that cannot be offset), explanation (meaning · cares: which uses care · weighting · remedy, the same text the report page shows when a row is expanded)
scenarios[]Suitability per use case: ai · social · streaming · gaming · ecommerce · email, each with score, level, levelLabel and reason. score is null when level is restricted or not_applicable
scenarioApplicablefalse means the address is public infrastructure (a public DNS resolver, a search engine crawler), not anyone's egress IP; the reason is in scenarioNote
blocklists[]Each public blocklist: listed; benign (listed but not a risk, e.g. a PBL residential declaration); unavailable (could not be queried, so unknown)
abuse[]Abuse reports (score, reports) and recorded attack activity (attacks.byType: login attempts, bulk registration, vulnerability probing …). Raw reports are not confirmed abuse; score = 0 entries carry a note explaining the 0% confidence
exposure / sharingNetwork exposure (open ports, known vulnerabilities) and sharing (devices observed on the address / in the subnet)
sources[]Status of each data source for this report: ok, ms, error, skipped. A failed source is not a clean result. v1 puts this in the envelope; legacy paths put it in the report
feedbackReal-world experience ratings (1-5, 5 is best) per use case from visitors who were actually using this IP. Shown once a use case has 3 ratings. Independent of the score
unknowns[]What cannot be determined from an IP alone — see below. Always pass these on. v1 puts this in the envelope; legacy paths put it in the report
reportUrlThe human-readable report. Cite it with any conclusion
lang / untranslatedEnglish responses carry lang = en. untranslated lists any deduction ids that had no English text (normally empty)

Limits of the data

What an IP check cannot tell you

Conclusions reflect public and partner data at query time. They are not a guarantee of account safety or platform access.

  • Each platform's internal risk labels

    Platforms keep their own IP reputation data and ban records and do not publish them. No hit in public sources does not mean a platform has nothing on this IP.

  • Whether the IP is truly exclusive

    Sharing is only the number of devices observed in the same subnet. It cannot show who else is using this exit right now, or what they are doing.

  • Which accounts this IP has been tied to

    Account-to-IP links exist only inside each platform. Accounts previously banned on this IP are invisible to us.

  • Device, browser and behaviour

    Risk systems look at IP + device fingerprint + behavioural rhythm together. A clean IP with an odd fingerprint or behaviour still gets blocked.

  • Whether the account profile matches the IP

    When sign-up country, phone number, payment method, language or time zone disagree with the IP's location, platforms verify further. That is unrelated to IP purity.

Examples

Calling v1

curl · current report

curl -s "https://ipure.dev/api/v1/ip/8.8.8.8?lang=en" | jq '{id: .request_id, purity: .data.risk.purity, verdict: .data.risk.verdict, cache: .cache, sources: [.sources[] | select(.ok) | .name]}'

curl · check history

curl -s "https://ipure.dev/api/v1/ip/8.8.8.8/history?limit=5&lang=en" | jq '{count, has_more, history: [.history[] | {checked_at, current, purity, level, change_type}]}'

Python

import requests

r = requests.get("https://ipure.dev/api/v1/ip/8.8.8.8?lang=en", timeout=30)
r.raise_for_status()
body = r.json()
report = body["data"]
print(body["request_id"], body["cache"], report["risk"]["purity"], report["risk"]["verdict"])
if not report["scenarioApplicable"]:
    print(report["scenarioNote"])
for s in report["scenarios"]:
    print(s["label"], s["score"], s["levelLabel"], "-", s["reason"])
print("Cannot be determined:", [u["label"] for u in body["unknowns"]])

TypeScript

const res = await fetch("https://ipure.dev/api/v1/ip/8.8.8.8?lang=en");
if (!res.ok) {
  const { error } = await res.json();
  throw new Error(`IPure ${error.code}: ${error.message}`);
}
const { data: report, sources, unknowns } = await res.json();
const ai = report.scenarios.find((s: { id: string }) => s.id === "ai");
console.log(report.risk.purity, ai?.levelLabel, sources.length, unknowns.length);

Terminal (reports are in Chinese unless you add lang=en)

curl -L "ipure.dev/cli/8.8.8.8?lang=en&color=0"

For agents

When to call it, and when not to

Good fit

“Is this IP clean?” “Can I use this proxy for ChatGPT / signing up on X / an Amazon seller account / Netflix?” “Is it a VPN or data center IP?” “Why do I keep getting flagged?” Also: checking your own egress IP before a network task, or comparing several candidate IPs.

Not a fit

DNS configuration, deployment, connectivity troubleshooting, or anything that needs a platform's internal risk data. When quoting a conclusion, pass on unknowns too, and never turn “no risk signals found” into “safe”.