VPNDetection MCP Server
OfficialAnonymity detection for IP addresses plus VPNDetection database catalog and license introspection, all read-only.
Classify a single IPv4/IPv6 address as VPN, residential/datacenter/mobile/public proxy, Tor node, privacy relay or web gateway, hosting provider or CDN, with provider names and detail where the plan allows.
Batch-classify a whole list of addresses in one call (e.g. for log triage), keyed by address with per-address errors instead of failing the batch.
Check the API key's plan, field tier, request usage, allowance and reset date.
List published database families with the organization's license standing (licensed, expired, unlicensed), versions and formats.
Inspect a database without downloading it: columns and types, sample rows, row count, build date, update frequency and file sizes.
Retrieve published MD5/SHA-1/SHA-256/SHA-512 checksums for a database file.
Review the organization's recent download attempts, including refusals, to diagnose failed fetches.
See coverage metadata on each lookup explaining which fields were included or withheld by the plan.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@VPNDetection MCP Serveris 45.83.91.1 a VPN?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
VPNDetection MCP Server
The official Model Context Protocol server for the VPNDetection API.
It gives an AI agent seven read-only tools for anonymity detection: whether an address belongs to a VPN, a residential, datacenter, mobile or public proxy, a Tor node, a privacy relay or web gateway, a hosting provider or a CDN, plus the database catalog and where your organization's license for each one stands.
Getting Started
We host it at https://mcp.vpndetection.io/mcp, and you sign in to it with your VPNDetection account. It also runs on your own machine from npm.
In Claude
In claude.ai, the desktop app or Cowork, add https://mcp.vpndetection.io/mcp as a custom connector and choose Use Claude's published identity when asked. In Claude Code, install our plugin, which adds the server with skills:
/plugin marketplace add vpndetection-io/claude-plugin
/plugin install vpndetection@vpndetectionEither way you sign in with your VPNDetection account, and Claude never sees your API key. The steps, the skills and how to disconnect: docs.vpndetection.io/integrations/claude.
In ChatGPT and Codex
In ChatGPT on the web, add https://mcp.vpndetection.io/mcp as a custom MCP server under Plugins and choose OAuth. In Codex, install our plugin, which adds the server with skills:
codex plugin marketplace add vpndetection-io/openai-plugin
codex plugin add vpndetection@vpndetection
codex mcp login vpndetectionEither way you sign in with your VPNDetection account, and neither sees your API key. The steps: docs.vpndetection.io/integrations/chatgpt and docs.vpndetection.io/integrations/codex. On the OpenAI API, the Responses API's mcp tool takes the server as its server_url and your key as its authorization.
In any other MCP client
Point it at https://mcp.vpndetection.io/mcp. A client that supports MCP authorization signs in the same way. One that doesn't can send a key instead, as Authorization: Bearer your-key.
On your own machine
No API key needed to start. Without a key, a lookup answers ip and is_vpn, up to 1000 a day per source address. A free key adds Tor and relay detection, where the address is and the AS that announces it, and every paid plan answers every field.
Add this to your MCP client's config:
{
"mcpServers": {
"vpndetection": {
"command": "npx",
"args": ["-y", "vpndetection-mcp"]
}
}
}Requires Node.js 22 or newer.
A key unlocks the provider name, the classification databases and the proxy families. Put it in the environment:
{
"mcpServers": {
"vpndetection": {
"command": "npx",
"args": ["-y", "vpndetection-mcp"],
"env": { "VPNDETECTION_API_KEY": "your-key" }
}
}
}VPNDETECTION_BASE_URL overrides the endpoint if you need to point somewhere else.
Related MCP server: mcp-ipquery
Tools
Tool | What it answers |
| Classify one address. |
| Classify a whole list of addresses in one call, keyed by address. A long list is batched for you. |
| What this key is entitled to and what it has spent: plan, field tier, requests so far, allowance, and when it resets. |
| Every database we publish, with its |
| A database's columns, sample rows, row count, build date and file sizes. |
| The published digests for one database file. |
| Your organization's recent download attempts, refusals included. |
The database tools need a key with the db.download scope, which your organization's Default key has.
Every tool is read-only. There is deliberately no download tool: the databases run to several GB, which is not something an agent should pull into a conversation. Fetch them with the client libraries or the API instead.
There is deliberately no my_ip tool, although every client library has one. Over a hosted transport the address our edge observes belongs to whatever proxied the call - Claude's infrastructure, not the person asking - so the tool would answer confidently and wrongly for the only reading anyone would put on it. my_entitlement has no such problem and is the same answer from any transport, because it describes the credential rather than the connection. A test pins the tool's absence so it cannot be added back by accident.
Usage counts against the anniversary of the subscription, not the calendar month and not the billing period. A null hard_limit means we never stop serving; it is not a limit of zero.
Reading a result
Each lookup comes back with a coverage block beside it:
{
"result": { "ip": "45.83.91.1", "is_vpn": true },
"coverage": {
"included": ["ip", "is_vpn"],
"not_included": ["is_hosting", "is_tor", "hosting", "tor", "..."],
"note": "The fields in not_included were not returned, because this API key's plan does not include them. ..."
}
}This matters more here than in a normal client library. A field missing from a result means your plan doesn't include it, never "we checked and found nothing" - and a model reading the result on its own will otherwise treat the absence as a negative answer. coverage states the difference explicitly so it can't.
Other Libraries
There are official VPNDetection client libraries available for many languages including PHP, Python, Go, Java, Ruby, and many popular frameworks such as Django, Rails, and Laravel. See our GitHub at https://github.com/vpndetection-io for more.
About VPNDetection
VPN Detection API: Accurate anonymity detection identifying VPNs, residential proxies, hosting servers, Tor nodes, CDNs, relays and more.
License
This project is licensed under the MIT License.
Available Tools
7 toolsdatabase_checksumGet a database's checksumsARead-onlyIdempotentInspect
The published digests for one database file, for verifying a copy you already hold or deciding whether a build has changed since you last fetched it. It is licensed like the download itself, so a database this key cannot download is refused here too. Looking up a checksum is not a download and does not appear in list_downloads.
| Name | Required | Description | Default |
|---|---|---|---|
| format | Yes | Which published file to digest. | |
| dataset_id | Yes | A VERSIONED database ID, from `versions[].id` in `list_databases` - `cdn_ip_v1`, not `cdn_ip`. The unversioned base ID is a license reference and is not accepted here. |
Output Schema
| Name | Required | Description |
|---|---|---|
| checksums | Yes | The published digests for one database file. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only, idempotent, non-destructive behavior, so the bar is lower; the description adds genuinely new context by noting the licensing gate (a database this key cannot download is refused here too) and that the call has no download side effect and creates no list_downloads entry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences: purpose and use cases first, then the licensing caveat, then the download/logging clarification. No filler, and the most decision-relevant information is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists so return values need no explanation, and annotations cover the safety profile. The description supplies the remaining unknowns an agent needs — licensing behavior and the absence of a download side effect — for a simple two-parameter read.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the schema already documents both parameters in detail, including the versioned-ID requirement and the format enum. The description only reinforces the 'one database file' framing without adding format or ID syntax, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific resource — published digests for one database file — and gives the concrete purposes (verifying a held copy, detecting a changed build). It is clearly not a download tool, differentiating it from list_downloads, though the core noun 'checksum' comes from the title rather than the description's opening.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It names the two situations that select this tool (verifying a copy you already hold; checking whether a build changed since last fetch) and explicitly rules out the nearest confusion by saying a checksum lookup is not a download and does not appear in list_downloads. It also flags the refusal condition tied to licensing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
database_metadataDescribe a databaseARead-onlyIdempotentInspect
What is inside one database before you fetch it: the columns in each published format with their types, a few sample rows, the row count, the build date and the file sizes. Use this to answer questions about what a database contains without downloading it - the files reach several GB - and to budget a transfer before starting one.
| Name | Required | Description | Default |
|---|---|---|---|
| dataset_id | Yes | A VERSIONED database ID, from `versions[].id` in `list_databases` - `cdn_ip_v1`, not `cdn_ip`. The unversioned base ID is a license reference and is not accepted here. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | |
| size | No | Bytes per format |
| sample | No | A few real rows, keyed by format |
| schema | Yes | Columns, keyed by format |
| entries | Yes | Row count in the current build |
| updated | Yes | |
| sample_size | No | Bytes per format of the evaluation sample, where one is published |
| update_freq | No | How often a new build is published |
| sample_entries | No | Row count in the evaluation sample |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false and openWorldHint=false, so the safety profile is covered. The description adds genuinely new operational context — that the underlying files reach several GB and that this call avoids the download — which is exactly the kind of cost/side-effect context annotations cannot express.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, both load-bearing, with the return contents front-loaded before the usage rationale. The opening clause ('What is inside one database before you fetch it') is slightly convoluted but not wasteful.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, yet the description still summarizes the payload categories an agent needs in order to decide whether to call it. With one well-documented required parameter, read-only annotations and a declared output schema, nothing an agent needs is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the schema itself spells out the versioned-vs-unversioned ID trap with an example (`cdn_ip_v1`, not `cdn_ip`). The description adds nothing about the dataset_id parameter, so the baseline 3 applies when the schema does all the work.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description enumerates exactly what the tool returns — columns with types, sample rows, row count, build date, file sizes — so the agent knows precisely what 'describe' means here. It does not name any sibling tool, so the distinction from database_checksum or list_databases must be inferred from the enumerated contents rather than stated.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear positive guidance: use this to answer questions about a database's contents without downloading multi-GB files, and to budget a transfer before starting one. It stops short of naming explicit alternatives or stating when another sibling should be preferred instead.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_databasesList databasesARead-onlyIdempotentInspect
The database catalog as this API key's organization may see it, one entry per database FAMILY, with standing saying where their license stands: licensed if the family is theirs today, expired if the term has ended, unlicensed if it is published but has never been bought. Each entry has a base ID, which is what the license names, and a versions array whose id is what the other database tools take - pass versions[].id (cdn_ip_v1), never the base (cdn_ip). Ask again rather than holding on to this: it is answered per key and is not the same for everyone.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| databases | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations (which already cover readOnly/idempotent): it discloses that results are per-key, differ between callers, and should not be retained. Standing semantics (`licensed`/`expired`/`unlicensed`) are spelled out, which the agent cannot infer from the schema. No contradictions with the declared hints.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the purpose, then the standing vocabulary, then the critical ID rule and freshness warning. Dense but every sentence carries information; the long compound sentence about standings is slightly harder to parse than necessary.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a no-param, read-only catalog tool with an output schema, this covers everything an agent needs: what each entry contains, what the status field means, which ID to feed downstream tools, and freshness expectations.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Zero parameters, so the baseline is 4; the description correctly spends its words on output shape instead. The `base`/`versions[].id` explanation is valuable but describes return values rather than parameter meaning.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('The database catalog as this API key's organization may see it') and immediately scopes it to one entry per database FAMILY with the `standing` values enumerated. The base-vs-versions distinction makes clear how this catalog relates to the other database tools, so an agent can place it precisely among siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit operational guidance: pass `versions[].id` (`cdn_ip_v1`), never the `base` (`cdn_ip`), and re-query rather than cache. What is missing is a direct statement of when to prefer this over siblings like database_metadata or my_entitlement, though the ID guidance implies the workflow.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_downloadsList recent download attemptsARead-onlyInspect
This organization's own recent download attempts, newest first, REFUSALS INCLUDED - a denial carries the outcome and http_status that answer "it stopped working", which nothing else here can. Use it to explain a failing fetch, to confirm a transfer ran, or to check whether a request was theirs. This is a bounded WINDOW of at most 200 rows, so a database missing from the answer means it is not in this window - never that it was never downloaded.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | How many attempts to return, newest first. At most 200; the API defaults to 50. |
Output Schema
| Name | Required | Description |
|---|---|---|
| downloads | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/openWorld/destructive, so the description's value is in what they cannot say: that denials are included with `outcome` and `http_status`, and that the result set is a bounded 200-row window. The explicit warning that a missing database means 'not in this window' rather than 'never downloaded' is exactly the kind of behavioral caveat structured fields cannot express.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with scope and ordering, then use cases, then the critical caveat placed last as the takeaway. Every clause carries distinct information; no restatement of the title or annotations.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter read tool with an output schema, the description covers the remaining gaps: refusal inclusion, ordering, row cap, and the negative-inference trap that would otherwise cause an agent to misreport a database as never downloaded. Nothing needed to call or interpret it is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the single `limit` parameter is fully documented including the 200 cap and the default of 50, so the baseline is 3. The description adds interpretive meaning beyond the schema by framing the limit as a hard window whose boundary changes how absences should be read.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific resource plus scope ('This organization's own recent download attempts'), an ordering ('newest first'), and a distinguishing trait ('REFUSALS INCLUDED') that no sibling like database_checksum or list_databases shares. An agent can tell this apart from the other tools without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives three concrete use cases ('explain a failing fetch', 'confirm a transfer ran', 'check whether a request was theirs') and asserts nothing else here answers the failure question. It stops short of naming an alternative tool or an explicit when-not-to-use condition, so it is clear context rather than full routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
lookup_ipLook up an IP addressARead-onlyIdempotentInspect
Classify a single IPv4 or IPv6 address: whether it belongs to a VPN, a residential, datacenter, mobile or public proxy, a Tor node, a privacy relay or web gateway, a hosting provider or a CDN, with the provider name where we have one. Private and reserved addresses are answered locally, without calling the API. Always read coverage.note before concluding anything from a field that is not in the result.
| Name | Required | Description | Default |
|---|---|---|---|
| ip | Yes | The IPv4 or IPv6 address to classify. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes | The union of every plan's answer. Only `ip` and `is_vpn` are guaranteed; each remaining field is present when your plan includes it, and absent otherwise. A client that must work across plans should treat an absent flag as unknown rather than as `false`. |
| coverage | Yes | Which datasets this result covered. Read `note` before interpreting any absent field. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only, idempotent, open-world and non-destructive, so the safety profile is free. The description adds two non-obvious behaviors the annotations cannot convey: private and reserved addresses are resolved locally without an API call (so results may bypass the remote dataset), and missing fields mean unknown rather than negative, per `coverage.note`. It does not cover rate limits, auth or invalid-input handling, which keeps it below a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, zero waste, front-loaded with the classification scope before the local-resolution caveat and the coverage warning. The long category list is functional information, not padding, and the most consequential warning is placed last where it reads as a caution.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be explained, and the description still usefully warns that absent fields are unknown. The one real gap is the unaddressed bulk sibling lookup_ips, plus no mention of invalid-IP or rate-limit behavior for an open-world tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Only one parameter with 100% schema description coverage, so the baseline is 4. The description's 'IPv4 or IPv6' restates what the schema already declares and adds no syntax or format detail beyond it.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (classify) and resource (a single IP address) and enumerates the exact classification categories returned: VPN, residential, datacenter, mobile, public proxy, Tor, privacy relay, web gateway, hosting, CDN, plus provider name. The word 'single' cleanly separates it from the bulk sibling lookup_ips without needing the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The word 'single' implies the bulk alternative exists but lookup_ips is never named, so the agent must infer when to switch to the batch tool. It does give one concrete usage instruction — always read `coverage.note` before concluding anything from an absent field — which is genuinely actionable guidance rather than filler.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
lookup_ipsLook up several IP addressesARead-onlyIdempotentInspect
Classify a list of addresses in one call, returning a map keyed by address so duplicates collapse and the order you passed them stops mattering. Pass the whole list rather than splitting it yourself: a long one is batched for you. Prefer this over repeated lookup_ip calls when you already have the list, for example when triaging a log file. An address that fails carries its error in place of a result rather than failing the batch. Always read coverage.note before concluding anything from a field that is not in the result.
| Name | Required | Description | Default |
|---|---|---|---|
| ips | Yes | The IPv4 or IPv6 addresses to classify. |
Output Schema
| Name | Required | Description |
|---|---|---|
| results | Yes | Keyed by address. A value is either a result or an error. |
| coverage | Yes | Which datasets this result covered. Read `note` before interpreting any absent field. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, and the description goes well beyond them: duplicates collapse into a map keyed by address, order is irrelevant, long lists are batched server-side, per-item failures are returned in place rather than aborting the batch, and the `coverage.note` caveat warns against over-reading fields. Rich behavioral context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Dense but front-loaded: the core behavior (batch classify, map keyed by address) comes first, then routing guidance, then error semantics and the coverage caveat. Every sentence adds distinct value with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite an output schema handling return structure, the description still explains the map keying, per-item error placement, and the coverage.note caveat needed to interpret results correctly. Nothing an agent needs to call or read this tool is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single parameter is fully documented by the schema, but the description adds real usage meaning: duplicates are deduplicated by key, order does not matter, and long lists are batched automatically, so the agent knows passing the raw list is safe and preferable to chunking.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (classify) and resource (a list of IP addresses) in one call, and explicitly contrasts itself with the sibling lookup_ip. An agent can distinguish batch vs single lookup without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Names the alternative (`lookup_ip`) and the exact condition that selects this tool: 'when you already have the list, for example when triaging a log file.' It also advises passing the whole list instead of splitting it. Explicit when-to-use and when-not.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
my_entitlementPlan and usage for this keyARead-onlyInspect
What this API key is entitled to and how much of it has been used: the plan, the field tier that decides how much of a lookup answer comes back, the requests counted so far, the allowance, and when it resets. Use it to explain why a field is missing from a lookup, or before a large batch. Usage counts against the anniversary of the subscription rather than the calendar month, and can lag a few seconds behind. A null hard_limit means we never stop serving - it is NOT a limit of zero.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| entitlement | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnly/idempotent annotations, it discloses that counting follows the subscription anniversary rather than the calendar month, that counts can lag a few seconds, and that a null hard_limit means unlimited rather than zero. That last point preempts a likely misinterpretation and is exactly the kind of behavior annotations cannot convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences: what it returns, when to use it, then the two caveats that would otherwise cause misuse. The payload is front-loaded and every clause carries information an agent cannot get from the schema or annotations.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no parameters, an output schema, and full annotations covering safety, the only remaining risks are semantic (billing period, lag, null meaning) and all three are addressed. Nothing needed to call or interpret this tool correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so the baseline is 4. The description additionally names the response fields (plan, field tier, requests, allowance, reset, hard_limit) even though an output schema exists, which is mild but useful reinforcement.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states precisely what the tool returns: plan, field tier, requests counted, allowance, and reset time for the current API key. It is clearly not a lookup tool, and the mention of explaining missing lookup fields implicitly separates it from lookup_ip/lookup_ips, though no sibling is named explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives two concrete triggering conditions: diagnosing why a field is missing from a lookup answer, and checking before a large batch. No explicit when-not or named alternative is provided, but the conditions are clear enough to route an agent reliably.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
4 tool updates
v6.2.5- Changed
database_checksum1 field changed- changed
Input schema / properties / dataset_id / descriptionPrevious value: -"A VERSIONED database id, from `versions[].id` in `list_databases` - `cdn_ip_v1`, not `cdn_ip`. The unversioned base id is a license reference and is not accepted here."New value: +"A VERSIONED database ID, from `versions[].id` in `list_databases` - `cdn_ip_v1`, not `cdn_ip`. The unversioned base ID is a license reference and is not accepted here."
- Changed
database_metadata1 field changed- changed
Input schema / properties / dataset_id / descriptionPrevious value: -"A VERSIONED database id, from `versions[].id` in `list_databases` - `cdn_ip_v1`, not `cdn_ip`. The unversioned base id is a license reference and is not accepted here."New value: +"A VERSIONED database ID, from `versions[].id` in `list_databases` - `cdn_ip_v1`, not `cdn_ip`. The unversioned base ID is a license reference and is not accepted here."
- Changed
list_databases2 fields changed- changed
Output schema / properties / databases / items / descriptionPrevious value: -"One database FAMILY, with your organization's license beside it. A license covers\nthe family, while a download names a specific version, so the ids you\npass to the download and checksum endpoints come from `versions`.\n"New value: +"One database FAMILY, with your organization's license beside it. A license covers\nthe family, while a download names a specific version, so the IDs you\npass to the download and checksum endpoints come from `versions`.\n" - changed
Output schema / properties / databases / items / properties / versions / items / properties / id / descriptionPrevious value: -"The versioned database id, e.g. `vpn_ip_v1`. Pass this to download."New value: +"The versioned database ID, e.g. `vpn_ip_v1`. Pass this to download."
- Changed
my_entitlement1 field changed- changed
Output schema / properties / entitlement / properties / apikey / descriptionPrevious value: -"The credential itself. The key is never echoed - only its id, which is\nwhat the console shows and what you can act on.\n"New value: +"The credential itself. The key is never echoed - only its ID, which is\nwhat the console shows and what you can act on.\n"
4 tool updates
v6.2.0- Changed
list_databases2 fields changed- added
Output schema / properties / databases / items / properties / openAdded value: +{ + "description": "An Open database: free under CC BY-SA 4.0 (credit VPNDetection,\nhttps://vpndetection.io, and share what you build from it under the\nsame license). Every organization holds a `standard` license to it\nfrom the start, which `standing` and `license_type` report like any\nother.\n", + "type": "boolean" +} - changed
Output schema / properties / databases / items / requiredPrevious value: -[ - "base", - "name", - "summary", - "license_type", - "starts", - "expires", - "renews_at", - "notice_due_at", - "in_term", - "standing", - "versions" -]New value: +[ + "base", + "name", + "summary", + "license_type", + "starts", + "expires", + "renews_at", + "notice_due_at", + "in_term", + "standing", + "open", + "versions" +]
- Changed
list_downloads2 fields changed- added
Output schema / properties / downloads / items / properties / openAdded value: +{ + "description": "A download of an Open database.", + "type": "boolean" +} - changed
Output schema / properties / downloads / items / requiredPrevious value: -[ - "dataset_id", - "format", - "outcome", - "sample", - "bytes", - "http_status", - "apikey_id", - "client_ip", - "user_agent", - "created" -]New value: +[ + "dataset_id", + "format", + "outcome", + "sample", + "open", + "bytes", + "http_status", + "apikey_id", + "client_ip", + "user_agent", + "created" +]
- Changed
lookup_ip22 fields changed- added
Output schema / properties / result / properties / asnAdded value: +{ + "description": "The autonomous system that announces the address. Free and paid plans.\nEach field is present whenever the object is: `asn` is 0 and the rest\nempty where no announcement covers the address.\n", + "properties": { + "asn": { + "description": "The AS number, 0 when no announcement covers the address.", + "type": "integer" + }, + "domain": { + "description": "The AS operator's own registrable domain.", + "type": "string" + }, + "name": { + "description": "Name of the organization that operates the AS.", + "type": "string" + }, + "prefix": { + "description": "The announced BGP prefix that covers the address, the most specific when several do.", + "type": "string" + }, + "type": { + "description": "The kind of network the AS runs: isp, hosting, education, government or business.", + "type": "string" + } + }, + "required": [ + "asn", + "name", + "domain", + "type", + "prefix" + ], + "type": "object" +} - changed
Output schema / properties / result / properties / cdn / descriptionPrevious value: -"Detail for `is_cdn`. Empty when false. Scale and above."New value: +"Detail for `is_cdn`. Empty when false. Paid plans." - changed
Output schema / properties / result / properties / dcproxy / descriptionPrevious value: -"Detail for `is_dcproxy`. Empty when false. Max only."New value: +"Detail for `is_dcproxy`. Empty when false. Paid plans." - changed
Output schema / properties / result / properties / hosting / descriptionPrevious value: -"Detail for `is_hosting`. Empty when false. Scale and above."New value: +"Detail for `is_hosting`. Empty when false. Paid plans." - changed
Output schema / properties / result / properties / is_cdn / descriptionPrevious value: -"Whether the address belongs to a CDN. Starter and above."New value: +"Whether the address belongs to a CDN. Paid plans." - changed
Output schema / properties / result / properties / is_dcproxy / descriptionPrevious value: -"Whether the address was seen in a datacenter proxy pool. Scale and above."New value: +"Whether the address was seen in a datacenter proxy pool. Paid plans." - changed
Output schema / properties / result / properties / is_hosting / descriptionPrevious value: -"Whether the address belongs to a hosting or cloud provider. Starter and above."New value: +"Whether the address belongs to a hosting or cloud provider. Paid plans." - changed
Output schema / properties / result / properties / is_mobproxy / descriptionPrevious value: -"Whether the address was seen in a mobile proxy pool. Scale and above."New value: +"Whether the address was seen in a mobile proxy pool. Paid plans." - changed
Output schema / properties / result / properties / is_pubproxy / descriptionPrevious value: -"Whether a public proxy, one anyone can connect through, listens on the address or sends its traffic out from it. Scale and above."New value: +"Whether a public proxy, one anyone can connect through, listens on the address or sends its traffic out from it. Paid plans." - changed
Output schema / properties / result / properties / is_relay / descriptionPrevious value: -"Whether the address is privacy relay or enterprise web gateway egress, such as iCloud Private Relay or Zscaler. Starter and above."New value: +"Whether the address is privacy relay or enterprise web gateway egress, such as iCloud Private Relay or Zscaler. Free and paid plans." - changed
Output schema / properties / result / properties / is_resproxy / descriptionPrevious value: -"Whether the address was seen in a residential proxy pool. Scale and above."New value: +"Whether the address was seen in a residential proxy pool. Paid plans." - changed
Output schema / properties / result / properties / is_tor / descriptionPrevious value: -"Whether the address is a Tor node. Starter and above."New value: +"Whether the address is a Tor node. Free and paid plans." - added
Output schema / properties / result / properties / locationAdded value: +{ + "description": "Where the address is, to the country: the fields of our IP Country\ndatabase. Free and paid plans. Each field is present whenever the\nobject is, so an address in no country, or a private one, answers\nempty strings.\n", + "properties": { + "continent": { + "description": "Continent code: AF, AN (Antarctica), AS, EU, NA, OC or SA.", + "type": "string" + }, + "continent_name": { + "description": "Continent name.", + "type": "string" + }, + "country": { + "description": "ISO 3166-1 alpha-2 country code.", + "type": "string" + }, + "country_name": { + "description": "Country name.", + "type": "string" + }, + "currency_code": { + "description": "ISO 4217 code of the country's currency, where there is one.", + "type": "string" + }, + "currency_name": { + "description": "Name of the country's currency, where there is one.", + "type": "string" + }, + "currency_symbol": { + "description": "Symbol of the country's currency, where known.", + "type": "string" + }, + "flag_emoji": { + "description": "The country's flag, as an emoji.", + "type": "string" + }, + "flag_unicode": { + "description": "The Unicode code points of the flag emoji.", + "type": "string" + }, + "timezone": { + "description": "IANA timezone of the address.", + "type": "string" + } + }, + "required": [ + "continent", + "continent_name", + "country", + "country_name", + "timezone", + "currency_code", + "currency_name", + "currency_symbol", + "flag_emoji", + "flag_unicode" + ], + "type": "object" +} - changed
Output schema / properties / result / properties / mobproxy / descriptionPrevious value: -"Detail for `is_mobproxy`. Empty when false. Max only."New value: +"Detail for `is_mobproxy`. Empty when false. Paid plans." - changed
Output schema / properties / result / properties / pubproxy / descriptionPrevious value: -"Detail for `is_pubproxy`. Empty when false. Max only."New value: +"Detail for `is_pubproxy`. Empty when false. Paid plans." - changed
Output schema / properties / result / properties / relay / descriptionPrevious value: -"Detail for `is_relay`. Empty when false. Scale and above."New value: +"Detail for `is_relay`. Empty when false. Free and paid plans." - changed
Output schema / properties / result / properties / resproxy / descriptionPrevious value: -"Detail for `is_resproxy`. Empty when false. Max only."New value: +"Detail for `is_resproxy`. Empty when false. Paid plans." - changed
Output schema / properties / result / properties / tor / descriptionPrevious value: -"Detail for `is_tor`. Empty when false. Scale and above. The tor\ndataset carries no provider, so `provider` is always an empty string.\n"New value: +"Detail for `is_tor`. Empty when false. Free and paid plans. The tor\ndataset carries no provider, so `provider` is always an empty string.\n" - changed
Output schema / properties / result / properties / vpn / descriptionPrevious value: -"Detail for `is_vpn`. Empty when `is_vpn` is false. Starter and above.\n"New value: +"Detail for `is_vpn`. Empty when `is_vpn` is false. Paid plans.\n" - changed
Output schema / properties / result / properties / vpn / properties / confidence / descriptionPrevious value: -"How strongly the attribution is supported. Max only."New value: +"How strongly the attribution is supported." - added
Output schema / properties / result / properties / vpn / properties / method / deprecatedAdded value: +true - changed
Output schema / properties / result / properties / vpn / properties / method / descriptionPrevious value: -"The class of evidence the attribution rests on, one of four values.\nMax only.\n\n`scan` - we spoke the VPN protocol to the address ourselves and got a\nvalid server response. `scrape` - the operator published the address\nthrough its own API, client or configuration. `registry` - public\nregistration or naming records attribute it to the operator.\n`infer` - the address was extrapolated from confirmed neighbors in\nthe same block.\n"New value: +"Never returned since 2026-10-20: no plan is answered with the class of\nevidence an attribution rests on. Declared so that a client typed\nagainst it keeps compiling; read `confidence` instead.\n"
- Changed
lookup_ips1 field changed- changed
Output schema / properties / results / additionalProperties / anyOfPrevious value: -[ - { - "description": "The union of every plan's answer. Only `ip` and `is_vpn` are guaranteed;\neach remaining field is present when your plan includes it, and absent\notherwise. A client that must work across plans should treat an absent\nflag as unknown rather than as `false`.\n", - "properties": { - "cdn": { - "description": "Detail for `is_cdn`. Empty when false. Scale and above.", - "properties": { - "confidence": { - "description": "How strongly the classification is supported.", - "type": "string" - }, - "last_seen": { - "description": "The most recent date this address was observed in this dataset.", - "format": "date", - "type": "string" - }, - "provider": { - "description": "The provider, or an empty string where the dataset has none.", - "type": "string" - } - }, - "type": "object" - }, - "dcproxy": { - "description": "Detail for `is_dcproxy`. Empty when false. Max only.", - "properties": { - "first_seen": { - "description": "The earliest date within the window this address was seen in the pool.", - "format": "date", - "type": "string" - }, - "hits": { - "description": "How many times the address was observed in the pool during the window.", - "type": "integer" - }, - "hits_days_pct": { - "description": "The share of days in the window on which the address was seen, as a\npercentage. A high value means a stable pool member rather than a\none-off sighting.\n", - "type": "integer" - }, - "last_seen": { - "description": "The most recent date within the window this address was seen in the pool.", - "format": "date", - "type": "string" - }, - "provider": { - "description": "The proxy network, or an empty string where unattributed.", - "type": "string" - }, - "providers_num": { - "description": "How many distinct proxy networks this address was seen in.", - "type": "integer" - } - }, - "type": "object" - }, - "hosting": { - "description": "Detail for `is_hosting`. Empty when false. Scale and above.", - "properties": { - "confidence": { - "description": "How strongly the classification is supported.", - "type": "string" - }, - "last_seen": { - "description": "The most recent date this address was observed in this dataset.", - "format": "date", - "type": "string" - }, - "provider": { - "description": "The provider, or an empty string where the dataset has none.", - "type": "string" - } - }, - "type": "object" - }, - "ip": { - "description": "The address that was looked up, normalized.", - "type": "string" - }, - "is_bogon": { - "description": "Present, and true, only for a private, reserved or otherwise non-routable address. Such an answer is given locally without calling the API, so every other flag in it is false by definition rather than by lookup. Absent from every served answer.", - "type": "boolean" - }, - "is_cdn": { - "description": "Whether the address belongs to a CDN. Starter and above.", - "type": "boolean" - }, - "is_dcproxy": { - "description": "Whether the address was seen in a datacenter proxy pool. Scale and above.", - "type": "boolean" - }, - "is_hosting": { - "description": "Whether the address belongs to a hosting or cloud provider. Starter and above.", - "type": "boolean" - }, - "is_mobproxy": { - "description": "Whether the address was seen in a mobile proxy pool. Scale and above.", - "type": "boolean" - }, - "is_pubproxy": { - "description": "Whether a public proxy, one anyone can connect through, listens on the address or sends its traffic out from it. Scale and above.", - "type": "boolean" - }, - "is_relay": { - "description": "Whether the address is privacy relay or enterprise web gateway egress, such as iCloud Private Relay or Zscaler. Starter and above.", - "type": "boolean" - }, - "is_resproxy": { - "description": "Whether the address was seen in a residential proxy pool. Scale and above.", - "type": "boolean" - }, - "is_tor": { - "description": "Whether the address is a Tor node. Starter and above.", - "type": "boolean" - }, - "is_vpn": { - "description": "Whether the address is VPN infrastructure. Keys off presence in the\nVPN dataset, so an unattributed range with no provider is still\n`true`.\n", - "type": "boolean" - }, - "mobproxy": { - "description": "Detail for `is_mobproxy`. Empty when false. Max only.", - "properties": { - "first_seen": { - "description": "The earliest date within the window this address was seen in the pool.", - "format": "date", - "type": "string" - }, - "hits": { - "description": "How many times the address was observed in the pool during the window.", - "type": "integer" - }, - "hits_days_pct": { - "description": "The share of days in the window on which the address was seen, as a\npercentage. A high value means a stable pool member rather than a\none-off sighting.\n", - "type": "integer" - }, - "last_seen": { - "description": "The most recent date within the window this address was seen in the pool.", - "format": "date", - "type": "string" - }, - "provider": { - "description": "The proxy network, or an empty string where unattributed.", - "type": "string" - }, - "providers_num": { - "description": "How many distinct proxy networks this address was seen in.", - "type": "integer" - } - }, - "type": "object" - }, - "pubproxy": { - "description": "Detail for `is_pubproxy`. Empty when false. Max only.", - "properties": { - "entry": { - "description": "Whether a public proxy listens on this address.", - "type": "boolean" - }, - "exit": { - "description": "Whether a public proxy's traffic leaves from this address.", - "type": "boolean" - }, - "last_seen": { - "description": "The most recent date within the window a proxy on this address was seen.", - "format": "date", - "type": "string" - }, - "last_usable": { - "description": "The most recent date within the window a request through a proxy on\nthis address was answered, as YYYY-MM-DD, or an empty string when\nnone was.\n", - "type": "string" - }, - "num_ports": { - "description": "How many distinct ports a public proxy listens on at this address;\n0 when it is only an exit, or its port is not known.\n", - "type": "integer" - }, - "num_proxies": { - "description": "How many distinct public proxies' traffic left from this address;\n0 when it is only an entry.\n", - "type": "integer" - }, - "protocols": { - "description": "The protocols of the proxies listening on or leaving from this\naddress: `http`, `https` (an HTTP proxy that also tunnels HTTPS),\n`socks4` or `socks5`. Empty where only the address is known.\n", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "type": "object" - }, - "relay": { - "description": "Detail for `is_relay`. Empty when false. Scale and above.", - "properties": { - "confidence": { - "description": "How strongly the classification is supported.", - "type": "string" - }, - "last_seen": { - "description": "The most recent date this address was observed in this dataset.", - "format": "date", - "type": "string" - }, - "provider": { - "description": "The provider, or an empty string where the dataset has none.", - "type": "string" - } - }, - "type": "object" - }, - "resproxy": { - "description": "Detail for `is_resproxy`. Empty when false. Max only.", - "properties": { - "first_seen": { - "description": "The earliest date within the window this address was seen in the pool.", - "format": "date", - "type": "string" - }, - "hits": { - "description": "How many times the address was observed in the pool during the window.", - "type": "integer" - }, - "hits_days_pct": { - "description": "The share of days in the window on which the address was seen, as a\npercentage. A high value means a stable pool member rather than a\none-off sighting.\n", - "type": "integer" - }, - "last_seen": { - "description": "The most recent date within the window this address was seen in the pool.", - "format": "date", - "type": "string" - }, - "provider": { - "description": "The proxy network, or an empty string where unattributed.", - "type": "string" - }, - "providers_num": { - "description": "How many distinct proxy networks this address was seen in.", - "type": "integer" - } - }, - "type": "object" - }, - "tor": { - "description": "Detail for `is_tor`. Empty when false. Scale and above. The tor\ndataset carries no provider, so `provider` is always an empty string.\n", - "properties": { - "confidence": { - "description": "How strongly the classification is supported.", - "type": "string" - }, - "last_seen": { - "description": "The most recent date this address was observed in this dataset.", - "format": "date", - "type": "string" - }, - "provider": { - "description": "The provider, or an empty string where the dataset has none.", - "type": "string" - } - }, - "type": "object" - }, - "vpn": { - "description": "Detail for `is_vpn`. Empty when `is_vpn` is false. Starter and above.\n", - "properties": { - "confidence": { - "description": "How strongly the attribution is supported. Max only.", - "type": "string" - }, - "last_seen": { - "description": "The most recent date this address was observed as VPN infrastructure.", - "format": "date", - "type": "string" - }, - "method": { - "description": "The class of evidence the attribution rests on, one of four values.\nMax only.\n\n`scan` - we spoke the VPN protocol to the address ourselves and got a\nvalid server response. `scrape` - the operator published the address\nthrough its own API, client or configuration. `registry` - public\nregistration or naming records attribute it to the operator.\n`infer` - the address was extrapolated from confirmed neighbors in\nthe same block.\n", - "type": "string" - }, - "provider": { - "description": "The VPN provider, or an empty string for an unattributed range.", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "ip", - "is_vpn" - ], - "type": "object" - }, - { - "properties": { - "error": { - "properties": { - "kind": { - "description": "How the client SDK classified the failure, such as `bad_request` for a string that is not an IP address.", - "type": "string" - }, - "message": { - "type": "string" - } - }, - "required": [ - "kind", - "message" - ], - "type": "object" - } - }, - "required": [ - "error" - ], - "type": "object" - } -]New value: +[ + { + "description": "The union of every plan's answer. Only `ip` and `is_vpn` are guaranteed;\neach remaining field is present when your plan includes it, and absent\notherwise. A client that must work across plans should treat an absent\nflag as unknown rather than as `false`.\n", + "properties": { + "asn": { + "description": "The autonomous system that announces the address. Free and paid plans.\nEach field is present whenever the object is: `asn` is 0 and the rest\nempty where no announcement covers the address.\n", + "properties": { + "asn": { + "description": "The AS number, 0 when no announcement covers the address.", + "type": "integer" + }, + "domain": { + "description": "The AS operator's own registrable domain.", + "type": "string" + }, + "name": { + "description": "Name of the organization that operates the AS.", + "type": "string" + }, + "prefix": { + "description": "The announced BGP prefix that covers the address, the most specific when several do.", + "type": "string" + }, + "type": { + "description": "The kind of network the AS runs: isp, hosting, education, government or business.", + "type": "string" + } + }, + "required": [ + "asn", + "name", + "domain", + "type", + "prefix" + ], + "type": "object" + }, + "cdn": { + "description": "Detail for `is_cdn`. Empty when false. Paid plans.", + "properties": { + "confidence": { + "description": "How strongly the classification is supported.", + "type": "string" + }, + "last_seen": { + "description": "The most recent date this address was observed in this dataset.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The provider, or an empty string where the dataset has none.", + "type": "string" + } + }, + "type": "object" + }, + "dcproxy": { + "description": "Detail for `is_dcproxy`. Empty when false. Paid plans.", + "properties": { + "first_seen": { + "description": "The earliest date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "hits": { + "description": "How many times the address was observed in the pool during the window.", + "type": "integer" + }, + "hits_days_pct": { + "description": "The share of days in the window on which the address was seen, as a\npercentage. A high value means a stable pool member rather than a\none-off sighting.\n", + "type": "integer" + }, + "last_seen": { + "description": "The most recent date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The proxy network, or an empty string where unattributed.", + "type": "string" + }, + "providers_num": { + "description": "How many distinct proxy networks this address was seen in.", + "type": "integer" + } + }, + "type": "object" + }, + "hosting": { + "description": "Detail for `is_hosting`. Empty when false. Paid plans.", + "properties": { + "confidence": { + "description": "How strongly the classification is supported.", + "type": "string" + }, + "last_seen": { + "description": "The most recent date this address was observed in this dataset.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The provider, or an empty string where the dataset has none.", + "type": "string" + } + }, + "type": "object" + }, + "ip": { + "description": "The address that was looked up, normalized.", + "type": "string" + }, + "is_bogon": { + "description": "Present, and true, only for a private, reserved or otherwise non-routable address. Such an answer is given locally without calling the API, so every other flag in it is false by definition rather than by lookup. Absent from every served answer.", + "type": "boolean" + }, + "is_cdn": { + "description": "Whether the address belongs to a CDN. Paid plans.", + "type": "boolean" + }, + "is_dcproxy": { + "description": "Whether the address was seen in a datacenter proxy pool. Paid plans.", + "type": "boolean" + }, + "is_hosting": { + "description": "Whether the address belongs to a hosting or cloud provider. Paid plans.", + "type": "boolean" + }, + "is_mobproxy": { + "description": "Whether the address was seen in a mobile proxy pool. Paid plans.", + "type": "boolean" + }, + "is_pubproxy": { + "description": "Whether a public proxy, one anyone can connect through, listens on the address or sends its traffic out from it. Paid plans.", + "type": "boolean" + }, + "is_relay": { + "description": "Whether the address is privacy relay or enterprise web gateway egress, such as iCloud Private Relay or Zscaler. Free and paid plans.", + "type": "boolean" + }, + "is_resproxy": { + "description": "Whether the address was seen in a residential proxy pool. Paid plans.", + "type": "boolean" + }, + "is_tor": { + "description": "Whether the address is a Tor node. Free and paid plans.", + "type": "boolean" + }, + "is_vpn": { + "description": "Whether the address is VPN infrastructure. Keys off presence in the\nVPN dataset, so an unattributed range with no provider is still\n`true`.\n", + "type": "boolean" + }, + "location": { + "description": "Where the address is, to the country: the fields of our IP Country\ndatabase. Free and paid plans. Each field is present whenever the\nobject is, so an address in no country, or a private one, answers\nempty strings.\n", + "properties": { + "continent": { + "description": "Continent code: AF, AN (Antarctica), AS, EU, NA, OC or SA.", + "type": "string" + }, + "continent_name": { + "description": "Continent name.", + "type": "string" + }, + "country": { + "description": "ISO 3166-1 alpha-2 country code.", + "type": "string" + }, + "country_name": { + "description": "Country name.", + "type": "string" + }, + "currency_code": { + "description": "ISO 4217 code of the country's currency, where there is one.", + "type": "string" + }, + "currency_name": { + "description": "Name of the country's currency, where there is one.", + "type": "string" + }, + "currency_symbol": { + "description": "Symbol of the country's currency, where known.", + "type": "string" + }, + "flag_emoji": { + "description": "The country's flag, as an emoji.", + "type": "string" + }, + "flag_unicode": { + "description": "The Unicode code points of the flag emoji.", + "type": "string" + }, + "timezone": { + "description": "IANA timezone of the address.", + "type": "string" + } + }, + "required": [ + "continent", + "continent_name", + "country", + "country_name", + "timezone", + "currency_code", + "currency_name", + "currency_symbol", + "flag_emoji", + "flag_unicode" + ], + "type": "object" + }, + "mobproxy": { + "description": "Detail for `is_mobproxy`. Empty when false. Paid plans.", + "properties": { + "first_seen": { + "description": "The earliest date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "hits": { + "description": "How many times the address was observed in the pool during the window.", + "type": "integer" + }, + "hits_days_pct": { + "description": "The share of days in the window on which the address was seen, as a\npercentage. A high value means a stable pool member rather than a\none-off sighting.\n", + "type": "integer" + }, + "last_seen": { + "description": "The most recent date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The proxy network, or an empty string where unattributed.", + "type": "string" + }, + "providers_num": { + "description": "How many distinct proxy networks this address was seen in.", + "type": "integer" + } + }, + "type": "object" + }, + "pubproxy": { + "description": "Detail for `is_pubproxy`. Empty when false. Paid plans.", + "properties": { + "entry": { + "description": "Whether a public proxy listens on this address.", + "type": "boolean" + }, + "exit": { + "description": "Whether a public proxy's traffic leaves from this address.", + "type": "boolean" + }, + "last_seen": { + "description": "The most recent date within the window a proxy on this address was seen.", + "format": "date", + "type": "string" + }, + "last_usable": { + "description": "The most recent date within the window a request through a proxy on\nthis address was answered, as YYYY-MM-DD, or an empty string when\nnone was.\n", + "type": "string" + }, + "num_ports": { + "description": "How many distinct ports a public proxy listens on at this address;\n0 when it is only an exit, or its port is not known.\n", + "type": "integer" + }, + "num_proxies": { + "description": "How many distinct public proxies' traffic left from this address;\n0 when it is only an entry.\n", + "type": "integer" + }, + "protocols": { + "description": "The protocols of the proxies listening on or leaving from this\naddress: `http`, `https` (an HTTP proxy that also tunnels HTTPS),\n`socks4` or `socks5`. Empty where only the address is known.\n", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "relay": { + "description": "Detail for `is_relay`. Empty when false. Free and paid plans.", + "properties": { + "confidence": { + "description": "How strongly the classification is supported.", + "type": "string" + }, + "last_seen": { + "description": "The most recent date this address was observed in this dataset.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The provider, or an empty string where the dataset has none.", + "type": "string" + } + }, + "type": "object" + }, + "resproxy": { + "description": "Detail for `is_resproxy`. Empty when false. Paid plans.", + "properties": { + "first_seen": { + "description": "The earliest date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "hits": { + "description": "How many times the address was observed in the pool during the window.", + "type": "integer" + }, + "hits_days_pct": { + "description": "The share of days in the window on which the address was seen, as a\npercentage. A high value means a stable pool member rather than a\none-off sighting.\n", + "type": "integer" + }, + "last_seen": { + "description": "The most recent date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The proxy network, or an empty string where unattributed.", + "type": "string" + }, + "providers_num": { + "description": "How many distinct proxy networks this address was seen in.", + "type": "integer" + } + }, + "type": "object" + }, + "tor": { + "description": "Detail for `is_tor`. Empty when false. Free and paid plans. The tor\ndataset carries no provider, so `provider` is always an empty string.\n", + "properties": { + "confidence": { + "description": "How strongly the classification is supported.", + "type": "string" + }, + "last_seen": { + "description": "The most recent date this address was observed in this dataset.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The provider, or an empty string where the dataset has none.", + "type": "string" + } + }, + "type": "object" + }, + "vpn": { + "description": "Detail for `is_vpn`. Empty when `is_vpn` is false. Paid plans.\n", + "properties": { + "confidence": { + "description": "How strongly the attribution is supported.", + "type": "string" + }, + "last_seen": { + "description": "The most recent date this address was observed as VPN infrastructure.", + "format": "date", + "type": "string" + }, + "method": { + "deprecated": true, + "description": "Never returned since 2026-10-20: no plan is answered with the class of\nevidence an attribution rests on. Declared so that a client typed\nagainst it keeps compiling; read `confidence` instead.\n", + "type": "string" + }, + "provider": { + "description": "The VPN provider, or an empty string for an unattributed range.", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "ip", + "is_vpn" + ], + "type": "object" + }, + { + "properties": { + "error": { + "properties": { + "kind": { + "description": "How the client SDK classified the failure, such as `bad_request` for a string that is not an IP address.", + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "kind", + "message" + ], + "type": "object" + } + }, + "required": [ + "error" + ], + "type": "object" + } +]
2 tool updates
v6.1.1- Changed
lookup_ip3 fields changed- added
Output schema / properties / result / properties / is_pubproxyAdded value: +{ + "description": "Whether a public proxy, one anyone can connect through, listens on the address or sends its traffic out from it. Scale and above.", + "type": "boolean" +} - changed
Output schema / properties / result / properties / is_relay / descriptionPrevious value: -"Whether the address is a privacy relay egress. Starter and above."New value: +"Whether the address is privacy relay or enterprise web gateway egress, such as iCloud Private Relay or Zscaler. Starter and above." - added
Output schema / properties / result / properties / pubproxyAdded value: +{ + "description": "Detail for `is_pubproxy`. Empty when false. Max only.", + "properties": { + "entry": { + "description": "Whether a public proxy listens on this address.", + "type": "boolean" + }, + "exit": { + "description": "Whether a public proxy's traffic leaves from this address.", + "type": "boolean" + }, + "last_seen": { + "description": "The most recent date within the window a proxy on this address was seen.", + "format": "date", + "type": "string" + }, + "last_usable": { + "description": "The most recent date within the window a request through a proxy on\nthis address was answered, as YYYY-MM-DD, or an empty string when\nnone was.\n", + "type": "string" + }, + "num_ports": { + "description": "How many distinct ports a public proxy listens on at this address;\n0 when it is only an exit, or its port is not known.\n", + "type": "integer" + }, + "num_proxies": { + "description": "How many distinct public proxies' traffic left from this address;\n0 when it is only an entry.\n", + "type": "integer" + }, + "protocols": { + "description": "The protocols of the proxies listening on or leaving from this\naddress: `http`, `https` (an HTTP proxy that also tunnels HTTPS),\n`socks4` or `socks5`. Empty where only the address is known.\n", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" +}
- Changed
lookup_ips1 field changed- changed
Output schema / properties / results / additionalProperties / anyOfPrevious value: -[ - { - "description": "The union of every plan's answer. Only `ip` and `is_vpn` are guaranteed;\neach remaining field is present when your plan includes it, and absent\notherwise. A client that must work across plans should treat an absent\nflag as unknown rather than as `false`.\n", - "properties": { - "cdn": { - "description": "Detail for `is_cdn`. Empty when false. Scale and above.", - "properties": { - "confidence": { - "description": "How strongly the classification is supported.", - "type": "string" - }, - "last_seen": { - "description": "The most recent date this address was observed in this dataset.", - "format": "date", - "type": "string" - }, - "provider": { - "description": "The provider, or an empty string where the dataset has none.", - "type": "string" - } - }, - "type": "object" - }, - "dcproxy": { - "description": "Detail for `is_dcproxy`. Empty when false. Max only.", - "properties": { - "first_seen": { - "description": "The earliest date within the window this address was seen in the pool.", - "format": "date", - "type": "string" - }, - "hits": { - "description": "How many times the address was observed in the pool during the window.", - "type": "integer" - }, - "hits_days_pct": { - "description": "The share of days in the window on which the address was seen, as a\npercentage. A high value means a stable pool member rather than a\none-off sighting.\n", - "type": "integer" - }, - "last_seen": { - "description": "The most recent date within the window this address was seen in the pool.", - "format": "date", - "type": "string" - }, - "provider": { - "description": "The proxy network, or an empty string where unattributed.", - "type": "string" - }, - "providers_num": { - "description": "How many distinct proxy networks this address was seen in.", - "type": "integer" - } - }, - "type": "object" - }, - "hosting": { - "description": "Detail for `is_hosting`. Empty when false. Scale and above.", - "properties": { - "confidence": { - "description": "How strongly the classification is supported.", - "type": "string" - }, - "last_seen": { - "description": "The most recent date this address was observed in this dataset.", - "format": "date", - "type": "string" - }, - "provider": { - "description": "The provider, or an empty string where the dataset has none.", - "type": "string" - } - }, - "type": "object" - }, - "ip": { - "description": "The address that was looked up, normalized.", - "type": "string" - }, - "is_bogon": { - "description": "Present, and true, only for a private, reserved or otherwise non-routable address. Such an answer is given locally without calling the API, so every other flag in it is false by definition rather than by lookup. Absent from every served answer.", - "type": "boolean" - }, - "is_cdn": { - "description": "Whether the address belongs to a CDN. Starter and above.", - "type": "boolean" - }, - "is_dcproxy": { - "description": "Whether the address was seen in a datacenter proxy pool. Scale and above.", - "type": "boolean" - }, - "is_hosting": { - "description": "Whether the address belongs to a hosting or cloud provider. Starter and above.", - "type": "boolean" - }, - "is_mobproxy": { - "description": "Whether the address was seen in a mobile proxy pool. Scale and above.", - "type": "boolean" - }, - "is_relay": { - "description": "Whether the address is a privacy relay egress. Starter and above.", - "type": "boolean" - }, - "is_resproxy": { - "description": "Whether the address was seen in a residential proxy pool. Scale and above.", - "type": "boolean" - }, - "is_tor": { - "description": "Whether the address is a Tor node. Starter and above.", - "type": "boolean" - }, - "is_vpn": { - "description": "Whether the address is VPN infrastructure. Keys off presence in the\nVPN dataset, so an unattributed range with no provider is still\n`true`.\n", - "type": "boolean" - }, - "mobproxy": { - "description": "Detail for `is_mobproxy`. Empty when false. Max only.", - "properties": { - "first_seen": { - "description": "The earliest date within the window this address was seen in the pool.", - "format": "date", - "type": "string" - }, - "hits": { - "description": "How many times the address was observed in the pool during the window.", - "type": "integer" - }, - "hits_days_pct": { - "description": "The share of days in the window on which the address was seen, as a\npercentage. A high value means a stable pool member rather than a\none-off sighting.\n", - "type": "integer" - }, - "last_seen": { - "description": "The most recent date within the window this address was seen in the pool.", - "format": "date", - "type": "string" - }, - "provider": { - "description": "The proxy network, or an empty string where unattributed.", - "type": "string" - }, - "providers_num": { - "description": "How many distinct proxy networks this address was seen in.", - "type": "integer" - } - }, - "type": "object" - }, - "relay": { - "description": "Detail for `is_relay`. Empty when false. Scale and above.", - "properties": { - "confidence": { - "description": "How strongly the classification is supported.", - "type": "string" - }, - "last_seen": { - "description": "The most recent date this address was observed in this dataset.", - "format": "date", - "type": "string" - }, - "provider": { - "description": "The provider, or an empty string where the dataset has none.", - "type": "string" - } - }, - "type": "object" - }, - "resproxy": { - "description": "Detail for `is_resproxy`. Empty when false. Max only.", - "properties": { - "first_seen": { - "description": "The earliest date within the window this address was seen in the pool.", - "format": "date", - "type": "string" - }, - "hits": { - "description": "How many times the address was observed in the pool during the window.", - "type": "integer" - }, - "hits_days_pct": { - "description": "The share of days in the window on which the address was seen, as a\npercentage. A high value means a stable pool member rather than a\none-off sighting.\n", - "type": "integer" - }, - "last_seen": { - "description": "The most recent date within the window this address was seen in the pool.", - "format": "date", - "type": "string" - }, - "provider": { - "description": "The proxy network, or an empty string where unattributed.", - "type": "string" - }, - "providers_num": { - "description": "How many distinct proxy networks this address was seen in.", - "type": "integer" - } - }, - "type": "object" - }, - "tor": { - "description": "Detail for `is_tor`. Empty when false. Scale and above. The tor\ndataset carries no provider, so `provider` is always an empty string.\n", - "properties": { - "confidence": { - "description": "How strongly the classification is supported.", - "type": "string" - }, - "last_seen": { - "description": "The most recent date this address was observed in this dataset.", - "format": "date", - "type": "string" - }, - "provider": { - "description": "The provider, or an empty string where the dataset has none.", - "type": "string" - } - }, - "type": "object" - }, - "vpn": { - "description": "Detail for `is_vpn`. Empty when `is_vpn` is false. Starter and above.\n", - "properties": { - "confidence": { - "description": "How strongly the attribution is supported. Max only.", - "type": "string" - }, - "last_seen": { - "description": "The most recent date this address was observed as VPN infrastructure.", - "format": "date", - "type": "string" - }, - "method": { - "description": "The class of evidence the attribution rests on, one of four values.\nMax only.\n\n`scan` - we spoke the VPN protocol to the address ourselves and got a\nvalid server response. `scrape` - the operator published the address\nthrough its own API, client or configuration. `registry` - public\nregistration or naming records attribute it to the operator.\n`infer` - the address was extrapolated from confirmed neighbors in\nthe same block.\n", - "type": "string" - }, - "provider": { - "description": "The VPN provider, or an empty string for an unattributed range.", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "ip", - "is_vpn" - ], - "type": "object" - }, - { - "properties": { - "error": { - "properties": { - "kind": { - "description": "How the client SDK classified the failure, such as `bad_request` for a string that is not an IP address.", - "type": "string" - }, - "message": { - "type": "string" - } - }, - "required": [ - "kind", - "message" - ], - "type": "object" - } - }, - "required": [ - "error" - ], - "type": "object" - } -]New value: +[ + { + "description": "The union of every plan's answer. Only `ip` and `is_vpn` are guaranteed;\neach remaining field is present when your plan includes it, and absent\notherwise. A client that must work across plans should treat an absent\nflag as unknown rather than as `false`.\n", + "properties": { + "cdn": { + "description": "Detail for `is_cdn`. Empty when false. Scale and above.", + "properties": { + "confidence": { + "description": "How strongly the classification is supported.", + "type": "string" + }, + "last_seen": { + "description": "The most recent date this address was observed in this dataset.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The provider, or an empty string where the dataset has none.", + "type": "string" + } + }, + "type": "object" + }, + "dcproxy": { + "description": "Detail for `is_dcproxy`. Empty when false. Max only.", + "properties": { + "first_seen": { + "description": "The earliest date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "hits": { + "description": "How many times the address was observed in the pool during the window.", + "type": "integer" + }, + "hits_days_pct": { + "description": "The share of days in the window on which the address was seen, as a\npercentage. A high value means a stable pool member rather than a\none-off sighting.\n", + "type": "integer" + }, + "last_seen": { + "description": "The most recent date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The proxy network, or an empty string where unattributed.", + "type": "string" + }, + "providers_num": { + "description": "How many distinct proxy networks this address was seen in.", + "type": "integer" + } + }, + "type": "object" + }, + "hosting": { + "description": "Detail for `is_hosting`. Empty when false. Scale and above.", + "properties": { + "confidence": { + "description": "How strongly the classification is supported.", + "type": "string" + }, + "last_seen": { + "description": "The most recent date this address was observed in this dataset.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The provider, or an empty string where the dataset has none.", + "type": "string" + } + }, + "type": "object" + }, + "ip": { + "description": "The address that was looked up, normalized.", + "type": "string" + }, + "is_bogon": { + "description": "Present, and true, only for a private, reserved or otherwise non-routable address. Such an answer is given locally without calling the API, so every other flag in it is false by definition rather than by lookup. Absent from every served answer.", + "type": "boolean" + }, + "is_cdn": { + "description": "Whether the address belongs to a CDN. Starter and above.", + "type": "boolean" + }, + "is_dcproxy": { + "description": "Whether the address was seen in a datacenter proxy pool. Scale and above.", + "type": "boolean" + }, + "is_hosting": { + "description": "Whether the address belongs to a hosting or cloud provider. Starter and above.", + "type": "boolean" + }, + "is_mobproxy": { + "description": "Whether the address was seen in a mobile proxy pool. Scale and above.", + "type": "boolean" + }, + "is_pubproxy": { + "description": "Whether a public proxy, one anyone can connect through, listens on the address or sends its traffic out from it. Scale and above.", + "type": "boolean" + }, + "is_relay": { + "description": "Whether the address is privacy relay or enterprise web gateway egress, such as iCloud Private Relay or Zscaler. Starter and above.", + "type": "boolean" + }, + "is_resproxy": { + "description": "Whether the address was seen in a residential proxy pool. Scale and above.", + "type": "boolean" + }, + "is_tor": { + "description": "Whether the address is a Tor node. Starter and above.", + "type": "boolean" + }, + "is_vpn": { + "description": "Whether the address is VPN infrastructure. Keys off presence in the\nVPN dataset, so an unattributed range with no provider is still\n`true`.\n", + "type": "boolean" + }, + "mobproxy": { + "description": "Detail for `is_mobproxy`. Empty when false. Max only.", + "properties": { + "first_seen": { + "description": "The earliest date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "hits": { + "description": "How many times the address was observed in the pool during the window.", + "type": "integer" + }, + "hits_days_pct": { + "description": "The share of days in the window on which the address was seen, as a\npercentage. A high value means a stable pool member rather than a\none-off sighting.\n", + "type": "integer" + }, + "last_seen": { + "description": "The most recent date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The proxy network, or an empty string where unattributed.", + "type": "string" + }, + "providers_num": { + "description": "How many distinct proxy networks this address was seen in.", + "type": "integer" + } + }, + "type": "object" + }, + "pubproxy": { + "description": "Detail for `is_pubproxy`. Empty when false. Max only.", + "properties": { + "entry": { + "description": "Whether a public proxy listens on this address.", + "type": "boolean" + }, + "exit": { + "description": "Whether a public proxy's traffic leaves from this address.", + "type": "boolean" + }, + "last_seen": { + "description": "The most recent date within the window a proxy on this address was seen.", + "format": "date", + "type": "string" + }, + "last_usable": { + "description": "The most recent date within the window a request through a proxy on\nthis address was answered, as YYYY-MM-DD, or an empty string when\nnone was.\n", + "type": "string" + }, + "num_ports": { + "description": "How many distinct ports a public proxy listens on at this address;\n0 when it is only an exit, or its port is not known.\n", + "type": "integer" + }, + "num_proxies": { + "description": "How many distinct public proxies' traffic left from this address;\n0 when it is only an entry.\n", + "type": "integer" + }, + "protocols": { + "description": "The protocols of the proxies listening on or leaving from this\naddress: `http`, `https` (an HTTP proxy that also tunnels HTTPS),\n`socks4` or `socks5`. Empty where only the address is known.\n", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "relay": { + "description": "Detail for `is_relay`. Empty when false. Scale and above.", + "properties": { + "confidence": { + "description": "How strongly the classification is supported.", + "type": "string" + }, + "last_seen": { + "description": "The most recent date this address was observed in this dataset.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The provider, or an empty string where the dataset has none.", + "type": "string" + } + }, + "type": "object" + }, + "resproxy": { + "description": "Detail for `is_resproxy`. Empty when false. Max only.", + "properties": { + "first_seen": { + "description": "The earliest date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "hits": { + "description": "How many times the address was observed in the pool during the window.", + "type": "integer" + }, + "hits_days_pct": { + "description": "The share of days in the window on which the address was seen, as a\npercentage. A high value means a stable pool member rather than a\none-off sighting.\n", + "type": "integer" + }, + "last_seen": { + "description": "The most recent date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The proxy network, or an empty string where unattributed.", + "type": "string" + }, + "providers_num": { + "description": "How many distinct proxy networks this address was seen in.", + "type": "integer" + } + }, + "type": "object" + }, + "tor": { + "description": "Detail for `is_tor`. Empty when false. Scale and above. The tor\ndataset carries no provider, so `provider` is always an empty string.\n", + "properties": { + "confidence": { + "description": "How strongly the classification is supported.", + "type": "string" + }, + "last_seen": { + "description": "The most recent date this address was observed in this dataset.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The provider, or an empty string where the dataset has none.", + "type": "string" + } + }, + "type": "object" + }, + "vpn": { + "description": "Detail for `is_vpn`. Empty when `is_vpn` is false. Starter and above.\n", + "properties": { + "confidence": { + "description": "How strongly the attribution is supported. Max only.", + "type": "string" + }, + "last_seen": { + "description": "The most recent date this address was observed as VPN infrastructure.", + "format": "date", + "type": "string" + }, + "method": { + "description": "The class of evidence the attribution rests on, one of four values.\nMax only.\n\n`scan` - we spoke the VPN protocol to the address ourselves and got a\nvalid server response. `scrape` - the operator published the address\nthrough its own API, client or configuration. `registry` - public\nregistration or naming records attribute it to the operator.\n`infer` - the address was extrapolated from confirmed neighbors in\nthe same block.\n", + "type": "string" + }, + "provider": { + "description": "The VPN provider, or an empty string for an unattributed range.", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "ip", + "is_vpn" + ], + "type": "object" + }, + { + "properties": { + "error": { + "properties": { + "kind": { + "description": "How the client SDK classified the failure, such as `bad_request` for a string that is not an IP address.", + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "kind", + "message" + ], + "type": "object" + } + }, + "required": [ + "error" + ], + "type": "object" + } +]
7 tool updates
v6.0.4- Changed
database_checksum1 field changed- added
Input schema / additionalPropertiesAdded value: +false
- Changed
database_metadata1 field changed- added
Input schema / additionalPropertiesAdded value: +false
- Changed
list_databases2 fields changed- added
Input schema / propertiesAdded value: +{} - changed
Output schema / properties / databases / items / properties / versions / items / properties / formats / items / properties / bytes / descriptionPrevious value: -"Size of the published file, or null when it has not been published\nyet. int64 because it is not hypothetical: resproxy_ip_14d's MMDB is\n4.58 GB, so a 32-bit field cannot carry the catalogue and `list`\nthrows for every caller rather than for that one entry.\n"New value: +"Size of the published file, or null when it has not been published\nyet. int64 because it is not hypothetical: resproxy_ip_14d's MMDB is\n4.58 GB, so a 32-bit field cannot carry the catalog and `list`\nthrows for every caller rather than for that one entry.\n"
- Changed
list_downloads1 field changed- added
Input schema / additionalPropertiesAdded value: +false
- Changed
lookup_ip2 fields changed- added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / properties / result / properties / vpn / properties / method / descriptionPrevious value: -"The class of evidence the attribution rests on, one of four values.\nMax only.\n\n`scan` - we spoke the VPN protocol to the address ourselves and got a\nvalid server response. `scrape` - the operator published the address\nthrough its own API, client or configuration. `registry` - public\nregistration or naming records attribute it to the operator.\n`infer` - the address was extrapolated from confirmed neighbours in\nthe same block.\n"New value: +"The class of evidence the attribution rests on, one of four values.\nMax only.\n\n`scan` - we spoke the VPN protocol to the address ourselves and got a\nvalid server response. `scrape` - the operator published the address\nthrough its own API, client or configuration. `registry` - public\nregistration or naming records attribute it to the operator.\n`infer` - the address was extrapolated from confirmed neighbors in\nthe same block.\n"
- Changed
lookup_ips2 fields changed- added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / properties / results / additionalProperties / anyOfPrevious value: -[ - { - "description": "The union of every plan's answer. Only `ip` and `is_vpn` are guaranteed;\neach remaining field is present when your plan includes it, and absent\notherwise. A client that must work across plans should treat an absent\nflag as unknown rather than as `false`.\n", - "properties": { - "cdn": { - "description": "Detail for `is_cdn`. Empty when false. Scale and above.", - "properties": { - "confidence": { - "description": "How strongly the classification is supported.", - "type": "string" - }, - "last_seen": { - "description": "The most recent date this address was observed in this dataset.", - "format": "date", - "type": "string" - }, - "provider": { - "description": "The provider, or an empty string where the dataset has none.", - "type": "string" - } - }, - "type": "object" - }, - "dcproxy": { - "description": "Detail for `is_dcproxy`. Empty when false. Max only.", - "properties": { - "first_seen": { - "description": "The earliest date within the window this address was seen in the pool.", - "format": "date", - "type": "string" - }, - "hits": { - "description": "How many times the address was observed in the pool during the window.", - "type": "integer" - }, - "hits_days_pct": { - "description": "The share of days in the window on which the address was seen, as a\npercentage. A high value means a stable pool member rather than a\none-off sighting.\n", - "type": "integer" - }, - "last_seen": { - "description": "The most recent date within the window this address was seen in the pool.", - "format": "date", - "type": "string" - }, - "provider": { - "description": "The proxy network, or an empty string where unattributed.", - "type": "string" - }, - "providers_num": { - "description": "How many distinct proxy networks this address was seen in.", - "type": "integer" - } - }, - "type": "object" - }, - "hosting": { - "description": "Detail for `is_hosting`. Empty when false. Scale and above.", - "properties": { - "confidence": { - "description": "How strongly the classification is supported.", - "type": "string" - }, - "last_seen": { - "description": "The most recent date this address was observed in this dataset.", - "format": "date", - "type": "string" - }, - "provider": { - "description": "The provider, or an empty string where the dataset has none.", - "type": "string" - } - }, - "type": "object" - }, - "ip": { - "description": "The address that was looked up, normalized.", - "type": "string" - }, - "is_bogon": { - "description": "Present, and true, only for a private, reserved or otherwise non-routable address. Such an answer is given locally without calling the API, so every other flag in it is false by definition rather than by lookup. Absent from every served answer.", - "type": "boolean" - }, - "is_cdn": { - "description": "Whether the address belongs to a CDN. Starter and above.", - "type": "boolean" - }, - "is_dcproxy": { - "description": "Whether the address was seen in a datacenter proxy pool. Scale and above.", - "type": "boolean" - }, - "is_hosting": { - "description": "Whether the address belongs to a hosting or cloud provider. Starter and above.", - "type": "boolean" - }, - "is_mobproxy": { - "description": "Whether the address was seen in a mobile proxy pool. Scale and above.", - "type": "boolean" - }, - "is_relay": { - "description": "Whether the address is a privacy relay egress. Starter and above.", - "type": "boolean" - }, - "is_resproxy": { - "description": "Whether the address was seen in a residential proxy pool. Scale and above.", - "type": "boolean" - }, - "is_tor": { - "description": "Whether the address is a Tor node. Starter and above.", - "type": "boolean" - }, - "is_vpn": { - "description": "Whether the address is VPN infrastructure. Keys off presence in the\nVPN dataset, so an unattributed range with no provider is still\n`true`.\n", - "type": "boolean" - }, - "mobproxy": { - "description": "Detail for `is_mobproxy`. Empty when false. Max only.", - "properties": { - "first_seen": { - "description": "The earliest date within the window this address was seen in the pool.", - "format": "date", - "type": "string" - }, - "hits": { - "description": "How many times the address was observed in the pool during the window.", - "type": "integer" - }, - "hits_days_pct": { - "description": "The share of days in the window on which the address was seen, as a\npercentage. A high value means a stable pool member rather than a\none-off sighting.\n", - "type": "integer" - }, - "last_seen": { - "description": "The most recent date within the window this address was seen in the pool.", - "format": "date", - "type": "string" - }, - "provider": { - "description": "The proxy network, or an empty string where unattributed.", - "type": "string" - }, - "providers_num": { - "description": "How many distinct proxy networks this address was seen in.", - "type": "integer" - } - }, - "type": "object" - }, - "relay": { - "description": "Detail for `is_relay`. Empty when false. Scale and above.", - "properties": { - "confidence": { - "description": "How strongly the classification is supported.", - "type": "string" - }, - "last_seen": { - "description": "The most recent date this address was observed in this dataset.", - "format": "date", - "type": "string" - }, - "provider": { - "description": "The provider, or an empty string where the dataset has none.", - "type": "string" - } - }, - "type": "object" - }, - "resproxy": { - "description": "Detail for `is_resproxy`. Empty when false. Max only.", - "properties": { - "first_seen": { - "description": "The earliest date within the window this address was seen in the pool.", - "format": "date", - "type": "string" - }, - "hits": { - "description": "How many times the address was observed in the pool during the window.", - "type": "integer" - }, - "hits_days_pct": { - "description": "The share of days in the window on which the address was seen, as a\npercentage. A high value means a stable pool member rather than a\none-off sighting.\n", - "type": "integer" - }, - "last_seen": { - "description": "The most recent date within the window this address was seen in the pool.", - "format": "date", - "type": "string" - }, - "provider": { - "description": "The proxy network, or an empty string where unattributed.", - "type": "string" - }, - "providers_num": { - "description": "How many distinct proxy networks this address was seen in.", - "type": "integer" - } - }, - "type": "object" - }, - "tor": { - "description": "Detail for `is_tor`. Empty when false. Scale and above. The tor\ndataset carries no provider, so `provider` is always an empty string.\n", - "properties": { - "confidence": { - "description": "How strongly the classification is supported.", - "type": "string" - }, - "last_seen": { - "description": "The most recent date this address was observed in this dataset.", - "format": "date", - "type": "string" - }, - "provider": { - "description": "The provider, or an empty string where the dataset has none.", - "type": "string" - } - }, - "type": "object" - }, - "vpn": { - "description": "Detail for `is_vpn`. Empty when `is_vpn` is false. Starter and above.\n", - "properties": { - "confidence": { - "description": "How strongly the attribution is supported. Max only.", - "type": "string" - }, - "last_seen": { - "description": "The most recent date this address was observed as VPN infrastructure.", - "format": "date", - "type": "string" - }, - "method": { - "description": "The class of evidence the attribution rests on, one of four values.\nMax only.\n\n`scan` - we spoke the VPN protocol to the address ourselves and got a\nvalid server response. `scrape` - the operator published the address\nthrough its own API, client or configuration. `registry` - public\nregistration or naming records attribute it to the operator.\n`infer` - the address was extrapolated from confirmed neighbours in\nthe same block.\n", - "type": "string" - }, - "provider": { - "description": "The VPN provider, or an empty string for an unattributed range.", - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "ip", - "is_vpn" - ], - "type": "object" - }, - { - "properties": { - "error": { - "properties": { - "kind": { - "description": "How the client SDK classified the failure, such as `bad_request` for a string that is not an IP address.", - "type": "string" - }, - "message": { - "type": "string" - } - }, - "required": [ - "kind", - "message" - ], - "type": "object" - } - }, - "required": [ - "error" - ], - "type": "object" - } -]New value: +[ + { + "description": "The union of every plan's answer. Only `ip` and `is_vpn` are guaranteed;\neach remaining field is present when your plan includes it, and absent\notherwise. A client that must work across plans should treat an absent\nflag as unknown rather than as `false`.\n", + "properties": { + "cdn": { + "description": "Detail for `is_cdn`. Empty when false. Scale and above.", + "properties": { + "confidence": { + "description": "How strongly the classification is supported.", + "type": "string" + }, + "last_seen": { + "description": "The most recent date this address was observed in this dataset.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The provider, or an empty string where the dataset has none.", + "type": "string" + } + }, + "type": "object" + }, + "dcproxy": { + "description": "Detail for `is_dcproxy`. Empty when false. Max only.", + "properties": { + "first_seen": { + "description": "The earliest date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "hits": { + "description": "How many times the address was observed in the pool during the window.", + "type": "integer" + }, + "hits_days_pct": { + "description": "The share of days in the window on which the address was seen, as a\npercentage. A high value means a stable pool member rather than a\none-off sighting.\n", + "type": "integer" + }, + "last_seen": { + "description": "The most recent date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The proxy network, or an empty string where unattributed.", + "type": "string" + }, + "providers_num": { + "description": "How many distinct proxy networks this address was seen in.", + "type": "integer" + } + }, + "type": "object" + }, + "hosting": { + "description": "Detail for `is_hosting`. Empty when false. Scale and above.", + "properties": { + "confidence": { + "description": "How strongly the classification is supported.", + "type": "string" + }, + "last_seen": { + "description": "The most recent date this address was observed in this dataset.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The provider, or an empty string where the dataset has none.", + "type": "string" + } + }, + "type": "object" + }, + "ip": { + "description": "The address that was looked up, normalized.", + "type": "string" + }, + "is_bogon": { + "description": "Present, and true, only for a private, reserved or otherwise non-routable address. Such an answer is given locally without calling the API, so every other flag in it is false by definition rather than by lookup. Absent from every served answer.", + "type": "boolean" + }, + "is_cdn": { + "description": "Whether the address belongs to a CDN. Starter and above.", + "type": "boolean" + }, + "is_dcproxy": { + "description": "Whether the address was seen in a datacenter proxy pool. Scale and above.", + "type": "boolean" + }, + "is_hosting": { + "description": "Whether the address belongs to a hosting or cloud provider. Starter and above.", + "type": "boolean" + }, + "is_mobproxy": { + "description": "Whether the address was seen in a mobile proxy pool. Scale and above.", + "type": "boolean" + }, + "is_relay": { + "description": "Whether the address is a privacy relay egress. Starter and above.", + "type": "boolean" + }, + "is_resproxy": { + "description": "Whether the address was seen in a residential proxy pool. Scale and above.", + "type": "boolean" + }, + "is_tor": { + "description": "Whether the address is a Tor node. Starter and above.", + "type": "boolean" + }, + "is_vpn": { + "description": "Whether the address is VPN infrastructure. Keys off presence in the\nVPN dataset, so an unattributed range with no provider is still\n`true`.\n", + "type": "boolean" + }, + "mobproxy": { + "description": "Detail for `is_mobproxy`. Empty when false. Max only.", + "properties": { + "first_seen": { + "description": "The earliest date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "hits": { + "description": "How many times the address was observed in the pool during the window.", + "type": "integer" + }, + "hits_days_pct": { + "description": "The share of days in the window on which the address was seen, as a\npercentage. A high value means a stable pool member rather than a\none-off sighting.\n", + "type": "integer" + }, + "last_seen": { + "description": "The most recent date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The proxy network, or an empty string where unattributed.", + "type": "string" + }, + "providers_num": { + "description": "How many distinct proxy networks this address was seen in.", + "type": "integer" + } + }, + "type": "object" + }, + "relay": { + "description": "Detail for `is_relay`. Empty when false. Scale and above.", + "properties": { + "confidence": { + "description": "How strongly the classification is supported.", + "type": "string" + }, + "last_seen": { + "description": "The most recent date this address was observed in this dataset.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The provider, or an empty string where the dataset has none.", + "type": "string" + } + }, + "type": "object" + }, + "resproxy": { + "description": "Detail for `is_resproxy`. Empty when false. Max only.", + "properties": { + "first_seen": { + "description": "The earliest date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "hits": { + "description": "How many times the address was observed in the pool during the window.", + "type": "integer" + }, + "hits_days_pct": { + "description": "The share of days in the window on which the address was seen, as a\npercentage. A high value means a stable pool member rather than a\none-off sighting.\n", + "type": "integer" + }, + "last_seen": { + "description": "The most recent date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The proxy network, or an empty string where unattributed.", + "type": "string" + }, + "providers_num": { + "description": "How many distinct proxy networks this address was seen in.", + "type": "integer" + } + }, + "type": "object" + }, + "tor": { + "description": "Detail for `is_tor`. Empty when false. Scale and above. The tor\ndataset carries no provider, so `provider` is always an empty string.\n", + "properties": { + "confidence": { + "description": "How strongly the classification is supported.", + "type": "string" + }, + "last_seen": { + "description": "The most recent date this address was observed in this dataset.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The provider, or an empty string where the dataset has none.", + "type": "string" + } + }, + "type": "object" + }, + "vpn": { + "description": "Detail for `is_vpn`. Empty when `is_vpn` is false. Starter and above.\n", + "properties": { + "confidence": { + "description": "How strongly the attribution is supported. Max only.", + "type": "string" + }, + "last_seen": { + "description": "The most recent date this address was observed as VPN infrastructure.", + "format": "date", + "type": "string" + }, + "method": { + "description": "The class of evidence the attribution rests on, one of four values.\nMax only.\n\n`scan` - we spoke the VPN protocol to the address ourselves and got a\nvalid server response. `scrape` - the operator published the address\nthrough its own API, client or configuration. `registry` - public\nregistration or naming records attribute it to the operator.\n`infer` - the address was extrapolated from confirmed neighbors in\nthe same block.\n", + "type": "string" + }, + "provider": { + "description": "The VPN provider, or an empty string for an unattributed range.", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "ip", + "is_vpn" + ], + "type": "object" + }, + { + "properties": { + "error": { + "properties": { + "kind": { + "description": "How the client SDK classified the failure, such as `bad_request` for a string that is not an IP address.", + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "kind", + "message" + ], + "type": "object" + } + }, + "required": [ + "error" + ], + "type": "object" + } +]
- Changed
my_entitlement1 field changed- added
Input schema / propertiesAdded value: +{}
7 tool updates
v5.3.4- Changed
database_checksum5 fields changed- changed
Input schema / properties / dataset_id / descriptionPrevious value: -"The dataset id, as returned by `list_databases`."New value: +"A VERSIONED database id, from `versions[].id` in `list_databases` - `cdn_ip_v1`, not `cdn_ip`. The unversioned base id is a license reference and is not accepted here." - removed
Output schema / properties / checksums / additionalPropertiesRemoved value: -true - added
Output schema / properties / checksums / descriptionAdded value: +"The published digests for one database file." - added
Output schema / properties / checksums / propertiesAdded value: +{ + "md5": { + "type": "string" + }, + "sha1": { + "type": "string" + }, + "sha256": { + "type": "string" + }, + "sha512": { + "type": "string" + } +} - added
Output schema / properties / checksums / requiredAdded value: +[ + "md5", + "sha1", + "sha256", + "sha512" +]
- Changed
database_metadata3 fields changed- changed
Input schema / properties / dataset_id / descriptionPrevious value: -"The dataset id, as returned by `list_databases`."New value: +"A VERSIONED database id, from `versions[].id` in `list_databases` - `cdn_ip_v1`, not `cdn_ip`. The unversioned base id is a license reference and is not accepted here." - added
Output schema / properties / sample_size / additionalProperties / formatAdded value: +"int64" - added
Output schema / properties / size / additionalProperties / formatAdded value: +"int64"
- Changed
list_databases3 fields changed- added
Output schema / properties / databasesAdded value: +{ + "items": { + "description": "One database FAMILY, with your organization's license beside it. A license covers\nthe family, while a download names a specific version, so the ids you\npass to the download and checksum endpoints come from `versions`.\n", + "properties": { + "base": { + "description": "The database family, e.g. `vpn_ip`. What the license is held against.", + "type": "string" + }, + "expires": { + "description": "A hard stop. Null when the license has no end date, which is the normal case for a rolling agreement, and when there is no license. A rolling license reports its turnover date in renews_at instead.", + "format": "date-time", + "type": [ + "string", + "null" + ] + }, + "in_term": { + "description": "False when the license has lapsed; downloads are refused.", + "type": "boolean" + }, + "license_type": { + "description": "What a license permits you to do with the data. Null for a family\nyou hold no license for, which is every one with standing\n`unlicensed`.\n", + "enum": [ + "evaluation", + "standard", + "redistribute", + null + ], + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + }, + "notice_due_at": { + "description": "The last day notice of non-renewal can be given for the term ending at renews_at. Null whenever renews_at is, and when the agreement records no notice period.", + "format": "date-time", + "type": [ + "string", + "null" + ] + }, + "renews_at": { + "description": "When a rolling license next renews. Null when the license has no defined term, when expires sets a hard stop instead, and when there is no license.", + "format": "date-time", + "type": [ + "string", + "null" + ] + }, + "standing": { + "description": "Where your license for a database family stands today. `licensed` is a\nlive grant, `expired` one whose term has ended, and `unlicensed` a\ndatabase published but never bought.\n", + "enum": [ + "expired", + "licensed", + "unlicensed" + ], + "type": "string" + }, + "starts": { + "format": "date-time", + "type": [ + "string", + "null" + ] + }, + "summary": { + "type": "string" + }, + "versions": { + "description": "Every published version of this family. The `id` here is what the\ndownload and checksum endpoints take.\n", + "items": { + "properties": { + "formats": { + "items": { + "properties": { + "bytes": { + "description": "Size of the published file, or null when it has not been published\nyet. int64 because it is not hypothetical: resproxy_ip_14d's MMDB is\n4.58 GB, so a 32-bit field cannot carry the catalogue and `list`\nthrows for every caller rather than for that one entry.\n", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "format": { + "description": "A file format a database version is published in.", + "enum": [ + "csvgz", + "mmdb" + ], + "type": "string" + } + }, + "required": [ + "format", + "bytes" + ], + "type": "object" + }, + "type": "array" + }, + "id": { + "description": "The versioned database id, e.g. `vpn_ip_v1`. Pass this to download.", + "type": "string" + }, + "sample_formats": { + "description": "The formats an evaluation sample is published in, if any.", + "items": { + "description": "A file format a database version is published in.", + "enum": [ + "csvgz", + "mmdb" + ], + "type": "string" + }, + "type": "array" + }, + "summary": { + "type": "string" + }, + "version": { + "type": "integer" + } + }, + "required": [ + "id", + "version", + "formats" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "base", + "name", + "summary", + "license_type", + "starts", + "expires", + "renews_at", + "notice_due_at", + "in_term", + "standing", + "versions" + ], + "type": "object" + }, + "type": "array" +} - removed
Output schema / properties / datasetsRemoved value: -{ - "items": { - "description": "One dataset FAMILY your organization is licensed for. A license covers\nthe family, while a download names a specific version, so the ids you\npass to the download and checksum endpoints come from `versions`.\n", - "properties": { - "base": { - "description": "The dataset family, e.g. `vpn_ip`. What the license is held against.", - "type": "string" - }, - "expires": { - "description": "A hard stop. Null when the license has no end date, which is the normal case for a rolling agreement, and when there is no license. A rolling license reports its turnover date in renews_at instead.", - "format": "date-time", - "nullable": true, - "type": "string" - }, - "in_term": { - "description": "False when the license has lapsed; downloads are refused.", - "type": "boolean" - }, - "license_type": { - "description": "What your license permits you to do with the data.", - "enum": [ - "evaluation", - "standard", - "redistribute" - ], - "type": "string" - }, - "name": { - "type": "string" - }, - "notice_due_at": { - "description": "The last day notice of non-renewal can be given for the term ending at renews_at. Null whenever renews_at is, and when the agreement records no notice period.", - "format": "date-time", - "nullable": true, - "type": "string" - }, - "renews_at": { - "description": "When a rolling license next renews. Null when the license has no defined term, when expires sets a hard stop instead, and when there is no license.", - "format": "date-time", - "nullable": true, - "type": "string" - }, - "standing": { - "description": "`licensed` is a live grant, `expired` one whose term has ended, and\n`unlicensed` a dataset published but never bought.\n", - "enum": [ - "expired", - "licensed", - "unlicensed" - ], - "type": "string" - }, - "starts": { - "format": "date-time", - "nullable": true, - "type": "string" - }, - "summary": { - "type": "string" - }, - "versions": { - "description": "Every published version of this family. The `id` here is what the\ndownload and checksum endpoints take.\n", - "items": { - "properties": { - "formats": { - "items": { - "properties": { - "bytes": { - "description": "Size of the published file, or null when it has not been published yet", - "nullable": true, - "type": "integer" - }, - "format": { - "enum": [ - "csvgz", - "mmdb" - ], - "type": "string" - } - }, - "required": [ - "format", - "bytes" - ], - "type": "object" - }, - "type": "array" - }, - "id": { - "description": "The versioned dataset id, e.g. `vpn_ip_v1`. Pass this to download.", - "type": "string" - }, - "sampleFormats": { - "description": "The formats an evaluation sample is published in, if any.", - "items": { - "enum": [ - "csvgz", - "mmdb" - ], - "type": "string" - }, - "type": "array" - }, - "summary": { - "type": "string" - }, - "version": { - "type": "integer" - } - }, - "required": [ - "id", - "version", - "formats" - ], - "type": "object" - }, - "type": "array" - } - }, - "required": [ - "base", - "name", - "summary", - "license_type", - "starts", - "expires", - "renews_at", - "notice_due_at", - "in_term", - "standing", - "versions" - ], - "type": "object" - }, - "type": "array" -} - changed
Output schema / requiredPrevious value: -[ - "datasets" -]New value: +[ + "databases" +]
- Added
list_downloads - Changed
lookup_ip1 field changed- added
Output schema / properties / result / properties / is_bogonAdded value: +{ + "description": "Present, and true, only for a private, reserved or otherwise non-routable address. Such an answer is given locally without calling the API, so every other flag in it is false by definition rather than by lookup. Absent from every served answer.", + "type": "boolean" +}
- Changed
lookup_ips3 fields changed- changed
Input schema / properties / ips / descriptionPrevious value: -"The addresses to classify, at most 100."New value: +"The IPv4 or IPv6 addresses to classify." - removed
Input schema / properties / ips / maxItemsRemoved value: -100 - changed
Output schema / properties / results / additionalPropertiesPrevious value: -trueNew value: +{ + "anyOf": [ + { + "description": "The union of every plan's answer. Only `ip` and `is_vpn` are guaranteed;\neach remaining field is present when your plan includes it, and absent\notherwise. A client that must work across plans should treat an absent\nflag as unknown rather than as `false`.\n", + "properties": { + "cdn": { + "description": "Detail for `is_cdn`. Empty when false. Scale and above.", + "properties": { + "confidence": { + "description": "How strongly the classification is supported.", + "type": "string" + }, + "last_seen": { + "description": "The most recent date this address was observed in this dataset.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The provider, or an empty string where the dataset has none.", + "type": "string" + } + }, + "type": "object" + }, + "dcproxy": { + "description": "Detail for `is_dcproxy`. Empty when false. Max only.", + "properties": { + "first_seen": { + "description": "The earliest date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "hits": { + "description": "How many times the address was observed in the pool during the window.", + "type": "integer" + }, + "hits_days_pct": { + "description": "The share of days in the window on which the address was seen, as a\npercentage. A high value means a stable pool member rather than a\none-off sighting.\n", + "type": "integer" + }, + "last_seen": { + "description": "The most recent date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The proxy network, or an empty string where unattributed.", + "type": "string" + }, + "providers_num": { + "description": "How many distinct proxy networks this address was seen in.", + "type": "integer" + } + }, + "type": "object" + }, + "hosting": { + "description": "Detail for `is_hosting`. Empty when false. Scale and above.", + "properties": { + "confidence": { + "description": "How strongly the classification is supported.", + "type": "string" + }, + "last_seen": { + "description": "The most recent date this address was observed in this dataset.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The provider, or an empty string where the dataset has none.", + "type": "string" + } + }, + "type": "object" + }, + "ip": { + "description": "The address that was looked up, normalized.", + "type": "string" + }, + "is_bogon": { + "description": "Present, and true, only for a private, reserved or otherwise non-routable address. Such an answer is given locally without calling the API, so every other flag in it is false by definition rather than by lookup. Absent from every served answer.", + "type": "boolean" + }, + "is_cdn": { + "description": "Whether the address belongs to a CDN. Starter and above.", + "type": "boolean" + }, + "is_dcproxy": { + "description": "Whether the address was seen in a datacenter proxy pool. Scale and above.", + "type": "boolean" + }, + "is_hosting": { + "description": "Whether the address belongs to a hosting or cloud provider. Starter and above.", + "type": "boolean" + }, + "is_mobproxy": { + "description": "Whether the address was seen in a mobile proxy pool. Scale and above.", + "type": "boolean" + }, + "is_relay": { + "description": "Whether the address is a privacy relay egress. Starter and above.", + "type": "boolean" + }, + "is_resproxy": { + "description": "Whether the address was seen in a residential proxy pool. Scale and above.", + "type": "boolean" + }, + "is_tor": { + "description": "Whether the address is a Tor node. Starter and above.", + "type": "boolean" + }, + "is_vpn": { + "description": "Whether the address is VPN infrastructure. Keys off presence in the\nVPN dataset, so an unattributed range with no provider is still\n`true`.\n", + "type": "boolean" + }, + "mobproxy": { + "description": "Detail for `is_mobproxy`. Empty when false. Max only.", + "properties": { + "first_seen": { + "description": "The earliest date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "hits": { + "description": "How many times the address was observed in the pool during the window.", + "type": "integer" + }, + "hits_days_pct": { + "description": "The share of days in the window on which the address was seen, as a\npercentage. A high value means a stable pool member rather than a\none-off sighting.\n", + "type": "integer" + }, + "last_seen": { + "description": "The most recent date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The proxy network, or an empty string where unattributed.", + "type": "string" + }, + "providers_num": { + "description": "How many distinct proxy networks this address was seen in.", + "type": "integer" + } + }, + "type": "object" + }, + "relay": { + "description": "Detail for `is_relay`. Empty when false. Scale and above.", + "properties": { + "confidence": { + "description": "How strongly the classification is supported.", + "type": "string" + }, + "last_seen": { + "description": "The most recent date this address was observed in this dataset.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The provider, or an empty string where the dataset has none.", + "type": "string" + } + }, + "type": "object" + }, + "resproxy": { + "description": "Detail for `is_resproxy`. Empty when false. Max only.", + "properties": { + "first_seen": { + "description": "The earliest date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "hits": { + "description": "How many times the address was observed in the pool during the window.", + "type": "integer" + }, + "hits_days_pct": { + "description": "The share of days in the window on which the address was seen, as a\npercentage. A high value means a stable pool member rather than a\none-off sighting.\n", + "type": "integer" + }, + "last_seen": { + "description": "The most recent date within the window this address was seen in the pool.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The proxy network, or an empty string where unattributed.", + "type": "string" + }, + "providers_num": { + "description": "How many distinct proxy networks this address was seen in.", + "type": "integer" + } + }, + "type": "object" + }, + "tor": { + "description": "Detail for `is_tor`. Empty when false. Scale and above. The tor\ndataset carries no provider, so `provider` is always an empty string.\n", + "properties": { + "confidence": { + "description": "How strongly the classification is supported.", + "type": "string" + }, + "last_seen": { + "description": "The most recent date this address was observed in this dataset.", + "format": "date", + "type": "string" + }, + "provider": { + "description": "The provider, or an empty string where the dataset has none.", + "type": "string" + } + }, + "type": "object" + }, + "vpn": { + "description": "Detail for `is_vpn`. Empty when `is_vpn` is false. Starter and above.\n", + "properties": { + "confidence": { + "description": "How strongly the attribution is supported. Max only.", + "type": "string" + }, + "last_seen": { + "description": "The most recent date this address was observed as VPN infrastructure.", + "format": "date", + "type": "string" + }, + "method": { + "description": "The class of evidence the attribution rests on, one of four values.\nMax only.\n\n`scan` - we spoke the VPN protocol to the address ourselves and got a\nvalid server response. `scrape` - the operator published the address\nthrough its own API, client or configuration. `registry` - public\nregistration or naming records attribute it to the operator.\n`infer` - the address was extrapolated from confirmed neighbours in\nthe same block.\n", + "type": "string" + }, + "provider": { + "description": "The VPN provider, or an empty string for an unattributed range.", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "ip", + "is_vpn" + ], + "type": "object" + }, + { + "properties": { + "error": { + "properties": { + "kind": { + "description": "How the client SDK classified the failure, such as `bad_request` for a string that is not an IP address.", + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "kind", + "message" + ], + "type": "object" + } + }, + "required": [ + "error" + ], + "type": "object" + } + ] +}
- Added
my_entitlement
5 tool updates
v1.0.0- First observed
database_checksum - First observed
database_metadata - First observed
list_databases - First observed
lookup_ip - First observed
lookup_ips
TDQS
Scored across 7 tools
The only close pair is lookup_ip and lookup_ips, but their names and descriptions clearly distinguish single vs batch and explicitly recommend the batch tool for lists. The database tools separate catalog listing, metadata inspection, checksum lookup, and download history into clearly different facets, so an agent should not misselect.
All names use snake_case, but the set mixes verb_noun patterns (lookup_ip, list_databases, list_downloads) with noun_noun patterns (database_metadata, database_checksum) and the non-verb my_entitlement. It remains readable, but there is no single predictable naming convention.
Seven tools is well within the appropriate range for this server and each tool covers a distinct capability: entitlement, single/batch lookup, database catalog, metadata, checksum, and download history. There is no obvious filler.
The IP lookup side is complete with single and batch operations plus entitlement context, and the database side covers discovery, metadata, checksum verification, and download history. However, there is no tool to initiate or retrieve a database download, which leaves an obvious dead end for the database acquisition workflow implied by the descriptions.
Maintenance
Related MCP Connectors
Free no-key IP intelligence: geolocation, VPN detection, DNS, WHOIS, blacklists, breach checks
IP intelligence, network diagnostics, and AI service availability tools for AI agents.
IP geolocation, ASN and network data, plus VPN, proxy and Tor detection. ASN tools need no key.
Free, no-API-key network lookups for AI agents — IP geolocation, DNS, WHOIS, HTTP and SSL/TLS certificate checks, and full domain health reports.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceEnables checking any IP address for VPN, proxy, Tor, datacenter hosting, anonymity, and fraud risk signals, plus API service status, directly from MCP-capable clients like Claude and Cursor.98 npm2MIT
- AlicenseNot gradedqualityBmaintenanceEnables IP address lookups and risk assessments for IPv4/IPv6, providing geolocation, ISP/ASN, and security flags such as VPN, proxy, Tor, datacenter, and mobile with a risk score.652 npmMIT
- AlicenseNot gradedqualityCmaintenanceEnables AI assistants to query IP intelligence tools such as geolocation, VPN/security checks, ASN, timezone, astronomy, user-agent parsing, abuse contacts, and bulk lookups via natural language.9 npmMIT
- AlicenseAqualityDmaintenanceGives AI agents threat-intelligence verdicts for IPs, domains, URLs and hashes, CVE lookups with EPSS and CISA KEV data, and a prompt-injection gate that scans untrusted text before the agent acts on it. Agents can also batch-check up to 100 indicators, hunt brand-lookalike domains, and track scan and request quotas.1208 npm1MIT