VIES VAT Validator – Bulk EU VAT Number Check
Pricing
from $3.40 / 1,000 vat number validateds
VIES VAT Validator – Bulk EU VAT Number Check
Bulk VAT number validation through VIES for invoicing software, accountants and B2B webshops. Tells valid, invalid and "member state down" apart (never a false invalid), returns the registered name and the consultation number for reverse charge, and finds VAT numbers by company name.
Pricing
from $3.40 / 1,000 vat number validateds
Rating
0.0
(0)
Developer
Leyten Data
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
10 days ago
Last modified
Categories
Share
What does VIES VAT Validator do?
Bulk VAT number validation through VIES for invoicing software, accountants and B2B webshops: per EU VAT number (btw-nummer) it tells you whether the number is valid, invalid, or could not be checked because the member state's service did not answer. It returns the registered name and address where the member state discloses them, the VIES consultation number you need as evidence for reverse charge, and it can find the VAT number of a company by name.
The problem: "invalid" and "the service was down" look the same
VIES is not one database. It relays every check to the tax administration of the member state, and those services fail independently:
- Germany takes its VIES service offline every night (measured in August 2026: roughly 21:40 to 01:00 UTC, every night).
- Ireland takes its VIES service offline on Monday night (observed 22:00 Monday to 04:00 Tuesday UTC).
- Member states answer
MS_MAX_CONCURRENT_REQwhen they are busy. - The browser endpoint of the VIES website returns
"isValid": falsetogether with an error code when the check did not happen at all.
A validator that turns any of these into "invalid" breaks the accounting integration behind it: a customer with a perfectly valid VAT number is suddenly charged VAT, blocked at checkout, or flagged in your ICP/EC Sales List review. This Actor treats that distinction as the product.
How outages are handled
Per member state, the Actor keeps a gate with its own concurrency (at most 3 checks at a time), backoff and failure clock. It only gives up on a member state after it kept failing for Wait for an unavailable member state (default 1 minute); from then on its remaining numbers in the run are reported as unavailable immediately. All waiting also ends when the run stops checking before its timeout (see Run time under Input). When VIES itself reports the member state as unavailable, it checks once a minute instead of hammering a service that is down.
| VIES error | Meaning | What the Actor does |
|---|---|---|
MS_UNAVAILABLE | The member state's service is not replying | Retried with growing waits (5 s up to 1 min, calmer while VIES reports the state as down) until the maximum wait or until the run stops checking before its timeout, then unavailable |
TIMEOUT | The member state did not reply in time | Retried with growing waits (5 s up to 1 min, calmer while VIES reports the state as down) until the maximum wait or until the run stops checking before its timeout, then unavailable |
MS_MAX_CONCURRENT_REQ | Too many requests to this member state right now | One request at a time for that member state, retried from 1 s up to 20 s, then unavailable |
MS_MAX_CONCURRENT_REQ_TIME | Too many requests to this member state in a period | One request at a time for that member state, retried from 1 s up to 20 s, then unavailable |
GLOBAL_MAX_CONCURRENT_REQ | Too many requests to VIES right now | All checks pause and retry from 2 s up to 30 s, then unavailable |
GLOBAL_MAX_CONCURRENT_REQ_TIME | Too many requests to VIES in a period | All checks pause and retry from 2 s up to 30 s, then unavailable |
SERVICE_UNAVAILABLE | VIES itself (network or application) is failing | All checks pause and retry from 5 s up to 1 min, then unavailable |
VAT_BLOCKED | VIES temporarily refuses checks of this number | unavailable for this number; retried in the next run |
IP_BLOCKED | VIES refuses requests from this IP address | All remaining numbers unavailable; retried in the next run |
INVALID_REQUESTER_INFO | Your own VAT number was rejected | The run stops before anything is charged |
INVALID_INPUT | Country code or number format rejected | invalid-format, not charged |
Unknown error codes are treated as an outage, never as an answer. A run in which VIES only answers in an unexpected way (for example after an API change) fails with a clear message instead of reporting every number as unavailable.
Next runs. In a saved task (or a run with a State key), unavailable numbers and company names are checked again in the following runs for up to Keep retrying for days (default 7) after the first failed check, and delivered as changed with carriedOver: true once VIES answers. So is a number or company name with an earlier answer that a run's timeout left unchecked (see Run time under Input), for up to Keep retrying for days after that run: it is delivered with carriedOver: true once checked, as changed only when its answer changed. A number that is still unavailable, or that the run's check limit, your spending limit, the run's memory or its timeout left unchecked, does not produce a new record and keeps waiting.
Find a VAT number by company name
VIES cannot search by name. For names in Company names ("Coolblue; NL"), the Actor finds candidates and lets VIES decide:
- Candidates come from the Peppol Directory in every country (company identifiers such as a Belgian enterprise number, French SIREN, Danish CVR or a VAT number determine the VAT number), and for France also from the French government's company register (recherche-entreprises).
- Similarity. Names are compared without legal forms, punctuation, case or accents (
Coolblue B.V.equalsCOOLBLUE BV: one record, and another of these spellings in a later run is no change), tolerating typos. The best candidates are validated in VIES; Spain also compares the name inside VIES. - Result. The best valid candidate above Minimum name similarity becomes the result, with
match.score,match.sourceand up to fivematch.alternatives. When two valid VAT numbers match equally well (Coolblue B.V.andCoolblue GmbHfor "Coolblue"),match.ambiguousistrueand the reason names both.
Coverage is honest, not complete: for France it searches the active companies in the French company register; elsewhere it covers only companies registered on Peppol. French sole traders are skipped in the name search (see the GDPR question below); validate their VAT number directly.
Features
- Bulk EU VAT number check against VIES, thousands of numbers per run, with one record per distinct number
invalidonly when VIES answered. Every error, time-out and throttle becomesunavailable, with the VIES error code, the attempts made and, for known maintenance windows, when to retry- Retries per member state. A German outage does not slow down Dutch numbers; each member state gets its own concurrency, backoff and maximum wait
- Unavailable numbers come back. In a saved task, numbers VIES could not check are checked again in the next runs until VIES answers (for up to a week by default), even if your next batch no longer contains them
- A second opinion. An "invalid" answer from a member state that failed during the same run is checked once more before it is reported
- VIES consultation number per check when you provide your own VAT number: proof for reverse charge invoices
- VAT number lookup by company name through the Peppol Directory and the French company register, confirmed in VIES
- Never charged for downtime. Only definite answers cost money
- Change detection per number across runs (
added,unchanged,changed) with a webhook, so a customer who is no longer valid shows up the day it happens - Runs through the Apify API, on a schedule, or from Make, Zapier and n8n
Use cases
ERP and invoicing software: validate customer VAT numbers in bulk
Call the Actor from your backend when customers are created or imported, or nightly for the whole customer file. Branch on status, not on valid alone, and never store a record with fromEarlierRun: true as a new check:
// npm install apify-clientimport { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });// customers: [{ id: 'C-1001', vatNumber: 'NL810433941B01' }, ...]// One run checks as many numbers as its memory holds (15,000 with the default 1 GB, 35,000 with 2 GB, 50,000 with 4 GB).// A larger file at 1 GB leaves the same customers unchecked every night, so pick the memory by the size of the file.const memory = customers.length <= 15_000 ? 1024 : customers.length <= 35_000 ? 2048 : 4096;const run = await client.actor('lwsdjfls/vies-vat-validator').call({vatNumbers: customers.map((customer) => customer.vatNumber),requesterVatNumber: 'NL123456789B01', // your own VAT number: VIES returns consultation numbersmaxWaitMinutes: 10,maxChecksPerRun: customers.length, // the default of 10,000 would stop a larger file},{ memory },);// Runs without a task or state key share one state and take turns: a run started while another is still working// waits for it, at most 10 minutes, and then checks. If that run takes longer, this one fails before it checks or// charges anything: start it again later.if (run.status !== 'SUCCEEDED') throw new Error(`VAT check run ${run.id} ended with status ${run.status}`);// A run that its check limit, your spending limit, its memory or its timeout stopped still succeeds: its RUN_SUMMARY says "partial",// and its log says what was left out and what makes room. Watch for it, or the customers left out wait every night.const summary = await client.keyValueStore(run.defaultKeyValueStoreId).getRecord('RUN_SUMMARY');if (summary?.value.outcome === 'partial') console.warn(`VAT check run ${run.id} did not check every number; see its log`);const { items } = await client.dataset(run.defaultDatasetId).listItems({ clean: true });// One record per distinct number: spellings of one number ("NL 8104.33.941.B01", "nl810433941b01") are checked// once, so look records up by the number as the Actor normalizes it.const normalize = (vatNumber) => vatNumber.replace(/[\s.\-/,_:]/g, '').toUpperCase().replace(/^GR/, 'EL').replace(/^BE(\d{9})$/, 'BE0$1');const byNumber = new Map(items.map(({ payload }) => [payload.vatNumber ?? normalize(payload.input), payload]));for (const customer of customers) {const result = byNumber.get(normalize(customer.vatNumber));// Not checked in this run (your spending limit was reached, its memory was full or its timeout came): keep the evidence you// have and check again later. A record with fromEarlierRun repeats an earlier result; it is not a new check.if (!result || result.status === 'skipped' || result.fromEarlierRun) {await scheduleRecheck(customer.id, null);continue;}switch (result.status) {case 'valid': // reverse charge allowed; keep the evidence with the customerawait saveVatCheck(customer.id, { reverseCharge: true, name: result.name, consultationNumber: result.consultationNumber, checkedAt: result.checkedAt });break;case 'invalid':case 'invalid-format': // charge VAT and ask the customer for a correct numberawait saveVatCheck(customer.id, { reverseCharge: false, reason: result.statusReason });break;case 'unavailable': // no answer: keep the last known status and check again laterawait scheduleRecheck(customer.id, result.unavailable.retryAfter);break;default: // outside-vies (GB, CH, ...): handle outside the EU flowawait flagForReview(customer.id, result.statusReason);}}
1,000 customers cost $4.00 in checks plus $0.00005 for the run's start when VIES answers all of them; numbers VIES could not check cost nothing.
Accountants: monthly check of intra-EU customers before the ICP/EC Sales List
Save a task with the customer VAT numbers of your client, schedule it monthly (or before each filing), and add your client's VAT number for consultation numbers. Each run lists every number; the changeType column shows which customers changed since last month, and a webhook can alert you when a customer is no longer valid. A record with fromEarlierRun: true was not checked again because the check limit, your spending limit or the run's timeout came first: its unchanged says nothing about this month.
E-commerce with EU B2B sales
Validate the VAT numbers entered at checkout in batches (for example every hour) instead of in the checkout itself, so a German night-time outage never blocks an order: accept the order, and invoice with reverse charge once the number is valid.
Tax teams: reverse charge validation with proof
For an intra-EU B2B sale you apply the 0% rate or reverse charge (verlegde btw, Steuerschuldnerschaft des Leistungsempfängers, autoliquidation) only for a customer with a valid VAT number:
- Goods. Since the 2020 Quick Fixes, the customer's VAT number being valid in VIES is a substantive condition for the exemption of an intra-Community supply (Article 138 of the VAT Directive), and the number must appear in your recapitulative statement.
- Services. You may treat the customer as a taxable person when it gave you its VAT number and you verified it (Article 18 of Implementing Regulation 282/2011).
Checking is half of it; being able to show you checked is the other half. Put your own VAT number in Your VAT number and VIES issues a consultation number for every check: a reference showing that you verified that customer's number on that date. The Actor returns it with the check timestamp, so you can store it with the invoice or customer record.
How to use it
- Paste your VAT numbers into VAT numbers, one per line, and press Start. A country prefix is all that is needed; spaces, dots and dashes are ignored, and a number without a prefix uses Default country. Every number comes back as its own record.
- Fill in Your VAT number if you need evidence for reverse charge. VIES then issues a consultation number for each check and it lands in the record next to the answer, which is what an auditor asks for.
- Save the run as a task once the input is right, then call that task from your invoicing system. A task keeps its own state, so later runs report only what changed, and numbers VIES could not reach are checked again for you over the following week.
Three dataset views tell you where to look. validation holds the answer per number, unavailable lists the numbers a member state never answered for or the run had no time left for, and companyMatches shows which VAT number a company name resolved to and how confident that match is. Every run ends with a status message counting all three, so a quiet day never looks like a broken integration. Read one record in the Output tab before you wire anything up.
Two things this Actor deliberately does not do. It never turns an outage into invalid, so a number you get back as invalid really was rejected by the member state. And it does not check that a name belongs to a number: VIES returns the registered name where the member state discloses it, and comparing that with your own customer record stays your decision.
curl "https://api.apify.com/v2/acts/lwsdjfls~vies-vat-validator/run-sync-get-dataset-items?token=YOUR_TOKEN" \-H 'Content-Type: application/json' \-d '{"vatNumbers":["NL810433941B01","BE0403170701"],"requesterVatNumber":"NL123456789B01"}'
Input
| Field | What it does | Example |
|---|---|---|
vatNumbers | VAT numbers, one per line, with country prefix. Spaces, dots and dashes are fine; GR is read as EL. Numbers without a prefix use the default country. | ["NL810433941B01", "BE 0403.170.701"] |
companyNames | Names as "Company name; country code". Candidates come from the Peppol Directory and, for France, the French company register; each is confirmed in VIES. | ["Coolblue; NL", "Danone; FR"] |
defaultCountry | Country for VAT numbers without prefix and names without country code. | NL |
requesterVatNumber | Your own EU VAT number. VIES then issues a consultation number per check as proof of verification. | NL123456789B01 |
maxWaitMinutes | How long a member state may keep failing before its remaining numbers are reported as unavailable; at most until the run stops checking before its timeout. Waiting is free; raise it when you run once a day and want the answer in that run. | 1 |
retryUnavailableInLaterRuns | In saved tasks: numbers VIES could not check are checked again in the following runs and delivered once VIES answers. | true |
retryForDays | How many days an unavailable number is retried after the first failed check. | 7 |
minMatchScore | How similar a registered name must be to the name you gave (legal forms, punctuation and case ignored; 100 = identical). | 80 |
maxCandidatesPerCompany | How many similar companies are validated in VIES per name. | 3 |
maxChecksPerRun | Caps the charged validations and matches in one run. Inputs above it are not checked: a number with an earlier result keeps it (with fromEarlierRun: true), others are listed as skipped. | 10000 |
maxConcurrency | Checks running at the same time (at most 3 per member state). | 6 |
useTestService | Free rehearsal against the official VIES test service (see below). | false |
webhookUrl | Receives a POST with the new and changed results after each run; stored encrypted. | https://hooks.example.com/vat-checks |
stateKey | Keeps results and retries separate for runs outside a saved task. Runs without a task or state key share one state. | customer-file-2026 |
proxyConfiguration | Not needed normally; only when VIES answers IP_BLOCKED. | { "useApifyProxy": false } |
{"vatNumbers": ["NL810433941B01", "BE 0403.170.701", "DE143454214", "FR27552032534"],"companyNames": ["Coolblue; NL", "Danone; FR"],"requesterVatNumber": "NL123456789B01","maxWaitMinutes": 1,"webhookUrl": "https://hooks.example.com/vat-checks"}
Batch size and memory. One run checks up to 15,000 numbers and company names with the default 1 GB of memory, 35,000 with 2 GB and 55,000 (the input maximum) with 4 GB; 5,000 with 512 MB. Results kept from earlier runs of the same task or state key take memory too: every 4 that are not in the run take the place of one number. What does not fit is not checked, not charged and not delivered: the run ends as partial in its run summary, the log says what was left out and what would make room for it, and numbers VIES could not check in earlier runs wait for a later run. A run in which nothing fits checks and charges for no number. When results whose number was last checked more than 35 days ago take the room, it deletes them and ends as partial without records, so that the next run has room (numbers and company names of its input among them then arrive as added again, not compared with their deleted results); otherwise it fails, and its message says what makes room and from when runs make room again on their own.
Run time. A run delivers and charges its records after its last check, one after another, and keeps the time that takes before its timeout: up to 0.1 s per record, so about 2 minutes for 1,000 numbers and 17 minutes for 10,000. Every request of its checks ends before that: near the end, each attempt of a request gets the timeout that still ends in time instead of 45 s, but never less than 10 s, and a request is retried as it is without a timeout for as long as its next attempt still gets those 10 s. So a VAT number is checked while 10 s, its turn in the pace of VIES (up to 0.1 s per parallel check) and a second are left before that point; a company name is searched while one such request still fits in each register it is searched in before the last VIES check (its turn up to 0.5 s per parallel check in the Peppol Directory, 0.2 s in the French company register). From then on, it stops waiting for member states that do not answer, and a company search stops retrying (a name whose search was not finished by then is unavailable with code SEARCH_UNAVAILABLE). What it did not check is not charged, and neither is an invalid answer that had no time left for its second check: that counts as not checked. A number or company name of the input that has an earlier answer (valid, invalid or no-match) from an earlier run with the same state shows that answer again, with fromEarlierRun: true, as unchanged; the others come back as unavailable with code RUN_TIME_LIMIT (one whose member state it stopped waiting for keeps that member state's VIES error code, and one whose shortened request ran out has code NETWORK_ERROR). The run then ends as partial in its run summary, and its log and status message say how many were left and what to do. The next runs with the same state check them first, before anything else, what waited longest first: those without an answer, by when their first check failed or was left out, then those with an earlier answer, the least recently checked first. When more waits than one run can check, a number is checked in one of the next runs rather than in the next one. A saved task or a run with a state key also checks them when they are no longer in its input (for up to Keep retrying for days after the run that left them unchecked); without a task or state key, submit them again. So a task whose input is larger than one run can check covers all of it over its next runs, also an input several times larger. Otherwise a run checks in the order of its input, then the numbers retried from earlier runs. The default run timeout is 5 hours: the time the largest input (50,000 VAT numbers and 5,000 company names with up to 10 candidates each) takes at the Actor's own pace of 10 VIES requests per second, with its delivery, plus 10%, rounded up to whole hours. You can set another timeout in the run options of a run or task (Timeout in Apify Console, timeout in the API). If the timeout is too short even to deliver a record for every number and company name, the run checks nothing and charges for no number, and the platform may stop it before it has delivered all records. For a large input, give the run a longer timeout, or split the input into smaller runs.
A state that keeps getting new numbers fills up. A result stays until a run of its task or state key starts more than 35 days after the number was last checked. A task, or the one state of all runs without a task or state key, that checks new numbers all the time (hourly checkout batches, an API call for every new customer) therefore holds about 60,000 distinct numbers per 35 days with 1 GB of memory, 20,000 with 512 MB, 140,000 with 2 GB and 270,000 with 4 GB. The fuller the state, the fewer new numbers a run checks; once it is full, runs fail, and a failed run deletes nothing, so runs keep failing until you act or until enough results are more than 35 days old (the failure message gives the date): start these runs with more memory, give them a state key that you change regularly (checkout-2026-09, then checkout-2026-10), or delete the results of the full state as the GDPR question below describes. A new state key starts over: every number arrives as added again, numbers VIES could not check are retried only under their old key, and the results of the old key stay until you delete them. Delete the records of the old key (keys starting with checkout-2026-09.) once you no longer need them.
Runs that overlap. Every task and every state key has its own state, and all runs without a task or state key share one. Two runs with the same state take turns: a run that starts while another one with its state is still working waits for it, at most 10 minutes or half of the time it has left before its own timeout, and then checks its numbers as usual. If the other run is still working after that, it fails before it checks or charges anything. Give each integration that often runs at the same time as another its own state key, so neither waits (its unavailable numbers are then retried in its own later runs).
Testing your integration. Set Use the VIES test service to rehearse every situation for free with the official VIES test numbers: 100 valid, 200 invalid, 300 SERVICE_UNAVAILABLE, 301 MS_UNAVAILABLE, 302 TIMEOUT, 400 VAT_BLOCKED, 600 MS_MAX_CONCURRENT_REQ (with a default country).
Supported member states
All 27 EU member states and Northern Ireland (XI, for trade in goods):
| Prefix | Member state | Example |
|---|---|---|
AT | Austria | ATU12345678 |
BE | Belgium | BE0403170701 |
BG | Bulgaria | BG123456789 |
CY | Cyprus | CY12345678X |
CZ | Czechia | CZ12345678 |
DE | Germany | DE123456789 |
DK | Denmark | DK12345678 |
EE | Estonia | EE123456789 |
EL | Greece | EL123456789 |
ES | Spain | ESB12345678 |
FI | Finland | FI12345678 |
FR | France | FR27552032534 |
HR | Croatia | HR12345678901 |
HU | Hungary | HU12345678 |
IE | Ireland | IE9700053D |
IT | Italy | IT12345678901 |
LT | Lithuania | LT123456789 |
LU | Luxembourg | LU12345678 |
LV | Latvia | LV12345678901 |
MT | Malta | MT12345678 |
NL | Netherlands | NL810433941B01 |
PL | Poland | PL1234567890 |
PT | Portugal | PT123456789 |
RO | Romania | RO12345678 |
SE | Sweden | SE123456789001 |
SI | Slovenia | SI12345678 |
SK | Slovakia | SK1234567890 |
XI | Northern Ireland | XI123456789 |
Formats are checked structurally (length and characters). Check digits are deliberately left to VIES: a local check-digit rule that is wrong for one class of numbers would report real customers as invalid.
Numbers from these countries are reported as outside-vies:
GB: Great Britain left VIES in 2021 (HMRC checks GB numbers; businesses in Northern Ireland trading goods use XI).CH: Switzerland is not in VIES (the Swiss UID register checks CHE numbers).NO: Norway is not in VIES (the Brønnøysund register checks Norwegian numbers).IS: Iceland is not in VIES.LI: Liechtenstein is not in VIES.
Output
One record per distinct VAT number or company name that fits in the run's memory (see Batch size and memory under Input):
status:valid,invalid,invalid-format,outside-vies,unavailable,no-match(company names) orskipped(run limit reached)valid:trueorfalseonly when there is an answer;nullwhen the number was not checked and has no earlier answerfromEarlierRun:truewhen the check limit, your spending limit or the run's timeout stopped this run's check of a number with an earlier result.status,valid,name,address,consultationNumberandcheckedAtare then that earlier result, not a new verification. A number that was onlyskippedbefore has no earlier result: the check limit or spending limit leaves itskipped, withfromEarlierRun: false, and the timeout makes itunavailable(RUN_TIME_LIMIT)vatNumber(normalized, with prefix),vatNumberWithoutPrefix,countryCode(VIES code, so Greece isEL),countryNamename,address,addressLines: as registered, where the member state discloses them;detailsWithheld: truewhen a valid number comes without them (Germany, for example)consultationNumber,requesterVatNumber,checkedAt: your evidence that the number was valid on that datestatusReason: one sentence explaining the result, for your usersunavailable:code(the VIES error;SEARCH_UNAVAILABLEwhen a company search did not answer;RUN_TIME_LIMITwhen the run had no time left for the check),viesStatus,attempts,waitedSeconds,retryAfterpendingSince,carriedOver,doubleCheckedchangeTypeper record:added(first check),unchanged(also for a record withfromEarlierRun: true, which was not checked again), orchangedwith a summary such asNL123456789B01 (ACME B.V.): no longer valid (valid on 2026-09-01)
| Status | Meaning | Charged |
|---|---|---|
valid | VIES confirms the number is valid for intra-EU transactions | Yes |
invalid | VIES answered: not valid (not registered, no longer active, or not enabled for cross-border trade) | Yes |
invalid-format | Cannot be a VAT number of that member state (wrong length or characters); VIES is not asked | No |
outside-vies | A country VIES does not cover, such as GB, CH or NO | No |
unavailable | VIES or the member state did not answer within the maximum wait, or the run had no time left for the check before its timeout (RUN_TIME_LIMIT; a number with an earlier answer keeps that answer instead, with fromEarlierRun: true). Not an answer about the number | No |
no-match | No company with a similar name and a valid VAT number was found. match.alternatives lists the similar companies without their VIES result (not-checked): VIES results come with a charged match | No |
skipped | Not checked because the run's check limit or your spending limit was reached. A number with an earlier result other than skipped keeps that result instead (unchanged, with fromEarlierRun: true and a statusReason saying it was not checked in this run) | No |
A German number checked during an outage: not checked, not charged, and checked again in the next run of the task.
{"envelopeVersion": 1,"id": "vat:DE143454214","scope": "vat:DE143454214","changeType": "added","sourceUrl": "https://ec.europa.eu/taxation_customs/vies/","scrapedAt": "2026-09-15T16:25:00.000Z","firstSeenAt": "2026-09-15T16:25:00.000Z","hash": "7257a435b5f7582eed8bafd9f6162e9acb0168d22dd42f19da6885189f491109","previousHash": null,"changes": [],"summary": "Added: DE143454214","diffText": null,"payload": {"input": "DE143454214","inputType": "vat-number","status": "unavailable","valid": null,"statusReason": "Germany's VAT service did not answer VIES (MS_UNAVAILABLE) (tried 5× over 1 min). This says nothing about the number: it was not checked. It is checked again in the next run of this task.","countryCode": "DE","countryName": "Germany","vatNumber": "DE143454214","vatNumberWithoutPrefix": "143454214","name": null,"address": null,"addressLines": [],"detailsWithheld": null,"consultationNumber": null,"requesterVatNumber": null,"checkedAt": null,"fromEarlierRun": false,"unavailable": {"code": "MS_UNAVAILABLE","reason": "Germany's VAT service did not answer VIES (MS_UNAVAILABLE) (tried 5× over 1 min). This says nothing about the number: it was not checked. It is checked again in the next run of this task.","viesStatus": "Available","attempts": 5,"waitedSeconds": 75,"retryAfter": null},"pendingSince": "2026-09-15T16:25:00.000Z","carriedOver": false,"doubleChecked": false,"match": null}}
Pricing
Pay only for answers.
| Event | Name | What it means | Price |
|---|---|---|---|
apify-actor-start | Actor start | Charged by Apify when a run starts: once for a run of up to 1 GB of memory, and once more for every extra GB. It covers the first five seconds of compute. | $0.00005 per run up to 1 GB of memory ($0.05 per 1,000) |
vat-number-validated (primary) | VAT number validated | VIES gave a definite answer for one VAT number: valid or invalid, with the consultation number when you provide your own VAT number. Never charged when VIES or the member state did not answer. | $0.004 per VAT number ($4.00 per 1,000) |
company-matched | Company matched | A company name was matched to a VAT number that VIES confirmed as valid, including the validation itself. No charge without a confirmed match. | $0.02 per matched company ($20.00 per 1,000) |
These are list prices, which the Free plan pays. Paid Apify plans pay less for every event except the Actor start: 5% on Apify's Bronze tier, 10% on Silver and 15% on Gold and higher.
Unavailable numbers, impossible formats, countries outside VIES, company names without a confirmed match (their records list the similar companies found, without VIES results) and test-service checks cost nothing. The time spent waiting for a member state is on us. Example: 1,000 VAT numbers at the default 1 GB of memory. If VIES answers all of them, the run costs 1,000 at $0.004 = $4.00, plus $0.00005 for the start: $4.00005. If 100 of them are unavailable because a member state is down, the run costs 900 at $0.004 = $3.60, plus $0.00005: $3.60005. In a saved task or a run with a state key, those 100 are checked again in later runs and charged only in the run in which VIES answers them. Each company name that is matched costs $0.02, its validation included.
Worst case with the default settings. Maximum charged checks per run (default 10,000) caps the paid validations and matches of one run together; the $0.00005 start is charged on top. A run of VAT numbers costs at most 10,000 at $0.004 = $40.00, plus $0.00005 for the start at the default 1 GB of memory: $40.00005. One input holds at most 5,000 company names: 5,000 matches at $0.02 = $100.00 and 5,000 VAT numbers at $0.004 = $20.00 cost at most $120.00, plus $0.00005: $120.00005. The ceiling is 10,000 matches at $0.02 = $200.00, plus $0.00005: $200.00005, which a saved task or a run with a state key reaches only when it also retries company names that could not be checked in earlier runs. Lower Maximum charged checks per run to set a lower ceiling.
Set your own ceiling. You can also set a maximum cost per run in the run options in Apify Console: the Actor then stops paid checks before the run would pass that amount, and the numbers and company names of the input that it did not check are listed as skipped, or keep their earlier result with fromEarlierRun: true, and cost nothing; numbers and company names retried from earlier runs keep waiting without a record.
FAQ
What exactly does invalid mean?
VIES answered that the number is not valid for intra-Community transactions. That can mean it does not exist, is no longer active, or exists nationally but is not enabled for cross-border trade. In all cases, reverse charge is not supported by that answer.
Why are name and address empty for a valid German number?
Germany does not disclose them through VIES. detailsWithheld is then true. If you give a company name, the Actor can still compare it through the name sources above.
How long does a run take?
It depends on how fast each member state answers. During an outage, numbers from the affected member state wait up to the maximum wait you set; numbers from other member states are not delayed. Delivery takes time as well, up to 0.1 s per record, and a run checks nothing it could not deliver before its timeout, which is 5 hours unless you set another: see Run time under Input.
Is bulk VAT number validation through VIES allowed, and is it GDPR-compliant?
Every check is live, for the numbers you provide, and results go only to your own Apify account. every check is live, for the numbers you provide, and results go only to your own Apify account. There is no shared cache between users.
VAT numbers of sole traders can be personal data under the GDPR, and VIES returns their registered name and address. This Actor therefore returns name and address only as VIES provides them, for the numbers you ask about; keeps the last result per number (for change detection and retries), and a mark for a number a run's timeout left unchecked, only in the key-value store vies-vat-validator-state of your own Apify account; skips French sole traders (entrepreneurs individuels, whose company name is the owner's name) and companies that opted out of publication in the name search; and reads only names, countries and identifiers from Peppol business cards, which businesses publish themselves for e-invoicing (never contacts, addresses or free text).
Results are kept per task or state key, and runs without either share one set. A result is deleted by the first run with the same task or state key that starts more than 35 days after the last run that checked the number; a run that reached the number after its check limit or your spending limit was used up counts as checking it, a run that left it out because its memory was full does not, and a run that fails deletes nothing. A run in which nothing fits in memory fails, unless deleting results that are due for deletion makes room: then it deletes them and checks nothing. Results of a task or state key that does not run again stay until you delete them: delete the store in Apify Console (Storage, Key-value stores), or only the records whose key starts with the task ID or state key and a dot (default. for runs without either), while no run with that state is working.
What are the limitations?
- The Actor paces itself per member state and reacts to throttling, but a member state that throttles for longer than the maximum wait still yields
unavailablenumbers. - Consultation numbers require your own valid EU VAT number; VIES rejects the whole request otherwise, and the run stops before charging.
- Every answer is for the moment of the check (
checkedAt). The Actor sends VIES no date, so it cannot confirm that a number was valid on an earlier invoice date. - Company name matching depends on the coverage of the Peppol Directory outside France.
- The name search reads the first 20 matches in the Peppol Directory and, for France, the first 10 in the French company register. It drops names less than 50% similar and validates the best 3 in VIES by default (Candidates checked per company, up to 10). A company whose name many others share can come back as
no-match: validate its VAT number directly. - One run checks what its memory holds (15,000 numbers and company names with the default 1 GB, 35,000 with 2 GB, 55,000 with 4 GB), fewer when its task or state key keeps many results from earlier runs, and none once a state that keeps getting new numbers is full; see Batch size and memory under Input.
- One run checks only what it can still deliver before its timeout; see Run time under Input.
- This Actor is not affiliated with the European Commission. It uses the public VIES REST API and, for company names, the public Peppol Directory and French company register.
Related Actors
- EU Company ID Resolver: turn a VAT number into KvK, KBO, SIREN and LEI, with the registered name, legal form and status from the national registers.
- Peppol Participant Lookup: can this customer receive an e-invoice? A valid VAT number says the customer is a VAT-registered business; Peppol reachability is a different question, answered from the same kind of list.
- IBAN Validator & Bank Enricher for supplier and customer bank accounts: the same kind of bulk check for IBANs, fully offline.
Support
Missing something your accounting integration needs? Open an issue on the Actor page.
Last live run against VIES: 21 September 2026, with the input the Console form opens with (one VAT number).