# VPN and Proxy Detection API - Spur IP Context and Tags (`nabeelbaghoor/vpn-proxy-detection-api`) Actor

Detect VPNs, residential proxies, Tor and anonymizers on IP addresses with the Spur Context API: tunnel operators, proxy services, risks, infrastructure, ASN, geolocation and client counts, current or for a past date, plus service tag metadata. Bring your own key.

- **URL**: https://apify.com/nabeelbaghoor/vpn-proxy-detection-api.md
- **Developed by:** [Nabeel Hassan](https://apify.com/nabeelbaghoor) (community)
- **Categories:** Developer tools, Business, Integrations
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 ip context returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## VPN and Proxy Detection API - Spur IP Context and Tags

Detect VPNs, residential proxies, Tor and other anonymizers behind any IP address with the Spur Context API (api.spur.us) and your own API token, as one clean dataset row per IP address or service tag.

Works with the official Spur Context API: IP context lookups (GET /v2/context/:ip), historical IP context for a past date (?dt=), and service tag metadata (GET /v2/metadata/tags/:tag). Paste a list of IP addresses and get back which VPN or proxy services use each one, its risk factors, infrastructure type, ASN, organization and location, ready to download as JSON, CSV or Excel, schedule, or feed into fraud scoring, account security, SOC enrichment, ad fraud checks, Google Sheets and AI agents. Bring your own API key.

BYOK, not live-tested: every endpoint, parameter and field comes from Spur's documentation on docs.spur.us, and the routes were confirmed with a deliberately invalid token (documented routes answer 401, invented paths 404), but the actor has not been run against a live Spur token.

### What it does

- **VPN detection**: the tunnels the IP belongs to, with each tunnel's type (VPN, Tor and others), operator tag (such as PROTON_VPN or NORD_VPN), whether it is anonymous, and its entry IPs.
- **Residential proxy detection**: the callback and residential proxy services Spur has seen on the IP (such as OXYLABS_PROXY or NETNUT_PROXY), which ordinary IP geolocation databases miss.
- **Risk factors**: risks such as TUNNEL, CALLBACK_PROXY, GEO_MISMATCH, LOGIN_BRUTEFORCE or WEB_SCRAPING, and services such as OPENVPN, WIREGUARD or IPSEC.
- **Infrastructure and network**: infrastructure type (DATACENTER, MOBILE, SATELLITE and others), ASN number and organization, and the organization assigned the IP.
- **Client context**: how many devices use the IP, their device types and behaviors, how many countries they span, and where they concentrate.
- **Geolocation**: Spur IP Geo country, state and city, or MaxMind GeoLite2 location on request.
- **Historical lookups**: the IP context Spur had on a past date, for investigating old logins, chargebacks and incidents (Pro plans and above).
- **Service tag metadata**: for any VPN or proxy service tag, its name, description, website, categories (residential, datacenter, mobile, ISP proxy), protocols, platforms, no-log and anonymity flags, resellers, and metrics such as distinct IPs, ASNs and countries.
- **Pay only for results**: refused IPs and unknown tags are recorded as a free row that says why, never charged.

### How to get an API key

Spur sells Context API access as plans on spur.us, and enterprise plans through a Spur representative. Once your plan includes the Context API, an organization admin or developer can copy the token in the Spur app under Settings, API. Paste it into the API key field, or set the DATA_API_KEY secret. The token is sent only to api.spur.us in the Token header and is removed from everything the actor logs. Historical lookups need a token with the Context API Historical Query permission (Pro plans and above). Each lookup uses one query from your plan, and the run's status message reports the queries your token has left.

### Input

| Field | Modes | Description |
| --- | --- | --- |
| `mode` | all | `ipContext` (default) or `tagMetadata` |
| `ips` | IP context | One public IPv4 or IPv6 address per line, such as `89.39.106.191` |
| `historicalDate` | IP context | Optional past date as `YYYY-MM-DD` for a historical lookup |
| `useMaxMindGeo` | IP context | `true` returns MaxMind GeoLite2 location instead of Spur's (default `false`) |
| `tags` | tag metadata | One service tag per line, such as `OXYLABS_PROXY` or `PROTON_VPN` |
| `maxResults` | all | Row ceiling for the run (default 100) |
| `requestsPerMinute` | all | Pacing, default 120 |
| `apiKey` | all | Your Spur API token (or the DATA_API_KEY secret) |

Example: check two IP addresses as they were on 26 October 2025.

```json
{
    "mode": "ipContext",
    "ips": ["89.39.106.191", "2001:4860:4860::8888"],
    "historicalDate": "2025-10-26"
}
```

### Example output

An IP context row, built from the example IP Context Object in Spur's documentation (illustrative, not a live answer). The full as, client, location and tunnels objects are kept next to the flat columns.

```json
{
    "mode": "ipContext",
    "modeLabel": "IP context",
    "recordType": "ipContext",
    "query": "89.39.106.191",
    "requestedDate": null,
    "resultDate": null,
    "geoSource": "spur",
    "found": true,
    "ip": "89.39.106.191",
    "infrastructure": "DATACENTER",
    "organization": "WorldStream B.V.",
    "asn": 49981,
    "asOrganization": "WorldStream",
    "country": "NL",
    "state": "North Holland",
    "city": "Amsterdam",
    "isAnonymous": true,
    "tunnelTypes": ["VPN"],
    "tunnelOperators": ["PROTON_VPN"],
    "proxyServices": ["ABCPROXY_PROXY", "9PROXY_PROXY", "NETNUT_PROXY", "GOPROXY_PROXY"],
    "risks": ["CALLBACK_PROXY", "TUNNEL", "GEO_MISMATCH"],
    "services": ["OPENVPN"],
    "clientCount": 4,
    "clientTypes": ["MOBILE", "DESKTOP"],
    "clientBehaviors": ["FILE_SHARING", "TOR_PROXY_USER"],
    "tunnels": [{ "anonymous": true, "entries": ["89.39.106.82"], "operator": "PROTON_VPN", "type": "VPN" }],
    "as": { "number": 49981, "organization": "WorldStream" },
    "location": { "city": "Amsterdam", "country": "NL", "state": "North Holland" },
    "client": { "count": 4, "countries": 2, "spread": 4724209, "types": ["MOBILE", "DESKTOP"] },
    "tag": null,
    "tagName": null,
    "categories": null,
    "distinctIPs": null,
    "distinctCountries": null,
    "retrievedAt": "2026-10-18T09:00:00.000Z",
    "note": null
}
```

### FAQ

#### What is the Spur Context API?

Spur (Spur Intelligence) tracks VPNs, residential proxies, callback proxies, Tor and other anonymizing infrastructure across the internet. Its Context API returns an IP Context Object for a single IPv4 or IPv6 address: whether it is anonymous, which VPN or proxy services and tunnels use it, the infrastructure behind it, and where it is located. Requests go to api.spur.us with a Token header.

#### Can this detect residential proxies, not just datacenter VPNs?

Yes. Residential and callback proxy services that Spur has observed on an IP come back in the proxyServices column (the client proxies field), with a CALLBACK_PROXY risk, even when the IP belongs to an ordinary home broadband or mobile network. VPN and Tor exits come back in tunnelTypes and tunnelOperators.

#### Do I need my own Spur API token?

Yes. This is a bring-your-own-key actor: it calls Spur with your token, under your Spur plan and terms. A missing, rejected or exhausted token ends the run cleanly with a status message that explains what to change, and nothing is charged.

#### Can I look up what an IP address was doing on a past date?

Yes. Set the historical date and the actor sends dt=YYYYmmdd, returning the IP context from that day's Spur data feed; resultDate shows the date Spur sourced the answer from. Historical lookups need the Context API Historical Query permission, on Pro plans and above, and a token without it is answered with 401.

#### What is a Spur service tag?

A service tag is Spur's identifier for one VPN, proxy or anonymizing service, such as OXYLABS_PROXY, NETNUT_PROXY or PROTON_VPN. The tag metadata mode returns that service's description, website, proxy categories, protocols, platforms, no-log and callback proxy flags, the services that resell it, and metrics such as distinct IPs, ASNs, ISPs and countries.

#### What happens when my queries run out?

Spur answers 429 when the token or the account has no queries left for the billing period. The run then stops cleanly with a status message, keeping every row already collected. A 401 (wrong token, or missing permission) or 403 (revoked token or disabled account) also ends the run cleanly. A private or malformed IP that Spur refuses (400) becomes a free row with the reason, and the run moves on.

#### What do I pay?

Pay per result: 0.01 USD per IP context row and 0.01 USD per service tag row. Refused IPs and unknown tags are never charged. Your Spur plan is billed separately by Spur, and each IP address or tag is one query against it.

### Pricing

| Event | Price | Charged for |
| --- | --- | --- |
| `ip-context` | 0.01 USD | Each IP address's IP Context Object |
| `tag-metadata` | 0.01 USD | Each service tag's metadata |

### Keyword map

VPN detection API, proxy detection API, residential proxy detection, Spur API, Spur Context API, api.spur.us, IP intelligence API, anonymous IP detection, Tor exit node detection, callback proxy detection, IP risk API, IP reputation, VPN IP lookup, bulk IP lookup, historical IP lookup, IP to ASN, IP geolocation, fraud prevention IP check, account takeover prevention, bot and ad fraud detection, threat intelligence enrichment.

### Terms and data use

Use of the Spur Context API is governed by Spur's Terms and Conditions and its Website and API Terms of Use (spur.us/terms). Section 3 of the Website and API Terms of Use says a Context API licence is valid only for the individual or organization that registered it, and that data obtained under a self-serve Context API licence is for non-commercial use only and may not be sold, distributed, shared or otherwise provided to anyone else; free licences are for evaluation only. Section 4 requires deleting all Spur data when the licence expires or ends. The general terms also forbid using the offerings to build a product that competes with Spur. Commercial use of the results, or use on behalf of another organization, needs a commercial agreement with Spur. Run this actor with your own token, keep the results within what your licence allows, and delete them when your licence ends. This actor is not affiliated with or endorsed by Spur.

# Actor input Schema

## `mode` (type: `string`):

IP context gives one row per IP address from GET /v2/context/:ip: VPN and Tor tunnels and their operators, residential proxy services seen on the IP, risks, services, infrastructure, ASN, organization, location and client counts. Service tag metadata gives one row per Spur service tag from GET /v2/metadata/tags/:tag. Each lookup is one query on your Spur plan.

## `ips` (type: `array`):

IP context only. One public IPv4 or IPv6 address per line, such as 89.39.106.191 or 2001:4860:4860::8888. No ports, ranges or CIDR blocks. Duplicates are looked up once.

## `historicalDate` (type: `string`):

IP context only, optional. A past date as YYYY-MM-DD to get the IP context Spur had on that day (sent as dt=YYYYmmdd). Needs a token with the Context API Historical Query permission, on Pro plans and above. Leave empty for current context.

## `useMaxMindGeo` (type: `boolean`):

IP context only. Sends mmgeo=1 so the location object carries MaxMind GeoLite2 geolocation instead of Spur's own IP geolocation.

## `tags` (type: `array`):

Service tag metadata only. One Spur service tag per line, such as OXYLABS_PROXY, NETNUT_PROXY or PROTON_VPN. Tags appear in the tunnels operator and client proxies fields of IP context rows, and on docs.spur.us/service-tags.

## `maxResults` (type: `integer`):

The most rows this run returns and charges for. Each row is one IP address or tag.

## `requestsPerMinute` (type: `integer`):

Pacing for calls to api.spur.us.

## `apiKey` (type: `string`):

Your own Spur API token, from Settings, API in the Spur app. Stored as a secret and sent only to api.spur.us in the Token header; it is never logged. You can instead set the DATA_API_KEY environment secret.

## Actor input object example

```json
{
  "mode": "ipContext",
  "ips": [
    "89.39.106.191"
  ],
  "useMaxMindGeo": false,
  "maxResults": 100,
  "requestsPerMinute": 120
}
```

# Actor output Schema

## `records` (type: `string`):

One row per IP address or service tag.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "ips": [
        "89.39.106.191"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nabeelbaghoor/vpn-proxy-detection-api").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "ips": ["89.39.106.191"] }

# Run the Actor and wait for it to finish
run = client.actor("nabeelbaghoor/vpn-proxy-detection-api").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "ips": [
    "89.39.106.191"
  ]
}' |
apify call nabeelbaghoor/vpn-proxy-detection-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nabeelbaghoor/vpn-proxy-detection-api"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/IEb7cTmrps42AWO2u/builds/8zDS4TY7ccLWfQgVl/openapi.json
