# EU VAT Validator (VIES) - Bulk Check & Company Data (`clarno/eu-vat-validator-vies`) Actor

Validate EU VAT numbers in bulk against the official VIES service. Get validity and company details when supplied by VIES. Pay $5 per 1,000 unique completed checks; duplicates, invalid formats and unavailable responses are not charged. Platform usage included.

- **URL**: https://apify.com/clarno/eu-vat-validator-vies.md
- **Developed by:** [Andrea Bonati](https://apify.com/clarno) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$5.00 / 1,000 vat number checkeds

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) — Bulk Check & Company Data

Validate **EU VAT numbers in bulk** against **VIES**, the official VAT Information Exchange System of the European Commission. Paste a list, upload a CSV or pass an array: you get back validity, the **registered company name and address**, and the **official consultation number** when supplied by VIES, which you can retain as a reference for the check.

No API key, no account with a third-party data vendor. Data comes straight from the official source.

### What you get for every row

| Field | Description |
|---|---|
| `countryCode` / `vatNumber` | Normalised, country prefix separated |
| `status` | `VALID`, `INVALID`, `INVALID_FORMAT`, `UNVERIFIED`, `REJECTED`, `NOT_PROCESSED` |
| `valid` | `true`, `false` or `null` when the registry could not answer |
| `name` | Registered company name as returned by the national registry |
| `address` | Registered address (also split into `addressLines`) |
| `consultationNumber` | Official VIES request identifier (see below) |
| `reference` | Your own record ID, copied through so you can join results back |
| `errorCode` / `errorMessage` | Why a row could not be confirmed |
| `checkedAt` | Timestamp of the check |
| `charged` | Whether this row generated one confirmed paid event; always false for duplicates and non-monetized runs |

### Why this Actor instead of a raw VIES call

**It does not charge you for numbers that cannot possibly be valid.** Every input is first checked offline against the official national syntax rules — and, for Italian numbers, against the check digit. A malformed number is rejected locally, instantly, for free.

**It deduplicates.** The same VAT number repeated 40 times in your export is checked once and charged once.

**It survives VIES.** The service is public and has no SLA: national registries go down, refuse concurrent requests, and time out. Every temporary failure is retried with exponential backoff, and permanent errors are reported with a clear reason instead of a stack trace. A row that could not be confirmed is marked `UNVERIFIED`, never silently reported as invalid.

**It gives you the consultation number.** VIES issues an official request identifier only when the requester identifies itself. Fill in `requesterMemberStateCode` and `requesterNumber` with your own VAT details and every result carries the identifier you need to keep on file.

### Input

Three interchangeable ways to pass data:

```json
{
  "inputText": "IT00743110157\nDE811569869\nNL810462783B01"
}
```

```json
{
  "vatNumbers": [
    { "countryCode": "IT", "vatNumber": "00743110157", "reference": "customer-42" },
    "DE811569869"
  ]
}
```

```json
{
  "inputCsvUrl": "https://example.com/customers.csv",
  "csvColumn": "vat_number"
}
```

Numbers may be written as `IT01234567890`, `it-0123 4567 890` or without a prefix if you set `defaultCountryCode`. `GR` is accepted and mapped to `EL`.

### Coverage

All 27 EU member states, plus `XI` (Northern Ireland) and `EU` (OSS numbers of non-EU providers). `GB` is **not** covered: VIES stopped validating United Kingdom numbers on 1 January 2021.

### Pricing

Price: **$5.00 per 1,000 completed unique VAT checks ($0.005 each), with platform usage included**. The current price is also displayed in the Apify Store pricing tab.

The custom event `vat-number-checked` is charged **once per unique VAT number, only after VIES returns VALID or INVALID**. Locally rejected rows, duplicate rows, timeouts, service errors, rejected requests and malformed responses do not trigger this event. Internal retries do not generate extra events.

Only this custom event may have a positive price: disable paid Actor-start and default-dataset-item events in Apify. Keep platform usage included when configuring the intended all-inclusive price. The Actor validates the event configuration before processing. Configuration in this repository does not activate monetization or set prices in Apify.

The budget is checked before querying VIES and again before charging. In paid mode requests are sequential, even when a higher concurrency is requested, so concurrent calls cannot spend the same final budget slot. Unstarted rows are marked `NOT_PROCESSED`; the last successfully charged result is kept. `SUMMARY.uniqueChecked` counts unique VIES checks, and `SUMMARY.chargedCount` counts confirmed paid events.

An uncertain billing error fails the run without automatically retrying the charge. Automatic replay of an already-started paid run (including migration/resurrection) is blocked to prevent billing its input again. Inspect saved results and the Apify billing log before manually starting another paid run. Charges and dataset writes are separate platform operations: after an interrupted write, reconcile the run before retrying.

### Billing verification

Run `npm test` to test validation plus billing with simulated VIES responses and the real Apify SDK in local pay-per-event test mode. These tests do not charge real money. Production billing still needs a controlled verification after monetization is explicitly configured.

### Good to know

A `VALID` answer confirms that the number exists and is active in the national registry at the moment of the check. It is one element of evidence, not a complete substitute for the checks required by your own tax obligations. Company name and address are only returned when the member state chooses to disclose them; several countries return them empty by design.

This Actor calls the official public VIES REST API. It does not scrape any website and stores no data beyond your own run.

# Actor input Schema

## `inputText` (type: `string`):

Paste VAT numbers, one per line or comma separated. Include the country prefix, e.g. IT01234567890, DE123456789, NL123456789B01.

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

Alternative to the text field. Accepts plain strings or objects: { "countryCode": "IT", "vatNumber": "00743110157", "reference": "customer-42" }. The reference is copied to the output so you can join the results back to your records.

## `inputCsvUrl` (type: `string`):

Public URL of a CSV file. The column containing VAT numbers is auto-detected when its header contains 'vat', 'iva' or 'piva'.

## `csvColumn` (type: `string`):

Name of the column holding the VAT numbers. Leave empty for auto-detection.

## `defaultCountryCode` (type: `string`):

Used when a number has no country prefix. Example: IT.

## `requesterMemberStateCode` (type: `string`):

VIES only issues an official consultation number when the requester identifies itself. Fill this together with your own VAT number to receive it.

## `requesterNumber` (type: `string`):

Your own VAT number, without the country prefix.

## `skipInvalidFormat` (type: `boolean`):

Reject malformed numbers locally. If disabled, supported-country numbers are sent to VIES; only definitive VALID or INVALID responses can trigger a paid event.

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

Maximum parallel requests for non-monetized runs. Paid runs use one request at a time to enforce the spending limit safely.

## `maxRetries` (type: `integer`):

National registries are frequently unavailable for short periods. Retries use exponential backoff.

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

Safety cap. 0 means no limit.

## Actor input object example

```json
{
  "inputText": "IT00743110157\nDE811569869\nNL810462783B01",
  "vatNumbers": [],
  "skipInvalidFormat": true,
  "concurrency": 3,
  "maxRetries": 3,
  "maxItems": 0
}
```

# Actor output Schema

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

VAT validity, company details when available, errors and billing status for each input row.

# 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 = {
    "inputText": `IT00743110157
DE811569869
NL810462783B01`
};

// Run the Actor and wait for it to finish
const run = await client.actor("clarno/eu-vat-validator-vies").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 = { "inputText": """IT00743110157
DE811569869
NL810462783B01""" }

# Run the Actor and wait for it to finish
run = client.actor("clarno/eu-vat-validator-vies").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 '{
  "inputText": "IT00743110157\\nDE811569869\\nNL810462783B01"
}' |
apify call clarno/eu-vat-validator-vies --silent --output-dataset

```

## MCP server setup

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

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/dyojeNaSFP77x6pE3/builds/SewIqLWQmk9aOHLrZ/openapi.json
