Metadata-Version: 2.4
Name: ambolt-vies
Version: 0.1.0
Summary: EU VIES VAT number check: offline syntax validation, retry on VIES member-state errors, request queue, consultation number. Standard library only.
Author-email: Ambolt <hello@ambolt.dev>
License: MIT
Project-URL: Homepage, https://ambolt.dev
Keywords: vat,vies,eu-vat,vat-validation,vat-number,invoicing
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Intended Audience :: Developers
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# ambolt-vies

Check EU VAT numbers against the official VIES REST API. Standard library only (urllib), Python 3.9+, type hints.

```sh
pip install ambolt-vies
```

```python
from ambolt_vies import check_vat, check_vats

print(check_vat("DK10150817"))
# {'valid': True, 'countryCode': 'DK', 'vatNumber': '10150817', 'checkedAt': '...', 'source': 'EU VIES'}

proof = check_vat("DE811128135", requester="DK10150817")  # adds consultationNumber
many = check_vats(["DK10150817", "FR40303265045"], concurrency=2)
```

## What it does

- Offline syntax validation per member state (format only, no checksums; VIES decides validity). Invalid syntax returns `valid: False` with `reason: "invalid_syntax"` and makes no request.
- Normalises spaces, dots and dashes; `GR` is sent as `EL` (Greece), `XI` is Northern Ireland (goods).
- Retries with exponential backoff and full jitter on `MS_UNAVAILABLE`, `MS_MAX_CONCURRENT_REQ`, `TIMEOUT`, `SERVICE_UNAVAILABLE`, `GLOBAL_MAX_CONCURRENT_REQ`, HTTP 429/5xx and network errors (default 4 retries).
- `check_vats` limits requests in flight (default 2) and keeps input order. One failed number does not raise for the batch.
- With `requester` (your own EU VAT number) it uses the POST endpoint and returns VIES' consultation number as proof of the check.
- Privacy default: `name` and `address` are only returned with `include_details=True`. For sole traders they are personal data. Nothing is stored or logged.

Result keys: `valid, countryCode, vatNumber, consultationNumber?, checkedAt, source ("EU VIES"), name?, address?, reason?` (same as the npm package `@ambolt/vies`). A failed batch item is `{input, valid: None, error: {code, message, retryable}, checkedAt, source}`.

Keyword options: `country_code`, `requester`, `include_details`, `retries`, `base_delay_ms`, `max_delay_ms`, `timeout` (seconds, 15), `concurrency` (batch).

## Errors

`check_vat` raises `ViesError` (`code`, `retryable`, `attempts`, `status`) only when the check could not be completed.

| code | meaning | retried |
| --- | --- | --- |
| MS_UNAVAILABLE | member state's service is down | yes |
| MS_MAX_CONCURRENT_REQ | member state is at its request limit | yes |
| GLOBAL_MAX_CONCURRENT_REQ | VIES is at its request limit | yes |
| SERVICE_UNAVAILABLE | VIES unavailable | yes |
| TIMEOUT | VIES or the member state timed out | yes |
| NETWORK | your connection failed | yes |
| INVALID_REQUESTER | `requester` is not a valid VAT number | no |
| IP_BLOCKED, VAT_BLOCKED, HTTP_4xx, BAD_RESPONSE, others | VIES refused or answered unexpectedly | no |

## Why not pyvat or validate-vat

Facts as checked on 2026-10-05, from PyPI, npm and GitHub:

- `pyvat` had its last release on 2024-01-25 and open issues about `MS_UNAVAILABLE` and `MS_MAX_CONCURRENT_REQ` errors.
- The npm packages `validate-vat` (last published 2022-08-22, repository no longer resolved) and `jsvat` (deprecated as unmaintained) are the JavaScript equivalents.

This package targets the current VIES REST API, retries exactly those errors, and its tests mock HTTP only.

Respect VIES' terms of use and keep concurrency low.

Not legal or tax advice. VIES is a service of the European Commission; not affiliated.
MIT. Contact: hello@ambolt.dev. https://ambolt.dev
