Skip to main content
Glama
VPNDetection MCP Server

VPNDetection MCP Server

Official

VPNDetection MCP Server

npm license

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@vpndetection

Either 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 vpndetection

Either 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

lookup_ip

Classify one address.

lookup_ips

Classify a whole list of addresses in one call, keyed by address. A long list is batched for you.

my_entitlement

What this key is entitled to and what it has spent: plan, field tier, requests so far, allowance, and when it resets.

list_databases

Every database we publish, with its standing for your organization: licensed, expired or unlicensed.

database_metadata

A database's columns, sample rows, row count, build date and file sizes.

database_checksum

The published digests for one database file.

list_downloads

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 tools
database_checksumGet a database's checksumsA
Read-onlyIdempotent
Inspect

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.

NameRequiredDescriptionDefault
formatYesWhich published file to digest.
dataset_idYesA 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

NameRequiredDescription
checksumsYesThe published digests for one database file.

TDQS

A4.3/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines5/5

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 databaseA
Read-onlyIdempotent
Inspect

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.

NameRequiredDescriptionDefault
dataset_idYesA 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

NameRequiredDescription
idYes
sizeNoBytes per format
sampleNoA few real rows, keyed by format
schemaYesColumns, keyed by format
entriesYesRow count in the current build
updatedYes
sample_sizeNoBytes per format of the evaluation sample, where one is published
update_freqNoHow often a new build is published
sample_entriesNoRow count in the evaluation sample

TDQS

A4/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines4/5

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 databasesA
Read-onlyIdempotent
Inspect

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.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
databasesYes

TDQS

A4.6/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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 attemptsA
Read-only
Inspect

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.

NameRequiredDescriptionDefault
limitNoHow many attempts to return, newest first. At most 200; the API defaults to 50.

Output Schema

NameRequiredDescription
downloadsYes

TDQS

A4.7/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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 addressA
Read-onlyIdempotent
Inspect

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.

NameRequiredDescriptionDefault
ipYesThe IPv4 or IPv6 address to classify.

Output Schema

NameRequiredDescription
resultYesThe 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`.
coverageYesWhich datasets this result covered. Read `note` before interpreting any absent field.

TDQS

A4.2/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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 addressesA
Read-onlyIdempotent
Inspect

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.

NameRequiredDescriptionDefault
ipsYesThe IPv4 or IPv6 addresses to classify.

Output Schema

NameRequiredDescription
resultsYesKeyed by address. A value is either a result or an error.
coverageYesWhich datasets this result covered. Read `note` before interpreting any absent field.

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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 keyA
Read-only
Inspect

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.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
entitlementYes

TDQS

A4.4/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines4/5

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.

  1. 4 tool updatesv6.2.5
    • Changeddatabase_checksum1 field changed
      • changedInput schema / properties / dataset_id / description
        Previous 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."
    • Changeddatabase_metadata1 field changed
      • changedInput schema / properties / dataset_id / description
        Previous 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."
    • Changedlist_databases2 fields changed
      • changedOutput schema / properties / databases / items / description
        Previous 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"
      • changedOutput schema / properties / databases / items / properties / versions / items / properties / id / description
        Previous 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."
    • Changedmy_entitlement1 field changed
      • changedOutput schema / properties / entitlement / properties / apikey / description
        Previous 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"
  2. 4 tool updatesv6.2.0
    • Changedlist_databases2 fields changed
      • addedOutput schema / properties / databases / items / properties / open
        Added 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"
        +}
      • changedOutput schema / properties / databases / items / required
        Previous 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"
        +]
    • Changedlist_downloads2 fields changed
      • addedOutput schema / properties / downloads / items / properties / open
        Added value: +{
        +  "description": "A download of an Open database.",
        +  "type": "boolean"
        +}
      • changedOutput schema / properties / downloads / items / required
        Previous 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"
        +]
    • Changedlookup_ip22 fields changed
      • addedOutput schema / properties / result / properties / asn
        Added 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"
        +}
      • changedOutput schema / properties / result / properties / cdn / description
        Previous value: -"Detail for `is_cdn`. Empty when false. Scale and above."New value: +"Detail for `is_cdn`. Empty when false. Paid plans."
      • changedOutput schema / properties / result / properties / dcproxy / description
        Previous value: -"Detail for `is_dcproxy`. Empty when false. Max only."New value: +"Detail for `is_dcproxy`. Empty when false. Paid plans."
      • changedOutput schema / properties / result / properties / hosting / description
        Previous value: -"Detail for `is_hosting`. Empty when false. Scale and above."New value: +"Detail for `is_hosting`. Empty when false. Paid plans."
      • changedOutput schema / properties / result / properties / is_cdn / description
        Previous value: -"Whether the address belongs to a CDN. Starter and above."New value: +"Whether the address belongs to a CDN. Paid plans."
      • changedOutput schema / properties / result / properties / is_dcproxy / description
        Previous 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."
      • changedOutput schema / properties / result / properties / is_hosting / description
        Previous 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."
      • changedOutput schema / properties / result / properties / is_mobproxy / description
        Previous 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."
      • changedOutput schema / properties / result / properties / is_pubproxy / description
        Previous 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."
      • changedOutput schema / properties / result / properties / is_relay / description
        Previous 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."
      • changedOutput schema / properties / result / properties / is_resproxy / description
        Previous 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."
      • changedOutput schema / properties / result / properties / is_tor / description
        Previous value: -"Whether the address is a Tor node. Starter and above."New value: +"Whether the address is a Tor node. Free and paid plans."
      • addedOutput schema / properties / result / properties / location
        Added 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"
        +}
      • changedOutput schema / properties / result / properties / mobproxy / description
        Previous value: -"Detail for `is_mobproxy`. Empty when false. Max only."New value: +"Detail for `is_mobproxy`. Empty when false. Paid plans."
      • changedOutput schema / properties / result / properties / pubproxy / description
        Previous value: -"Detail for `is_pubproxy`. Empty when false. Max only."New value: +"Detail for `is_pubproxy`. Empty when false. Paid plans."
      • changedOutput schema / properties / result / properties / relay / description
        Previous value: -"Detail for `is_relay`. Empty when false. Scale and above."New value: +"Detail for `is_relay`. Empty when false. Free and paid plans."
      • changedOutput schema / properties / result / properties / resproxy / description
        Previous value: -"Detail for `is_resproxy`. Empty when false. Max only."New value: +"Detail for `is_resproxy`. Empty when false. Paid plans."
      • changedOutput schema / properties / result / properties / tor / description
        Previous 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"
      • changedOutput schema / properties / result / properties / vpn / description
        Previous 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"
      • changedOutput schema / properties / result / properties / vpn / properties / confidence / description
        Previous value: -"How strongly the attribution is supported. Max only."New value: +"How strongly the attribution is supported."
      • addedOutput schema / properties / result / properties / vpn / properties / method / deprecated
        Added value: +true
      • changedOutput schema / properties / result / properties / vpn / properties / method / description
        Previous 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"
    • Changedlookup_ips1 field changed
      • changedOutput schema / properties / results / additionalProperties / anyOf
        Previous 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"
        +  }
        +]
  3. 2 tool updatesv6.1.1
    • Changedlookup_ip3 fields changed
      • addedOutput schema / properties / result / properties / is_pubproxy
        Added 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"
        +}
      • changedOutput schema / properties / result / properties / is_relay / description
        Previous 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."
      • addedOutput schema / properties / result / properties / pubproxy
        Added 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"
        +}
    • Changedlookup_ips1 field changed
      • changedOutput schema / properties / results / additionalProperties / anyOf
        Previous 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"
        +  }
        +]
  4. 7 tool updatesv6.0.4
    • Changeddatabase_checksum1 field changed
      • addedInput schema / additionalProperties
        Added value: +false
    • Changeddatabase_metadata1 field changed
      • addedInput schema / additionalProperties
        Added value: +false
    • Changedlist_databases2 fields changed
      • addedInput schema / properties
        Added value: +{}
      • changedOutput schema / properties / databases / items / properties / versions / items / properties / formats / items / properties / bytes / description
        Previous 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"
    • Changedlist_downloads1 field changed
      • addedInput schema / additionalProperties
        Added value: +false
    • Changedlookup_ip2 fields changed
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / properties / result / properties / vpn / properties / method / description
        Previous 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"
    • Changedlookup_ips2 fields changed
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / properties / results / additionalProperties / anyOf
        Previous 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"
        +  }
        +]
    • Changedmy_entitlement1 field changed
      • addedInput schema / properties
        Added value: +{}
  5. 7 tool updatesv5.3.4
    • Changeddatabase_checksum5 fields changed
      • changedInput schema / properties / dataset_id / description
        Previous 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."
      • removedOutput schema / properties / checksums / additionalProperties
        Removed value: -true
      • addedOutput schema / properties / checksums / description
        Added value: +"The published digests for one database file."
      • addedOutput schema / properties / checksums / properties
        Added value: +{
        +  "md5": {
        +    "type": "string"
        +  },
        +  "sha1": {
        +    "type": "string"
        +  },
        +  "sha256": {
        +    "type": "string"
        +  },
        +  "sha512": {
        +    "type": "string"
        +  }
        +}
      • addedOutput schema / properties / checksums / required
        Added value: +[
        +  "md5",
        +  "sha1",
        +  "sha256",
        +  "sha512"
        +]
    • Changeddatabase_metadata3 fields changed
      • changedInput schema / properties / dataset_id / description
        Previous 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."
      • addedOutput schema / properties / sample_size / additionalProperties / format
        Added value: +"int64"
      • addedOutput schema / properties / size / additionalProperties / format
        Added value: +"int64"
    • Changedlist_databases3 fields changed
      • addedOutput schema / properties / databases
        Added 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"
        +}
      • removedOutput schema / properties / datasets
        Removed 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"
        -}
      • changedOutput schema / required
        Previous value: -[
        -  "datasets"
        -]New value: +[
        +  "databases"
        +]
    • Addedlist_downloads
    • Changedlookup_ip1 field changed
      • addedOutput schema / properties / result / properties / is_bogon
        Added 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"
        +}
    • Changedlookup_ips3 fields changed
      • changedInput schema / properties / ips / description
        Previous value: -"The addresses to classify, at most 100."New value: +"The IPv4 or IPv6 addresses to classify."
      • removedInput schema / properties / ips / maxItems
        Removed value: -100
      • changedOutput schema / properties / results / additionalProperties
        Previous 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"
        +    }
        +  ]
        +}
    • Addedmy_entitlement
  6. 5 tool updatesv1.0.0
    • First observeddatabase_checksum
    • First observeddatabase_metadata
    • First observedlist_databases
    • First observedlookup_ip
    • First observedlookup_ips

TDQS

A4.1/5.0

Scored across 7 tools

Disambiguation5/5

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.

Naming Consistency3/5

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.

Tool Count5/5

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.

Completeness3/5

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

ActivityActive
ResponsivenessSyncing

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables 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 npm
    2
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables 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 npm
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables 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 npm
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Gives 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.
    1
    208 npm
    1
    MIT