> ## Documentation Index
> Fetch the complete documentation index at: https://docs.avatcado.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Batch Validation

> Validate up to 50 VAT numbers in a single request

# 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 the appropriate upstream service (VIES, HMRC, BFS, Bronnysund, or ABR). Results are returned in the same order as the input array.

```
POST /v1/validate/batch
```

## Request body

| Field                  | Type       | Required | Description                                            |
| ---------------------- | ---------- | -------- | ------------------------------------------------------ |
| `vat_numbers`          | `string[]` | Yes      | Array of VAT numbers to validate (1-50 items)          |
| `requester_vat_number` | `string`   | No       | Your own VAT number, to receive consultation numbers   |
| `cache`                | `boolean`  | No       | Set to `false` to bypass the 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

```json theme={null}
{
  "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](/response-format). 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`.

```json theme={null}
{
  "error": {
    "code": "upstream_unavailable",
    "message": "HMRC service unavailable"
  },
  "meta": {
    "vat_number": "XX000000000"
  }
}
```

### Summary

Every batch response includes a `summary` object:

```json theme={null}
{
  "total": 3,
  "succeeded": 2,
  "failed": 1
}
```

## Full example response

```json theme={null}
{
  "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

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.avatcado.com/v1/validate/batch \
    -H "Authorization: Bearer avat_live_your_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "vat_numbers": ["NL123456789B01", "DE987654321", "GB123456789"],
      "requester_vat_number": "FR99999999999"
    }'
  ```

  ```typescript @avatcado/node theme={null}
  import Avatcado, { isBatchSuccess } from "@avatcado/node";

  const avatcado = new Avatcado("avat_live_your_api_key");
  const { data, error } = await avatcado.vat.validateBatch({
    vatNumbers: ["NL123456789B01", "DE987654321", "GB123456789"],
    requesterVatNumber: "FR99999999999",
  });

  if (error) {
    // HTTP-level error (401, 403, 422, 429)
    console.error(`${error.code}: ${error.message}`);
  } else {
    // Check each result for inline errors
    for (const item of data.data.results) {
      if (isBatchSuccess(item)) {
        console.log(`${item.data.vatNumber}: valid=${item.data.valid}`);
      } else {
        console.error(`${item.meta.vatNumber}: ${item.error.code}`);
      }
    }
    console.log(`Summary: ${data.data.summary.succeeded}/${data.data.summary.total} succeeded`);
  }
  ```

  ```python Python theme={null}
  from avatcado import Avatcado, is_batch_success

  avatcado = Avatcado("avat_live_your_api_key")
  result = avatcado.vat.validate_batch(["NL123456789B01", "DE987654321", "GB123456789"])

  # Check each result for inline errors
  for item in result.data.results:
      if is_batch_success(item):
          print(f"{item.data.vat_number}: valid={item.data.valid}")
      else:
          print(f"{item.meta.vat_number}: {item.error.code}")
  print(f"Summary: {result.data.summary.succeeded}/{result.data.summary.total} succeeded")
  ```
</CodeGroup>

## 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 Status | Error Code                                       | Cause                                              |
| ----------- | ------------------------------------------------ | -------------------------------------------------- |
| 401         | `unauthorized`                                   | Invalid or missing API key                         |
| 403         | [`tier_insufficient`](/errors/tier_insufficient) | Free tier -- upgrade to Pro or Business            |
| 422         | `validation_error`                               | Invalid request body (e.g. empty array, >50 items) |
| 429         | `rate_limit_exceeded`                            | Monthly 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`.

<Note>
  Always check both levels: first `error` for HTTP-level failures, then iterate `data.data.results` for inline per-item errors. With the Node.js SDK, use `isBatchSuccess()` to type-narrow each result. With the Python SDK, use `is_batch_success()`.
</Note>
