ChartQuery

Validate EU VAT Numbers

Verify European VAT numbers at checkout to enable B2B tax exemptions and prevent fraud using the Veille VAT validation API.

Overview

EU businesses are exempt from VAT when purchasing from other EU businesses — but only with a valid VAT number. This playbook shows how to validate a VAT number at checkout and retrieve the associated company details (name and address) from the VIES database.

Prerequisites

  • A Veille API key — get one at app.veille.io
  • Install dependencies for your language:
pip install requests

No extra dependencies — uses the native fetch API (Node 18+).

curl extension enabled (on by default).

No extra dependencies — uses net/http (Go 1.18+).

No extra dependencies — uses java.net.http (Java 11+).

No extra dependencies — uses System.Net.Http (.NET 6+).

# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"

Steps

Validate a VAT number

Call GET /v1/vat with the vat parameter. The number should include the country prefix (e.g. FR12345678901).

import requests

API_KEY  = "YOUR_API_KEY"
response = requests.get(
    "https://api.veille.io/v1/vat",
    headers={"x-api-key": API_KEY},
    params={"vat": "FR12345678901"},
)
result = response.json()
print(result)
const API_KEY = "YOUR_API_KEY";

const params   = new URLSearchParams({ vat: "FR12345678901" });
const response = await fetch(`https://api.veille.io/v1/vat?${params}`, {
  headers: { "x-api-key": API_KEY },
});
const result = await response.json();
console.log(result);
<?php
$apiKey = "YOUR_API_KEY";
$params = http_build_query(["vat" => "FR12345678901"]);

$ch = curl_init("https://api.veille.io/v1/vat?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($result);
package main

import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)

func main() {
    req, _ := http.NewRequest("GET", "https://api.veille.io/v1/vat?vat=FR12345678901", nil)
    req.Header.Set("x-api-key", "YOUR_API_KEY")

    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)

    var result map[string]any
    json.Unmarshal(body, &result)
    fmt.Println(result)
}
import java.net.URI;
import java.net.http.*;

var client  = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.veille.io/v1/vat?vat=FR12345678901"))
    .header("x-api-key", "YOUR_API_KEY")
    .GET().build();

var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
using System.Net.Http;

using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY");

var body = await client.GetStringAsync("https://api.veille.io/v1/vat?vat=FR12345678901");
Console.WriteLine(body);
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let result = reqwest::Client::new()
        .get("https://api.veille.io/v1/vat")
        .header("x-api-key", "YOUR_API_KEY")
        .query(&[("vat", "FR12345678901")])
        .send().await?.json::<serde_json::Value>().await?;

    println!("{:#?}", result);
    Ok(())
}

Read the company details

A valid number returns is_valid: true, the company_name, company_address, and country_code.

if result.get("is_valid"):
    print(f"✅ Valid VAT number")
    print(f"   Company : {result.get('company_name')}")
    print(f"   Address : {result.get('company_address')}")
    print(f"   Country : {result.get('country_code')}")
else:
    print(f"❌ Invalid VAT number: {result.get('error', 'Not found in VIES')}")
if (result.is_valid) {
  console.log("✅ Valid VAT number");
  console.log(`   Company : ${result.company_name}`);
  console.log(`   Address : ${result.company_address}`);
  console.log(`   Country : ${result.country_code}`);
} else {
  console.log(`❌ Invalid VAT number: ${result.error ?? "Not found in VIES"}`);
}
if ($result["is_valid"] ?? false) {
    echo "✅ Valid VAT number\n";
    echo "   Company : {$result['company_name']}\n";
    echo "   Address : {$result['company_address']}\n";
    echo "   Country : {$result['country_code']}\n";
} else {
    echo "❌ Invalid VAT number: " . ($result["error"] ?? "Not found in VIES") . "\n";
}
if result["is_valid"].(bool) {
    fmt.Printf("✅ Valid VAT number\n   Company : %v\n   Address : %v\n   Country : %v\n",
        result["company_name"], result["company_address"], result["country_code"])
} else {
    errMsg := "Not found in VIES"
    if e, ok := result["error"].(string); ok { errMsg = e }
    fmt.Println("❌ Invalid VAT number:", errMsg)
}
import org.json.*;

var r = new JSONObject(response.body());
if (r.getBoolean("is_valid")) {
    System.out.printf("✅ Valid VAT number%n   Company : %s%n   Address : %s%n   Country : %s%n",
        r.getString("company_name"), r.getString("company_address"), r.getString("country_code"));
} else {
    System.out.println("❌ Invalid VAT number: " + r.optString("error", "Not found in VIES"));
}
using System.Text.Json;

var r = JsonDocument.Parse(body).RootElement;
if (r.GetProperty("is_valid").GetBoolean())
{
    Console.WriteLine("✅ Valid VAT number");
    Console.WriteLine($"   Company : {r.GetProperty("company_name")}");
    Console.WriteLine($"   Address : {r.GetProperty("company_address")}");
    Console.WriteLine($"   Country : {r.GetProperty("country_code")}");
}
else
{
    var error = r.TryGetProperty("error", out var e) ? e.GetString() : "Not found in VIES";
    Console.WriteLine($"❌ Invalid VAT number: {error}");
}
if result["is_valid"].as_bool().unwrap_or(false) {
    println!("✅ Valid VAT number");
    println!("   Company : {}", result["company_name"].as_str().unwrap_or(""));
    println!("   Address : {}", result["company_address"].as_str().unwrap_or(""));
    println!("   Country : {}", result["country_code"].as_str().unwrap_or(""));
} else {
    let err = result["error"].as_str().unwrap_or("Not found in VIES");
    println!("❌ Invalid VAT number: {}", err);
}

Build a B2B checkout VAT handler

Strip VAT from the order total when a verified EU business VAT number is provided.

import requests

API_KEY    = "YOUR_API_KEY"
VAT_RATE   = 0.20  # 20% VAT

def lookup_vat(number: str) -> dict:
    r = requests.get("https://api.veille.io/v1/vat",
        headers={"x-api-key": API_KEY}, params={"vat": number})
    return r.json()

def calculate_checkout(subtotal: float, vat_number: str | None = None) -> dict:
    vat_amount = subtotal * VAT_RATE
    vat_info   = None

    if vat_number:
        result = lookup_vat(vat_number)
        if result.get("is_valid"):
            vat_amount = 0  # B2B reverse charge — no VAT charged
            vat_info   = {"company": result["company_name"], "country": result["country_code"]}
        else:
            return {"error": "The VAT number you provided is not valid."}

    total = subtotal + vat_amount
    return {"subtotal": subtotal, "vat": vat_amount, "total": total, "company": vat_info}

print(calculate_checkout(100.00))
print(calculate_checkout(100.00, vat_number="FR12345678901"))
print(calculate_checkout(100.00, vat_number="INVALID123"))
const API_KEY  = "YOUR_API_KEY";
const VAT_RATE = 0.20;

async function lookupVat(number: string) {
  const params = new URLSearchParams({ vat: number });
  const res    = await fetch(`https://api.veille.io/v1/vat?${params}`, {
    headers: { "x-api-key": API_KEY },
  });
  return res.json();
}

async function calculateCheckout(subtotal: number, vatNumber?: string) {
  let vatAmount = subtotal * VAT_RATE;
  let companyInfo: any = null;

  if (vatNumber) {
    const result = await lookupVat(vatNumber);
    if (result.is_valid) {
      vatAmount   = 0;
      companyInfo = { company: result.company_name, country: result.country_code };
    } else {
      return { error: "The VAT number you provided is not valid." };
    }
  }

  return { subtotal, vat: vatAmount, total: subtotal + vatAmount, company: companyInfo };
}

console.log(await calculateCheckout(100));
console.log(await calculateCheckout(100, "FR12345678901"));
console.log(await calculateCheckout(100, "INVALID123"));
<?php
const VAT_RATE = 0.20;

function lookupVat(string $apiKey, string $number): array {
    $params = http_build_query(["vat" => $number]);
    $ch = curl_init("https://api.veille.io/v1/vat?{$params}");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
    $result = json_decode(curl_exec($ch), true);
    curl_close($ch);
    return $result;
}

function calculateCheckout(string $apiKey, float $subtotal, ?string $vatNumber = null): array {
    $vatAmount = $subtotal * VAT_RATE;
    $company   = null;

    if ($vatNumber) {
        $result = lookupVat($apiKey, $vatNumber);
        if ($result["is_valid"] ?? false) {
            $vatAmount = 0;
            $company   = ["company" => $result["company_name"], "country" => $result["country_code"]];
        } else {
            return ["error" => "Invalid VAT number."];
        }
    }
    return ["subtotal" => $subtotal, "vat" => $vatAmount, "total" => $subtotal + $vatAmount, "company" => $company];
}

print_r(calculateCheckout("YOUR_API_KEY", 100.0));
print_r(calculateCheckout("YOUR_API_KEY", 100.0, "FR12345678901"));
package main

import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)

const APIKey  = "YOUR_API_KEY"
const VATRate = 0.20

func lookupVAT(number string) map[string]any {
    req, _ := http.NewRequest("GET", "https://api.veille.io/v1/vat?vat="+number, nil)
    req.Header.Set("x-api-key", APIKey)
    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)
    var r map[string]any
    json.Unmarshal(body, &r)
    return r
}

func calculateCheckout(subtotal float64, vatNumber string) map[string]any {
    vatAmount := subtotal * VATRate
    var company map[string]any

    if vatNumber != "" {
        result := lookupVAT(vatNumber)
        if result["is_valid"].(bool) {
            vatAmount = 0
            company   = map[string]any{"company": result["company_name"], "country": result["country_code"]}
        } else {
            return map[string]any{"error": "Invalid VAT number."}
        }
    }
    return map[string]any{"subtotal": subtotal, "vat": vatAmount, "total": subtotal + vatAmount, "company": company}
}

func main() {
    fmt.Println(calculateCheckout(100, ""))
    fmt.Println(calculateCheckout(100, "FR12345678901"))
    fmt.Println(calculateCheckout(100, "INVALID123"))
}
import java.net.URI;
import java.net.http.*;
import org.json.*;

public class Main {
    static HttpClient client  = HttpClient.newHttpClient();
    static String     API_KEY = "YOUR_API_KEY";
    static double     VAT_RATE = 0.20;

    static JSONObject lookupVat(String number) throws Exception {
        var req  = HttpRequest.newBuilder()
            .uri(URI.create("https://api.veille.io/v1/vat?vat=" + number))
            .header("x-api-key", API_KEY).GET().build();
        var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
        return new JSONObject(resp.body());
    }

    static JSONObject calculateCheckout(double subtotal, String vatNumber) throws Exception {
        double vatAmount = subtotal * VAT_RATE;
        JSONObject company = null;

        if (vatNumber != null && !vatNumber.isEmpty()) {
            var result = lookupVat(vatNumber);
            if (result.getBoolean("is_valid")) {
                vatAmount = 0;
                company   = new JSONObject().put("company", result.getString("company_name"))
                    .put("country", result.getString("country_code"));
            } else {
                return new JSONObject().put("error", "Invalid VAT number.");
            }
        }
        return new JSONObject().put("subtotal", subtotal).put("vat", vatAmount)
            .put("total", subtotal + vatAmount).put("company", company);
    }

    public static void main(String[] args) throws Exception {
        System.out.println(calculateCheckout(100, null));
        System.out.println(calculateCheckout(100, "FR12345678901"));
        System.out.println(calculateCheckout(100, "INVALID123"));
    }
}
using System.Net.Http;
using System.Text.Json;

const double VAT_RATE = 0.20;
var apiKey = "YOUR_API_KEY";
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", apiKey);

async Task<JsonElement> LookupVat(string number)
{
    var body = await client.GetStringAsync($"https://api.veille.io/v1/vat?vat={number}");
    return JsonDocument.Parse(body).RootElement;
}

async Task<string> CalculateCheckout(double subtotal, string? vatNumber = null)
{
    double vatAmount  = subtotal * VAT_RATE;
    string? company   = null;

    if (vatNumber != null)
    {
        var result = await LookupVat(vatNumber);
        if (result.GetProperty("is_valid").GetBoolean())
        { vatAmount = 0; company = result.GetProperty("company_name").GetString(); }
        else return """{"error":"Invalid VAT number."}""";
    }
    return $"""{{ "subtotal": {subtotal}, "vat": {vatAmount}, "total": {subtotal + vatAmount}, "company": "{company}" }}""";
}

Console.WriteLine(await CalculateCheckout(100));
Console.WriteLine(await CalculateCheckout(100, "FR12345678901"));
Console.WriteLine(await CalculateCheckout(100, "INVALID123"));
use reqwest::Client;
use serde_json::{json, Value};

const VAT_RATE: f64 = 0.20;
const API_KEY: &str = "YOUR_API_KEY";

async fn lookup_vat(client: &Client, number: &str) -> Value {
    client.get("https://api.veille.io/v1/vat")
        .header("x-api-key", API_KEY)
        .query(&[("vat", number)])
        .send().await.unwrap().json::<Value>().await.unwrap()
}

async fn calculate_checkout(client: &Client, subtotal: f64, vat_number: Option<&str>) -> Value {
    let mut vat_amount = subtotal * VAT_RATE;
    let mut company    = json!(null);

    if let Some(number) = vat_number {
        let result = lookup_vat(client, number).await;
        if result["is_valid"].as_bool().unwrap_or(false) {
            vat_amount = 0.0;
            company    = json!({"company": result["company_name"], "country": result["country_code"]});
        } else {
            return json!({ "error": "Invalid VAT number." });
        }
    }
    json!({ "subtotal": subtotal, "vat": vat_amount, "total": subtotal + vat_amount, "company": company })
}

#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let client = Client::new();
    println!("{}", calculate_checkout(&client, 100.0, None).await);
    println!("{}", calculate_checkout(&client, 100.0, Some("FR12345678901")).await);
    println!("{}", calculate_checkout(&client, 100.0, Some("INVALID123")).await);
    Ok(())
}

Always store the validated company name and address alongside the order record. EU tax authorities require proof that the reverse-charge mechanism was correctly applied.

On this page