DEV Community

Mohamed Zaghloul
Mohamed Zaghloul Subscriber

Posted on

Validate an IBAN and VAT number before paying an invoice

A mistyped IBAN or a made-up VAT number on a supplier invoice is cheap to catch before payment and expensive to fix after. Here's a small pre-payment check you can add to any accounts-payable flow with two HTTP calls.

I'll use the Invoice & Payment ID Validator API on RapidAPI (I built it; there's a free plan with 100 requests a month): https://rapidapi.com/MohamedBahaa/api/invoice-payment-id-validator

1. Check the IBAN

curl --request GET \
  --url 'https://invoice-payment-id-validator.p.rapidapi.com/v1/iban?value=DE89370400440532013000' \
  --header 'x-rapidapi-host: invoice-payment-id-validator.p.rapidapi.com' \
  --header 'x-rapidapi-key: YOUR_RAPIDAPI_KEY'
Enter fullscreen mode Exit fullscreen mode

Real response:

{
  "input": "DE89370400440532013000",
  "iban": "DE89370400440532013000",
  "country": "DE",
  "check_digits": "89",
  "bban": "370400440532013000",
  "valid": true,
  "country_name": "Germany",
  "bank_code": "37040044",
  "branch_code": null,
  "account_number": "0532013000",
  "formatted": "DE89 3704 0044 0532 0130 00",
  "sepa": true,
  "errors": []
}
Enter fullscreen mode Exit fullscreen mode

What it checks: the country's IBAN length, the ISO 13616 mod-97 checksum, and SEPA membership. It also splits out the bank code, branch code, and account number for 20 common countries. Change one digit and you get "valid": false with "errors": ["bad_checksum"].

What it doesn't check: whether the account is open or who owns it. A valid checksum means the IBAN is well-formed, not that it belongs to your supplier. For that you still need the supplier's confirmation, or your bank's payee-verification service where one exists.

2. Check the EU VAT number, live against VIES

curl --request GET \
  --url 'https://invoice-payment-id-validator.p.rapidapi.com/v1/vat/eu?value=IE6388047V&live=true' \
  --header 'x-rapidapi-host: invoice-payment-id-validator.p.rapidapi.com' \
  --header 'x-rapidapi-key: YOUR_RAPIDAPI_KEY'
Enter fullscreen mode Exit fullscreen mode

Real response (request_date trimmed):

{
  "input": "IE6388047V",
  "valid": true,
  "country": "IE",
  "number": "6388047V",
  "vat": "IE6388047V",
  "format_valid": true,
  "checksum": "not_checked",
  "errors": [],
  "vies": {
    "checked": true,
    "registered": true,
    "name": "GOOGLE IRELAND LIMITED",
    "address": "3RD FLOOR, GORDON HOUSE, BARROW STREET, DUBLIN 4",
    "error": null
  }
}
Enter fullscreen mode Exit fullscreen mode

With live=true, the API asks the EU's VIES service whether the number is registered. When the member state shares it, you also get the registered name and address, which you can compare with the invoice. Without live=true, it only checks the format (fast, offline). VIES is sometimes down for individual countries. When it is, vies.error tells you, so treat that as "retry later", not "invalid".

UK VAT (/v1/vat/gb), US EIN (/v1/ein) and BIC/SWIFT (/v1/bic) work the same way with ?value=.

3. Check a whole invoice in one call

curl --request POST \
  --url 'https://invoice-payment-id-validator.p.rapidapi.com/v1/batch' \
  --header 'content-type: application/json' \
  --header 'x-rapidapi-host: invoice-payment-id-validator.p.rapidapi.com' \
  --header 'x-rapidapi-key: YOUR_RAPIDAPI_KEY' \
  --data '{"items":[{"type":"iban","value":"DE89370400440532013000"},{"type":"eu_vat","value":"IE6388047V","live":true}]}'
Enter fullscreen mode Exit fullscreen mode

Up to 50 IDs per call (up to 10 of them live VIES checks).

4. A simple pay / hold rule

  • Hold if iban.valid is false
  • Hold if vies.registered is false
  • Flag for review if the VIES name doesn't roughly match the supplier name on the invoice
  • Retry later if vies.error is set
  • Otherwise, pass it to approval

5. Do it in n8n

If your invoices arrive by email, my free n8n template turns vendor invoice PDFs in Gmail into clean rows in Google Sheets: https://mohabaha0.gumroad.com/l/ap-invoice-triage-free

To add the checks above, put an HTTP Request node after the extraction step: use POST /v1/batch with the JSON body from step 3 and your two x-rapidapi-* headers. Then add an IF node with the pay / hold rule.

If you use AI agents, the API is also an MCP server at /mcp, so Claude or Cursor can call the validators as tools.

Questions or edge cases (odd country formats, VIES quirks)? Drop them in the comments.

Top comments (0)