# EU VAT & Customs Territory Check by Postcode (`fetchgrove/eu-special-territory-classifier`) Actor

Is this address in the EU VAT area, customs territory, excise area and Schengen? Send a country and postcode or coordinates. Covers the Canary Islands, Ceuta, Melilla, Heligoland, Büsingen, Livigno, Åland, Mount Athos, French overseas departments, Channel Islands and Northern Ireland.

- **URL**: https://apify.com/fetchgrove/eu-special-territory-classifier.md
- **Developed by:** [fetchgrove](https://apify.com/fetchgrove) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.56 / 1,000 classified places

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## EU VAT & Customs Territory Check by Postcode

Send a country and postcode (or coordinates) and find out whether the place is really inside the EU VAT area, the EU customs territory, the excise area and Schengen. Built for the places checkout and tax tools get wrong: the Canary Islands, Ceuta and Melilla, Heligoland, Büsingen, Livigno, Campione d'Italia, Åland, Mount Athos, the French overseas departments, the Channel Islands, Northern Ireland and Monaco.

Runs on bundled, dated reference data. No outside calls, nothing stored beyond your own run.

### What you get for each place

| Field | Example (Tenerife, 38001) |
|---|---|
| Territory | Canary Islands |
| In the EU | yes |
| In the EU VAT area | no |
| In the EU customs territory | yes |
| In the EU excise area | no |
| In Schengen, and since when | yes, since 1995-03-26 |
| Local indirect tax | IGIC |
| How it was decided | postcode, coordinates or country default |
| Confidence and near-border flag | high; true when a point is within 5 km of a territory boundary |
| Notes and sources | the special rule that applies, with official links and the date they were checked |
| Checkout answers | charge EU VAT on goods? customs paperwork from and to the EU? EU excise? the standard VAT or local tax rate on the date, each with a reason |

Northern Ireland has an extra field, `inEUVatAreaForGoods`, because EU VAT rules apply there to goods only.

### Use it for

- **Checkout and tax setup**: stop charging EU VAT to Canary Islands or Channel Islands customers, and catch orders that need customs paperwork.
- **Shipping and marketplaces**: flag addresses that leave the EU customs territory.
- **Data cleaning**: tag a customer list with the right tax and customs status in one run.
- **Travel tools**: know whether a destination is in Schengen on a given date.

### How to use it

Add items with `country` (ISO code) and a `postcode`, or `lat` and `lon`. Optional `asOf` date: answers use the rules in force on that date (for example before and after Croatia joined Schengen on 2023-01-01).

Or paste plain lines, one place per line: `ES 38001`, `GB BT1 1AA`, `29.035,-13.63`.

Leave the input empty to see three examples.

### Output example

Real output (shortened):

```json
{
  "country": "ES",
  "postcode": "38001",
  "territory": { "code": "ES-CN", "name": "Canary Islands" },
  "inEU": true,
  "inEUVatArea": false,
  "inEUCustomsTerritory": true,
  "inEUExciseArea": false,
  "inSchengen": true,
  "schengenSince": "1995-03-26",
  "localTax": "IGIC",
  "basis": "postcode",
  "confidence": "high"
}
```

### Pricing

| What | Price |
|---|---|
| One place classified | $0.0008 ($0.80 per 1,000) |
| Run start | $0.00005 per run (5 cents per 1,000 runs) |

Unknown countries and unreadable input are free. Free Apify plan: up to 50 places per run.

### Good to know

- Status follows the European Commission's published territorial-scope table, with each special case noted in the result.
- Coordinates near a border are marked; map outlines can be off by up to about a kilometre.
- Not legal or tax advice. Check with the tax authority or an adviser before relying on a result.

### Data sources and credits

EU and national official sources, listed with dates in each result. Map outlines: Natural Earth (public domain); © BKG (2026) dl-de/by-2-0, Datenquellen: https://sg.geodatenzentrum.de/web_public/gdz/datenquellen/datenquellen_vg_nuts.pdf (Helgoland, Büsingen); contains ISTAT data (CC BY 4.0), simplified (Livigno, Campione d'Italia).

Not affiliated with the European Commission or any national authority.

### Support

Spotted a territory we have wrong? Open an issue on this page.

# Actor input Schema

## `items` (type: `array`):

One object per address or point: {"id": optional, "country": ISO 3166-1 alpha-2 code such as ES, "postcode": as written, "lat": decimal degrees, "lon": decimal degrees}. Give a country plus a postcode, coordinates, or both; the postcode wins when both are given. Also accepted: countryCode, postalCode, zip, latitude, longitude, lng, and the codes UK, EL, XI (Northern Ireland), IC (Canary Islands) and EA (Ceuta and Melilla). If empty, the run classifies three built-in examples: Tenerife 38001, Madrid 28001 and Helgoland 27498.

## `lines` (type: `array`):

The quick way: one place per line, as text. Country and postcode (ES 38001, ES,38001, DE 27498, GB BT1 1AA; spaces inside the postcode are fine: GR 630 86, PT 9500-321), coordinates as latitude,longitude in decimal degrees with a comma or semicolon between them (29.035,-13.63; both numbers need a decimal part, so ES 35 001 is the postcode 35001), a country with coordinates (ES 29.035,-13.63) or a country code on its own. Use Bulk edit to paste many lines at once. Blank lines and lines starting with # are skipped. Each result gets the id line-<n> (its line number). A line that cannot be read, including other text after the country code such as a name or a street, gives an error row naming the line without repeating it, and nothing is charged for it. Through the API, send a list of strings; to send one multiline string, put it inside a list (\["ES 38001\nDE 27498"]): an entry holding several lines is split into lines. Classified after Addresses or points; when both are empty the built-in examples run.

## `asOf` (type: `string`):

Classify by the territorial status valid on this date (YYYY-MM-DD), for example before or after a country joined Schengen. Empty means today. Dates from 1995-01-01 are supported.

## Actor input object example

```json
{
  "items": [
    {
      "id": "tenerife",
      "country": "ES",
      "postcode": "38001"
    },
    {
      "id": "ceuta",
      "country": "ES",
      "postcode": "51001"
    },
    {
      "id": "helgoland",
      "country": "DE",
      "postcode": "27498"
    },
    {
      "id": "belfast",
      "country": "GB",
      "postcode": "BT1"
    },
    {
      "id": "lanzarote",
      "country": "ES",
      "lat": 29.035,
      "lon": -13.63
    }
  ]
}
```

# Actor output Schema

## `results` (type: `string`):

One row per input address or point: EU, VAT, customs, excise and Schengen status with notes and sources.

# 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 = {
    "items": [
        {
            "id": "tenerife",
            "country": "ES",
            "postcode": "38001"
        },
        {
            "id": "ceuta",
            "country": "ES",
            "postcode": "51001"
        },
        {
            "id": "helgoland",
            "country": "DE",
            "postcode": "27498"
        },
        {
            "id": "belfast",
            "country": "GB",
            "postcode": "BT1"
        },
        {
            "id": "lanzarote",
            "country": "ES",
            "lat": 29.035,
            "lon": -13.63
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("fetchgrove/eu-special-territory-classifier").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 = { "items": [
        {
            "id": "tenerife",
            "country": "ES",
            "postcode": "38001",
        },
        {
            "id": "ceuta",
            "country": "ES",
            "postcode": "51001",
        },
        {
            "id": "helgoland",
            "country": "DE",
            "postcode": "27498",
        },
        {
            "id": "belfast",
            "country": "GB",
            "postcode": "BT1",
        },
        {
            "id": "lanzarote",
            "country": "ES",
            "lat": 29.035,
            "lon": -13.63,
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("fetchgrove/eu-special-territory-classifier").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 '{
  "items": [
    {
      "id": "tenerife",
      "country": "ES",
      "postcode": "38001"
    },
    {
      "id": "ceuta",
      "country": "ES",
      "postcode": "51001"
    },
    {
      "id": "helgoland",
      "country": "DE",
      "postcode": "27498"
    },
    {
      "id": "belfast",
      "country": "GB",
      "postcode": "BT1"
    },
    {
      "id": "lanzarote",
      "country": "ES",
      "lat": 29.035,
      "lon": -13.63
    }
  ]
}' |
apify call fetchgrove/eu-special-territory-classifier --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fetchgrove/eu-special-territory-classifier"
        }
    }
}
```

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/syRVbMJZ13vF2Id5A/builds/vLZAf9SgTX4PbldON/openapi.json
