# EU VAT Batch Validator (`automation-lab/eu-vat-batch-validator`) Actor

Validate batches of EU and Northern Ireland VAT numbers through official VIES. Export validity, returned business name/address and check timestamps. Service outages remain errors, never invalid results.

- **URL**: https://apify.com/automation-lab/eu-vat-batch-validator.md
- **Developed by:** [Automation Lab](https://apify.com/automation-lab) (community)
- **Categories:** Business
- **Stats:** 2 total users, 1 monthly users, 66.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.68 / 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.
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 Batch Validator

Recheck supplied business VAT numbers with official **EU VAT validation** from VIES. Use this Actor for supplier onboarding, invoice checks and periodic compliance review. Each completed check returns validity, the business name and address when the member state supplies them, and dated source evidence.

A source outage is an error, **never an invalid VAT result**. This Actor does not discover VAT numbers or provide legal advice.

### Why use this Actor?

- Check an existing list instead of submitting numbers one at a time.
- Keep definitive invalid results separate from service failures.
- Export typed rows suitable for spreadsheets or an onboarding database.
- Normalize case, spacing and country codes consistently.
- Avoid duplicate charges for repeated identifiers within one run.

This is a focused batch checker. It does not provide risk scores, alerts, company search or historical validity.

### Who is it for?

Accounts-payable teams can recheck supplier identifiers before cross-border invoicing. Procurement teams can attach dated checks to onboarding records. Operations teams can schedule the same supplied list periodically and compare exports in their own systems.

A valid VAT number is not proof that an invoice is legitimate, that an entity is trustworthy, or that every reverse-charge condition is met.

### Getting started

1. Open the input form.
2. Supply VAT numbers with country prefixes.
3. Optionally reduce `maxItems` for a smaller check.
4. Run the Actor and inspect its final status.
5. Export completed rows from the default dataset as JSON, CSV or Excel.

```json
{
  "vatNumbers": ["IE6388047V", "IE0000000AA"],
  "maxItems": 10
}
```

The example includes a real valid Irish business identifier and an intentionally invalid identifier. Source answers can change over time. Member-state services operate independently: Germany can return `MS_UNAVAILABLE` while Ireland and other countries remain available. The demo does not guarantee availability for your own country; an outage still fails the run, never becomes an invalid result.

### EU VAT validation coverage

Supports the 27 EU member states plus Northern Ireland's `XI` prefix. Use `EL` for Greece; `GR` is accepted and normalized to `EL`. Great Britain's `GB` prefix is not supported by VIES.

The Actor removes whitespace, dots and hyphens, and uppercases letters. It performs basic structural checks, not a country-specific checksum guarantee. Leading zeroes are retained because inputs are strings.

Duplicate normalized numbers are checked once, in first-occurrence order. No cached result is substituted for a new source check across runs.

### Input parameters

| Field | Meaning |
|---|---|
| `vatNumbers` | Required list of 1–1000 strings, including country prefixes. |
| `maxItems` | Global unique-check limit, default 10, range 1–1000. Zero is not unlimited. |

The entire batch is structurally validated before billing, even if `maxItems` would exclude later entries. Unsupported fields fail validation rather than being silently ignored.

### Returned data

| Field | Meaning |
|---|---|
| `normalizedVatNumber` | Country prefix plus normalized local number. |
| `countryCode` | Country sent to VIES. |
| `vatNumber` | Local number without the country prefix. |
| `valid` | Boolean official result, true or false. |
| `name` | Registered business name, or null if not supplied. |
| `address` | Registered address, or null if not supplied. |
| `requestDate` | Consultation date/time returned by VIES. |
| `checkedAt` | UTC timestamp when the result was received and validated. |
| `requestIdentifier` | Source consultation reference when supplied, otherwise null. |
| `sourceUrl` | Official VIES endpoint. |

Business identity availability varies by member state. A null name/address does not mean the VAT number is invalid. VIES placeholders such as `---` become null. No requester-VAT fields are submitted, so a consultation reference is not guaranteed.

### Example output

This record reflects the actual official response shape observed for the example input; the check timestamp is illustrative.

```json
{
  "countryCode": "IE",
  "vatNumber": "6388047V",
  "normalizedVatNumber": "IE6388047V",
  "valid": true,
  "name": "GOOGLE IRELAND LIMITED",
  "address": "3RD FLOOR, GORDON HOUSE, BARROW STREET, DUBLIN 4",
  "requestDate": "2026-10-27T00:24:33.613Z",
  "checkedAt": "2026-10-27T00:24:33.675Z",
  "requestIdentifier": null,
  "sourceUrl": "https://ec.europa.eu/taxation_customs/vies/rest-api/check-vat-number"
}
```

### How much does it cost to validate EU VAT numbers?

Pay per completed unique VAT check, whether valid or invalid, plus a one-time run start charge. Failed lookups and duplicates have no item charge. A run that starts valid work but later fails may retain its start charge and charges for already completed checks.

A run starts at $0.005. Each completed check costs $0.00322 on FREE, $0.0028 on BRONZE, $0.002184 on SILVER, and $0.00168 on GOLD, PLATINUM or DIAMOND. Spend tiers follow your qualifying monthly Apify Store spend, not a private per-Actor volume schedule. Examples below use BRONZE; verify your plan in the pricing panel.

| Completed checks | BRONZE example total |
|---|---:|
| 1 | $0.0078 |
| 10 | $0.033 |
| 100 | $0.285 |

There is no separate event charge for name, address or timestamps. A maximum total charge setting may stop the run before all selected checks complete. Always compare output count with the supplied unique input count.

### Limits and service failures

Requests are sequential to avoid excessive load on member-state services. Each request has a 15-second timeout and up to three attempts for transient network, rate-limit or service errors with bounded backoff. There is no paid proxy fallback.

An exhausted service error fails the run immediately. Completed earlier rows remain available but are **partial**, not evidence that every input was checked. Never interpret absent rows as invalid results. Retry the unresolved subset later, not a successful subset unnecessarily.

The default run timeout is five minutes. Large batches can require a higher run timeout; 1000 accepted identifiers is an input limit, not a promise that every batch will finish within five minutes. Billing limits, runtime limits and source availability can all reduce delivered coverage.

### Integrations

Export CSV to a supplier-review spreadsheet. Join results to your supplier table using `normalizedVatNumber`, not row position. Store `requestDate` alongside invoice-review evidence. Schedule recurring runs with your own supplied list, then compare validity and business identity downstream.

Use Apify webhooks or Make/n8n to notify your workflow when a run completes. Check terminal status before marking the batch complete. This Actor itself does not send alerts or compare prior runs.

### API usage

Use a protected API token; never paste it into the input list.

```bash
curl -X POST 'https://api.apify.com/v2/acts/automation-lab~eu-vat-batch-validator/runs' \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"vatNumbers":["DE129274202"]}'
```

JavaScript with the official client:

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/eu-vat-batch-validator').call({
  vatNumbers: ['DE129274202']
});
if (run.status !== 'SUCCEEDED') throw new Error(`Partial or failed run: ${run.id}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems({ limit: 20 });
console.log(items);
```

Python:

```python
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/eu-vat-batch-validator').call(
    run_input={'vatNumbers': ['DE129274202']})
if run['status'] != 'SUCCEEDED':
    raise RuntimeError('Batch failed; rows may be partial')
print(client.dataset(run['defaultDatasetId']).list_items(limit=20).items)
```

### MCP setup

Select a stable Actor-specific subset rather than the full Store:

```bash
claude mcp add --transport http apify \
  'https://mcp.apify.com?tools=automation-lab/eu-vat-batch-validator'
```

For compatible Claude Desktop, Cursor, and VS Code clients:

```json
{"mcpServers":{"apify":{"url":"https://mcp.apify.com?tools=automation-lab/eu-vat-batch-validator"}}}
```

Authenticate through your client's secure setup. Discover actual names and input schemas with `tools/list`; selection also adds run/data tools, not exactly one tool. An example prompt is: “Check these supplied VAT IDs and show their returned validity and check dates.”

Start an authorized run once and retain its run/storage IDs. A tool response may contain only run metadata. If pending, check the same run with `get-actor-run` using 2/4/8-second backoff capped at 10 seconds and a 120-second total deadline. Do not restart after a client timeout; recover the known run or report uncertainty if its ID is unknown.

After success, read `get-dataset-items` in pages of 20 source rows, at most 100 rows and 64 KiB of serialized content admitted to model context. Enforce the byte budget host-side, including oversized rows; disclose any omissions. Track source offsets, report truncation and the continuation offset, and keep complete exports outside model context. If your client cannot intercept oversized responses, do not claim a hard byte guarantee. At the deadline, report pending with the run ID. On failure, report rows as partial. No universal tool-context or token savings are promised.

### Legality, responsible use and privacy

Use numbers you are authorized to process and comply with VIES terms and applicable privacy/tax obligations. Returned names and addresses can identify individuals in some jurisdictions; minimize retention and protect exports. This is not an official European Commission product or a tax opinion.

Failed operations send sanitized diagnostic input, exceptions and Actor/build/run IDs to our private GlitchTip service for repair. Secret fields and URL queries are removed; reports are retained for 30 days. Do not put secrets in VAT-number fields.

The runtime does not use AI or send VAT checks to an AI provider. Run inputs and results remain in your Apify storage until your account retention rules or deletion actions remove them; this Actor has no external business-data cache. Delete run storage in Console or via the API when no longer needed. Use the Actor Issues tab for bug reports, including your run ID and a non-sensitive example.

### FAQ and troubleshooting

**Why is the name blank for a valid number?** Some national services do not supply identity data. We preserve that absence as null.

**Why did a batch fail with some rows saved?** A later lookup exhausted retries or returned an unrecognizable response. Those rows are completed checks, not a full-batch result.

**Does this prove past validity?** No. It records a current consultation, not a historical registration certificate.

**Can I send numbers without prefixes?** No; include the country prefix and retain leading zeroes.

**Can I compare scheduled runs?** Yes, downstream. This Actor does not retain comparison state or emit change alerts.

### Related workflows

This Actor is intentionally standalone: it validates supplied identifiers rather than discovering suppliers, customs EORI numbers or procurement notices. Export the result into your existing supplier-onboarding system; no additional Actor is required.

# Changelog

This Actor's version history is a separate document: https://apify.com/automation-lab/eu-vat-batch-validator/changelog.md

# Actor input Schema

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

One to 1000 VAT identifiers including country prefixes. Case is normalized and spaces, dots and hyphens are removed. GR becomes EL. EU prefixes and XI are supported; GB is not. Duplicate normalized identifiers are checked once in first-occurrence order. Malformed entries fail the whole input before billing; format acceptance does not establish validity.

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

Global maximum unique normalized VAT numbers to check, after validating the whole batch and deduplicating. Default 10; range 1–1000. Zero does not mean unlimited. Checks stop at this limit, the billing cap, or the first exhausted upstream failure. Saved results in a failed or timed-out run are partial.

## Actor input object example

```json
{
  "vatNumbers": [
    "IE6388047V",
    "IE0000000AA"
  ],
  "maxItems": 10
}
```

# Actor output Schema

## `dataset` (type: `string`):

Completed official VIES validations in the default dataset.

# 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": [
        "IE6388047V",
        "IE0000000AA"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/eu-vat-batch-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": [
        "IE6388047V",
        "IE0000000AA",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/eu-vat-batch-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": [
    "IE6388047V",
    "IE0000000AA"
  ]
}' |
apify call automation-lab/eu-vat-batch-validator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/eu-vat-batch-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/g8cp8hK9hHUhGsYHa/builds/lAr9Y9u3dgypwVlBN/openapi.json
