Skip to main content

Batch Validation

Validate up to 50 VAT numbers in one request. Available on Pro and Business tiers only. Each VAT number is validated independently against VIES (EU) or HMRC (UK). Results are returned in the same order as the input array.
POST /v1/validate/batch

Request body

FieldTypeRequiredDescription
vat_numbersstring[]YesArray of VAT numbers to validate (1-50 items)
requester_vat_numberstringNoYour own VAT number, to receive consultation numbers
cachebooleanNoSet to false to bypass the 24-hour cache. Defaults to true

Response shape

The response always returns HTTP 200 with a data object containing results and summary. Per-item errors appear inline in the results array rather than causing the entire request to fail.

Success result

{
  "data": {
    "valid": true,
    "vat_number": "NL123456789B01",
    "country_code": "NL",
    "company": {
      "name": "Acme B.V.",
      "address": "Keizersgracht 123, Amsterdam"
    },
    "requested_at": "2026-03-18T12:00:00Z"
  },
  "meta": {
    "cached": true,
    "cached_at": "2026-03-18T10:00:00Z"
  }
}
The data object is the same VatValidationData shape as the single validate endpoint. Per-item meta contains cache information for that specific result.

Error result

When an individual VAT number fails (e.g. upstream timeout, invalid format), its result contains an error object instead of data. The vat_number that failed is in meta.
{
  "error": {
    "code": "upstream_unavailable",
    "message": "HMRC service unavailable"
  },
  "meta": {
    "vat_number": "XX000000000"
  }
}

Summary

Every batch response includes a summary object:
{
  "total": 3,
  "succeeded": 2,
  "failed": 1
}

Full example response

{
  "data": {
    "results": [
      {
        "data": {
          "valid": true,
          "vat_number": "NL123456789B01",
          "country_code": "NL",
          "company": {
            "name": "Acme B.V.",
            "address": "Keizersgracht 123, Amsterdam"
          },
          "requested_at": "2026-03-18T12:00:00Z"
        },
        "meta": {
          "cached": true,
          "cached_at": "2026-03-18T10:00:00Z"
        }
      },
      {
        "data": {
          "valid": true,
          "vat_number": "DE987654321",
          "country_code": "DE",
          "company": {
            "name": "Beispiel GmbH",
            "address": "Berliner Str. 42, Berlin"
          },
          "requested_at": "2026-03-18T12:00:01Z"
        },
        "meta": {}
      },
      {
        "error": {
          "code": "upstream_unavailable",
          "message": "VIES service unavailable"
        },
        "meta": {
          "vat_number": "FR12345678901"
        }
      }
    ],
    "summary": {
      "total": 3,
      "succeeded": 2,
      "failed": 1
    }
  },
  "meta": {
    "request_id": "550e8400-e29b-41d4-a716-446655440000",
    "request_duration_ms": 850
  }
}

Deduplication

Duplicate VAT numbers in the input array are deduplicated internally. Only unique numbers are validated against the upstream service and counted toward your monthly quota. The response still returns one result per input item in the original order — duplicates receive the same result. For example, sending ["NL123456789B01", "NL123456789B01", "DE987654321"] validates 2 unique numbers and returns 3 results.

Examples

curl -X POST https://api.vatly.dev/v1/validate/batch \
  -H "Authorization: Bearer vtly_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "vat_numbers": ["NL123456789B01", "DE987654321", "GB123456789"],
    "requester_vat_number": "FR99999999999"
  }'

Error handling

Batch validation has two levels of errors:

HTTP-level errors

These prevent the entire batch from executing. The response shape is the standard { error, meta } envelope.
HTTP StatusError CodeCause
401unauthorizedInvalid or missing API key
403tier_insufficientFree tier — upgrade to Pro or Business
422validation_errorInvalid request body (e.g. empty array, >50 items)
429rate_limit_exceededMonthly quota insufficient for this batch

Inline errors

When the batch executes but individual VAT numbers fail, those failures appear as inline errors in the results array. The HTTP status is still 200 and the summary.failed count tells you how many items had errors. Common inline error codes: invalid_vat_format, upstream_unavailable.
Always check both levels: first result.error for HTTP-level failures, then iterate result.data.results for inline per-item errors.