Home / Use cases / Go

Go

EU VAT validation in Go

Validate EU VAT numbers in Go using the standard net/http package. No external dependencies required — works with any Go 1.18+ project, from simple CLIs to high-throughput microservices.

Go's standard library net/http package is fully capable of calling the TaxID REST API without any third-party dependencies. The API returns a JSON object that maps cleanly to a Go struct, and the encoding/json package handles unmarshalling with no additional setup. This zero-dependency approach is well-suited to Go microservices where dependency footprint and binary size are concerns.

Defining a typed VATResult struct (with fields Valid bool, Status string, CompanyName string, CompanyAddress string, Cached bool, RequestID string and appropriate json tags) gives you compile-time safety and eliminates the map[string]any boilerplate. Wrap the HTTP call in a function that returns (*VATResult, error) so callers receive a nil pointer on non-200 responses and can apply standard Go error handling patterns.

For production microservices, create a shared http.Client with a custom timeout (typically 10 seconds to account for slow VIES member-state nodes) and reuse it across calls rather than using http.Get, which creates a new client with no timeout on each call. For high-throughput services, add a sync.Map or an external Redis cache keyed on country + vat number to serve repeat lookups without consuming API quota.

Implementation steps

  1. 1

    Import net/http and encoding/json from the standard library

    No go get is required — both packages ship with the Go standard library. Declare a VATResult struct with JSON tags matching the TaxID response fields: Valid (json:"valid"), Status (json:"status"), CompanyName (json:"company_name"), CompanyAddress (json:"company_address"), Cached (json:"cached"), and RequestID (json:"request_id"). This struct is your single source of truth for the API contract.

  2. 2

    Build a GET request with the Authorization header

    Construct the URL as fmt.Sprintf("https://api.taxid.pro/v1/validate/%s/%s", country, vat) and create an http.Request with http.NewRequestWithContext(ctx, http.MethodGet, url, nil). Set the Authorization header with req.Header.Set("Authorization", "Bearer "+apiKey) using an API key loaded from os.Getenv("TAXID_API_KEY"). Always pass a context so the caller can enforce timeouts and cancel in-flight requests during graceful shutdown.

  3. 3

    Decode the JSON response into map[string]any or a typed struct

    After executing the request with client.Do(req), check resp.StatusCode == http.StatusOK before decoding. Use json.NewDecoder(resp.Body).Decode(&result) where result is your VATResult pointer. Always call resp.Body.Close() in a defer statement to avoid connection leaks, which are especially impactful in high-concurrency services where leaked connections exhaust the connection pool.

  4. 4

    Handle valid, invalid, and service_unavailable cases

    Switch on result.Status after decoding: case "active" proceeds with B2B reverse-charge logic; case "invalid" returns a validation error to the caller; case "service_unavailable" returns a sentinel error (e.g. ErrVIESUnavailable = errors.New("VIES temporarily unavailable")) that the caller can identify with errors.Is and handle by retrying or falling back to standard VAT rather than silently treating the number as valid.

Code example

Go

package main

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

func validateVAT(country, vatNumber string) (map[string]any, error) {
  url := fmt.Sprintf("http://localhost:3000/api/v1/validate/%s/%s", country, vatNumber)
  req, _ := http.NewRequest("GET", url, nil)
  req.Header.Set("Authorization", "Bearer "+os.Getenv("TAXID_API_KEY"))

  resp, err := http.DefaultClient.Do(req)
  if err != nil {
    return nil, err
  }
  defer resp.Body.Close()

  var result map[string]any
  json.NewDecoder(resp.Body).Decode(&result)
  return result, nil
}

func main() {
  result, _ := validateVAT("DE", "DE123456789")

  if result["valid"] == true {
    fmt.Println("Valid EU business:", result["company_name"])
  } else if result["status"] == "service_unavailable" {
    fmt.Println("VIES unavailable — retry later")
  } else {
    fmt.Println("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:

200 OK (active)valid
{
  "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.

active

VAT number is valid and the business is registered

invalid

VAT number format is wrong or not registered in VIES

service_unavailable

VIES or the national system is temporarily down — retry later

Further reading

Evaluating EU VAT APIs? Compare TaxID with:

Ready to implement?

Get your free API key and start validating EU VAT numbers today.

Get free API key

Related use cases

Stripe EU VAT: Validate Tax IDs Before Charging Customers

Stripe EU VAT integration guide: validate EU VAT numbers server-side before applying zero-rate B2B e...

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...