# EU VAT Number Validator (VIES) (`sfoln/vat-number-validator`) Actor

Validate EU VAT numbers live against the European Commission's VIES service: valid or invalid, the registered company name and address where the member state shares them, and an official consultation number when you add your own VAT number. $2 per 1,000 checks; outages are free.

- **URL**: https://apify.com/sfoln/vat-number-validator.md
- **Developed by:** [SFOLN LLC](https://apify.com/sfoln) (community)
- **Categories:** Automation, Developer tools, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 vat number checks

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

**EU VAT Number Validator (VIES)** checks EU VAT numbers live against the European Commission's VIES service and returns one record per number: valid or invalid, the registered company name and address where the member state shares them, and an official consultation number when you add your own VAT number. Every number is first checked against its country's format, so typos are caught without a request. Built for invoicing and checkout flows, supplier and customer onboarding, CRM clean-up and AI agents. $0.002 per checked number; member-state outages are reported as errors and never charged.

- **What you get:** `valid`, `name`, `address`, `requestDate`, `consultationNumber` (with `requesterVatNumber`), plus the normalised `vatNumber`, `countryCode`, `countryName` and `formatValid`.
- **All of VIES:** the 27 EU member states and Northern Ireland (XI); Greece uses the prefix EL (GR is accepted and converted).
- **Honest answers:** a member state that is down or busy is retried, then returned as `status: "error"` with the VIES code (for example `vies_ms_unavailable`), never as invalid.
- **Price:** $0.002 per definitive answer (`vat-check`), valid or invalid; malformed numbers and outages are free.
- **Works everywhere:** API, MCP for AI agents, Make, Zapier, n8n, schedules and webhooks.

### Input

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `vatNumbers` | string\[] | – | EU VAT numbers with their country prefix, one per line (e.g. NL004495445B01, FR27552032534). Spaces, dots and dashes are ignored; a line may hold several numbers separated by commas or semicolons. The same number written twice, in any format, is checked once. |
| `requesterVatNumber` | string | – | Your own EU VAT number. VIES then returns a consultation number for each check, a proof of when you verified the number. |
| `defaultCountry` | string | – | Prefix (e.g. DE) for numbers given without one. Greece is EL, Northern Ireland XI. |
| `maxItems` | integer | 10000 | Safety cap on VAT numbers per run (up to 100,000); further numbers are ignored and counted as `inputItemsDropped` in the `OUTPUT` record. |
| `concurrency` | integer | 2 | Checks in parallel (1–4). VIES limits concurrent requests per member state; busy answers are retried. |
| `includeRaw` | boolean | false | Attach the unmodified VIES answer under `raw`. |

```json
{
  "vatNumbers": ["NL004495445B01", "FR27552032534", "DE123456789"],
  "requesterVatNumber": "NL004495445B01"
}
```

### Output

One dataset record per VAT number:

```json
[
  {
    "input": "NL004495445B01",
    "status": "ok",
    "charged": true,
    "chargedEvent": "vat-check",
    "source": "vies",
    "fetchedAt": "2026-10-14T23:17:03.404Z",
    "vatNumber": "NL004495445B01",
    "countryCode": "NL",
    "countryName": "Netherlands",
    "number": "004495445B01",
    "formatValid": true,
    "valid": true,
    "name": "OPENJONGERENVERENIGING DE KOORNBEURS",
    "address": "VOLDERSGRACHT 00001, 2611ET DELFT",
    "requestDate": "2026-10-14T23:17:03.575Z",
    "consultationNumber": null,
    "requesterVatNumber": null
  },
  {
    "input": "DE123456789",
    "status": "ok",
    "charged": true,
    "chargedEvent": "vat-check",
    "source": "vies",
    "fetchedAt": "2026-10-14T23:17:03.249Z",
    "vatNumber": "DE123456789",
    "countryCode": "DE",
    "countryName": "Germany",
    "number": "123456789",
    "formatValid": true,
    "valid": false,
    "name": null,
    "address": null,
    "requestDate": "2026-10-14T23:17:03.411Z",
    "consultationNumber": null,
    "requesterVatNumber": null
  },
  {
    "input": "DE12345",
    "status": "skipped_invalid",
    "charged": false,
    "source": "vies",
    "fetchedAt": "2026-10-14T23:17:03.406Z",
    "vatNumber": "DE12345",
    "countryCode": "DE",
    "countryName": "Germany",
    "number": "12345",
    "formatValid": false,
    "valid": false,
    "error": { "code": "invalid_format", "message": "Germany VAT numbers look like DE123456789; \"DE12345\" does not." }
  }
]
```

| Field | Description |
| --- | --- |
| `valid` | `true` or `false` as answered by VIES; `null` when VIES gave no answer (`status: "error"`). |
| `name` / `address` | The registered trader's name and address, when the member state shares them (Germany, for example, does not). |
| `consultationNumber` | VIES request identifier, returned when `requesterVatNumber` is set; `requesterVatNumber` shows the requester used. |
| `formatValid` | Whether the number matches its country's VAT format; malformed numbers are not sent to VIES. |

`status` is one of:

- `ok`: VIES answered valid or invalid (charged);
- `error`: no definitive answer: the member state is unavailable or busy (`vies_ms_unavailable`, `vies_ms_max_concurrent_req` …), a timeout, or VIES rejected the request (not charged);
- `skipped_invalid`: the number does not match its country's format, or has no EU prefix (not charged);
- `skipped_budget`: your **Maximum cost per run** was reached (not charged);
- `skipped_free_plan`: beyond the free-plan limit below (not charged);
- `skipped_unmetered`: the run is not pay-per-event (not charged).

### Pricing

Pay-per-event. You are charged only for definitive answers:

| Event | Price | When |
| --- | --- | --- |
| `vat-check` | $0.002 | once per VAT number VIES answered as valid or invalid (`status: "ok"`) |

Checking 1,000 VAT numbers costs $2. Set **Maximum cost per run** on the run to cap spend; the Actor stops cleanly (records marked `skipped_budget`) when the cap is reached. Accounts on the Apify free plan can process up to 10 VAT numbers per run; further numbers are returned with `status: "skipped_free_plan"` and are not charged. Upgrade to any paid Apify plan for full batches.

### Use with AI agents (MCP)

Use this Actor from Claude, ChatGPT, Cursor, VS Code or any other MCP client through the [Apify MCP server](https://mcp.apify.com). Add this server URL to your client and sign in with your Apify account when asked:

```text
https://mcp.apify.com?tools=sfoln/vat-number-validator
```

To run the MCP server locally instead, start `npx -y @apify/actors-mcp-server --tools sfoln/vat-number-validator` with your `APIFY_TOKEN` in the environment.

Example prompt:

> Check whether the EU VAT numbers NL004495445B01 and FR27552032534 are valid and return the registered company names.

Runs started by an agent are billed like any other run (see Pricing). Agents can cap what a run may spend with `maxTotalChargeUsd`.

### API usage

Run the Actor and get its dataset items in one call (the call waits up to 5 minutes for the run to finish):

```bash
curl -X POST "https://api.apify.com/v2/acts/sfoln~vat-number-validator/run-sync-get-dataset-items" \
  -H "Authorization: Bearer <APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"vatNumbers": ["NL004495445B01"]}'
```

For large inputs, start the run asynchronously and download the dataset when it has finished:

```bash
## 1. Start a run: the response holds the run ID (data.id) and its dataset ID (data.defaultDatasetId)
curl -X POST "https://api.apify.com/v2/acts/sfoln~vat-number-validator/runs" \
  -H "Authorization: Bearer <APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"vatNumbers": ["NL004495445B01"]}'
## 2. Repeat until data.status is SUCCEEDED (each call waits up to 60 s)
curl "https://api.apify.com/v2/actor-runs/<runId>?waitForFinish=60" -H "Authorization: Bearer <APIFY_TOKEN>"
## 3. Download the results (format=json, csv or xlsx)
curl "https://api.apify.com/v2/datasets/<defaultDatasetId>/items?format=json" -H "Authorization: Bearer <APIFY_TOKEN>"
```

Python ([apify-client](https://pypi.org/project/apify-client/)):

```python
from apify_client import ApifyClient

client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("sfoln/vat-number-validator").call(run_input={"vatNumbers": ["NL004495445B01"]})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

Instead of polling, you can attach a webhook to the run (see Integrations). The API tab of this page shows the same calls for JavaScript and the Apify CLI.

### Integrations

- **Make, Zapier and n8n:** run `sfoln/vat-number-validator` with the Apify app (in n8n, the Apify node) and pass the dataset items to the next step of your workflow.
- **Google Sheets:** add the items to a sheet with the Google Sheets step of Make, Zapier or n8n, or download them as CSV or Excel from the run's Storage tab or the API (`format=csv` or `format=xlsx`).
- **Webhooks:** get a POST request when a run succeeds or fails: add a webhook in the Actor's Integrations tab, or pass `webhooks` when you start a run through the API.
- **Schedules:** run it hourly, daily or weekly with the same input from Apify Schedules; every run keeps its results in its own dataset.
- **More:** Slack, Google Drive, GitHub, LangChain, LlamaIndex and other [Apify integrations](https://docs.apify.com/platform/integrations).

### Related Actors

- [KYB Company Check – Registry, LEI, VAT](https://apify.com/sfoln/kyb-company-check)
- [LEI Lookup – GLEIF Legal Entity Search](https://apify.com/sfoln/lei-lookup)
- [Sanctions Screening – OFAC, EU, UK, UN Lists](https://apify.com/sfoln/sanctions-screening)
- [Website Contact Details Scraper](https://apify.com/sfoln/company-contacts-finder)
- [Domain WHOIS & DNS Lookup](https://apify.com/sfoln/domain-intelligence)

See [all our Actors](https://apify.com/sfoln) on Apify Store.

### FAQ

#### How much does it cost, and am I charged for failed items?

You pay per event at the prices under Pricing above, plus a run start fee of $0.00005 per GB of memory, with no subscription; platform usage is included. Errors and skipped items (`skipped_…` statuses) are never charged; a lookup that finds nothing is charged only where Pricing says so, and every record's `charged` field shows whether it was billed. Set **Maximum cost per run** to cap what a single run can spend.

#### How fresh is the data?

Each run queries its source while it runs, so you get what the source returns at that moment. Source-specific details, such as how often a provider refreshes its own database, are under Limits & notes.

#### What are the limits?

Per-run caps (for example the maximum number of items) are listed under Input, source limits under Limits & notes, and the free-plan limit under Pricing. For big batches, start the run asynchronously (see API usage) and read the dataset when it has finished.

#### Can I schedule runs or get results by webhook?

Yes. Create a schedule in Apify Console to run it hourly, daily or weekly with the same input, and add a webhook to get a POST request when a run finishes (see Integrations).

#### What does this Actor record about my runs?

This Actor records operational metrics about each run (item counts, duration, charges, error codes and a pseudonymous hash of your Apify user ID) to monitor reliability and costs. It never stores your input or results.

#### What happens when a member state's VIES service is down?

VIES answers MS_UNAVAILABLE (often during night-time maintenance). The Actor retries, then returns the number with status error and code vies_ms_unavailable. It is never reported as invalid and never charged; run it again later.

#### Why are name and address empty for some valid numbers?

Some member states (Germany, for example) do not share trader details through VIES; the number is still confirmed valid.

#### What is a consultation number?

When you set your own VAT number as the requester, VIES issues a consultation number (requestIdentifier) per check that proves when you verified your customer's number.

#### How do I get help?

Open an issue in the Actor's Issues tab and include the link to the run, so we can reproduce the problem. Feature requests are welcome there too.

### Limits & notes

- VIES relays each check to the member state's own VAT database. Some states are offline for maintenance at night or at weekends (VIES answers `MS_UNAVAILABLE`); those numbers come back as `error` and can be checked again later at no extra cost.
- VIES limits concurrent requests per member state, so busy states are retried with back-off and a batch for one country takes a few seconds per number at worst. Keep `concurrency` at 2 for large single-country batches.
- If VIES rejects your `requesterVatNumber`, the check is repeated without it and the record has a warning (no consultation number).
- Non-EU VAT numbers (UK GB, Norway, Switzerland) are not in VIES and are returned as `skipped_invalid`.

# Actor input Schema

## `vatNumbers` (type: `array`):

EU VAT numbers with their country prefix, one per line (e.g. NL004495445B01, FR27552032534, DE123456789); spaces, dots and dashes are ignored. Accounts on the Apify free plan can process up to 10 VAT numbers per run.

## `requesterVatNumber` (type: `string`):

Optional: your own EU VAT number. VIES then returns a consultation number for each check, which tax authorities accept as proof that you verified the number.

## `defaultCountry` (type: `string`):

Optional: the prefix (e.g. DE) for numbers given without one. Greece is EL, Northern Ireland XI.

## `maxItems` (type: `integer`):

Safety cap on VAT numbers per run; further numbers are ignored and counted in the OUTPUT record.

## `concurrency` (type: `integer`):

Checks in parallel. VIES limits concurrent requests per member state; busy answers are retried.

## `includeRaw` (type: `boolean`):

Attach the unmodified VIES answer under raw.

## Actor input object example

```json
{
  "vatNumbers": [
    "NL004495445B01"
  ],
  "maxItems": 10000,
  "concurrency": 2,
  "includeRaw": false
}
```

# Actor output Schema

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

No description

## `summary` (type: `string`):

No description

# 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 = {
    "vatNumbers": [
        "NL004495445B01"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("sfoln/vat-number-validator").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 = { "vatNumbers": ["NL004495445B01"] }

# Run the Actor and wait for it to finish
run = client.actor("sfoln/vat-number-validator").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 '{
  "vatNumbers": [
    "NL004495445B01"
  ]
}' |
apify call sfoln/vat-number-validator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,sfoln/vat-number-validator"
        }
    }
}
```

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/SV2pM1TCoDhfdqiM1/builds/oGDhmZRXzF3b5qQ9K/openapi.json
