EU VAT validation in .NET / C#
Validate EU VAT numbers in .NET 6+ and C# using HttpClient and System.Text.Json. Includes async/await pattern, strongly-typed response record, and IHttpClientFactory-ready setup.
.NET 6+ provides System.Net.Http.HttpClient and System.Text.Json as first-class built-ins, making the TaxID REST API call straightforward without adding NuGet packages. The recommended pattern in ASP.NET Core is IHttpClientFactory, which manages HttpClient lifetimes, connection pooling, and DNS rotation — avoiding the socket exhaustion issue that arises from instantiating HttpClient with new HttpClient() in per-request code.
Define a C# record for the response: record VATResult(bool Valid, string Status, string? CompanyName, string? CompanyAddress, bool Cached, string RequestId). Use JsonPropertyName attributes to handle the snake_case API fields. Returning a record rather than a class gives you value equality and immutability, which simplifies unit testing and caching key comparisons.
For ASP.NET Core applications, register the typed client in Program.cs: builder.Services.AddHttpClient<VATValidationService>(client => { client.BaseAddress = new Uri("https://api.taxid.pro/"); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", builder.Configuration["TaxID:ApiKey"]); }). This makes the API key injectable from appsettings.json or environment variables without the service class needing to access IConfiguration directly.
Implementation steps
- 1
Register HttpClient via IHttpClientFactory in your DI container
In Program.cs or Startup.cs, call builder.Services.AddHttpClient<VATValidationService>(client => { client.BaseAddress = new Uri("https://api.taxid.pro/"); client.Timeout = TimeSpan.FromSeconds(10); }). This registers a typed HttpClient that is injected into VATValidationService via constructor injection, follows ASP.NET Core's recommended lifetime management, and automatically rotates DNS to handle infrastructure changes at the TaxID API.
- 2
Set the Authorization header with your API key from IConfiguration
In the VATValidationService constructor, set _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", configuration["TaxID:ApiKey"]). Store the key in appsettings.json under TaxID:ApiKey and override it with an environment variable TAXID__APIKEY (double underscore for nested keys) in your deployment environment, keeping the key out of source control.
- 3
Deserialize the response with System.Text.Json into a C# record type
Call var response = await _httpClient.GetAsync($"v1/validate/{country}/{vat}", cancellationToken) and check response.EnsureSuccessStatusCode(). Deserialize with var result = await response.Content.ReadFromJsonAsync<VATResult>(cancellationToken: cancellationToken). Configure JsonSerializerOptions with PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower (available in .NET 8) or use [JsonPropertyName("company_name")] attributes on the record properties to map snake_case API fields to PascalCase C# names.
- 4
Return the VATResult to your controller or application service
Return the VATResult record from ValidateAsync and let the caller switch on result.Status: "active" triggers Stripe tax_exempt: 'reverse' or equivalent billing logic; "invalid" returns a ValidationProblem response from your API controller; "service_unavailable" throws a VIESUnavailableException that your global exception handler maps to HTTP 503, signalling to the client that it should retry rather than treating the number as definitively invalid.
Code example
C# / .NET
using System.Net.Http.Headers;
using System.Text.Json;
using System.Text.Json.Serialization;
public record VATResult(
[property: JsonPropertyName("valid")] bool Valid,
[property: JsonPropertyName("status")] string Status,
[property: JsonPropertyName("company_name")] string? CompanyName,
[property: JsonPropertyName("company_address")] string? CompanyAddress
);
// In your service — register HttpClient via IHttpClientFactory in prod
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer",
Environment.GetEnvironmentVariable("TAXID_API_KEY"));
var url = $"http://localhost:3000/api/v1/validate/DE/DE123456789";
var json = await http.GetStringAsync(url);
var result = JsonSerializer.Deserialize<VATResult>(json)!;
if (result.Valid)
Console.WriteLine($"Valid EU business: {result.CompanyName}");
else if (result.Status == "service_unavailable")
Console.WriteLine("VIES unavailable — retry later");
else
Console.WriteLine("Invalid VAT number");cURL
curl "http://localhost:3000/api/v1/validate/DE/DE123456789" \
-H "Authorization: Bearer $TAXID_API_KEY"
# {
# "valid": true,
# "status": "active",
# "company_name": "Example GmbH",
# "company_address": "Musterstraße 1, 10115 Berlin",
# "cached": false
# }API response
The TaxID API returns a consistent JSON response for every validation request:
{
"valid": true,
"status": "active",
"country_code": "DE",
"vat_number": "123456789",
"company_name": "Example GmbH",
"company_address": "Musterstraße 1, 10115 Berlin",
"request_date": "2026-05-10T00:00:00.000Z",
"cached": false,
"request_id": "req_01j..."
}Error handling
The API uses a consistent Stripe-style error format. Always handle service_unavailable separately — VIES has occasional downtime and you should not reject valid customers during outages.
activeVAT number is valid and the business is registered
invalidVAT number format is wrong or not registered in VIES
service_unavailableVIES or the national system is temporarily down — retry later
Further reading
EU VAT Number Validation: The Complete Developer Guide (2026)
VIES is SOAP-based, unreliable, and has no caching. This guide explains how EU VAT validation works end-to-end, how to h…
VAT for Developers: The Complete 2026 Implementation Guide
VAT is a consumption tax collected at each stage of the supply chain. For developers, this means implementing rate looku…
Evaluating EU VAT APIs? Compare TaxID vs Vatstack, Vatlayer, Avalara →
Related use cases
Validate EU VAT numbers in Stripe Checkout
Add EU VAT validation to your Stripe checkout flow. Verify customer VAT numbers server-side before a...
UK VAT validation in Shopify B2B
Validate UK VAT numbers for B2B customers in Shopify. Required under UK Making Tax Digital rules for...
WooCommerce Spain NIF/CIF validation
Validate Spanish NIF and CIF numbers in WooCommerce checkout. Automatically apply B2B tax exemptions...