BAV (VoP) API

Part of the Bank Account Verification service. One request asks the bank that holds an IBAN whether the name you send is the name on that account. The bank answers through the Verification of Payee scheme, and the API returns its verdict, yes, partial, no or unavailable, together with the bank’s BIC. Use it before you pay a new beneficiary, to catch typing errors and misdirected payments while they can still be corrected. Accounts in 21 countries can be verified.

Endpoint

POSThttps://api.iban.com/clients/api/verify/v3/
  • HTTPS, POST only; GET, HEAD and OPTIONS are refused with 405 (so there is no CORS preflight: call the API from your server, not from a browser). The key goes in the x-api-key header, the input in a JSON body with Content-Type: application/json. There is no GET form and no form-encoded form.
  • The HTTP status tells you whether a verification was carried out. 200: the request reached the bank and the outcome, including partial match, no match and unavailable, is in the body. 400, 401, 405, 415, 500: the request was refused, the body is just {"error":"CODE"}; no bank was contacted, except for INTERNAL_SERVER_ERROR:UNERSP, where the request to the bank network failed. 429: rejected by the rate limit (see Rate limits).
  • Responses are UTF-8 JSON, Content-Type: application/json; charset=UTF-8 (the 405 and 415 refusals send application/json without the charset).
  • A verification can take a while: the answer comes from the bank, and some banks are slow. Allow at least three minutes before giving up on a request (see Response times).
  • One query from your BAV balance is used once the request has passed the account checks and the IBAN’s bank has been identified: every 200 response, the check-digit refusal INVALID_IBAN_CHECKSUM and INTERNAL_SERVER_ERROR:UNERSP use one; everything refused before that point (the other 400s, 401, 405, 415, 429, INTERNAL_SERVER_ERROR:BNFDBS) uses none.
  • The service may be used only with payment intent: verify a payee because you are about to pay them, not to screen lists of accounts. See the terms of service.
  • Submitted IBANs and names and the answers to them are kept only in our technical logs, for troubleshooting.
Your first request

Start with the sandbox: same API, fixed answers, nothing charged.

curl "https://sandbox.iban.com/clients/api/verify/v3/" -H "x-api-key: testkey" -H "Content-Type: application/json" -d '{"IBAN":"FR7630006000011234567890189","name":"John Doe"}'

The answer:

{
   "query": {
      "IBAN": "FR7630006000011234567890189",
      "name": "John Doe",
      "success": true
   },
   "result": {
      "valid": true,
      "name_match": "yes",
      "bic": "AGRIFRPPXXX"
   },
   "error": ""
}

Then switch the host to api.iban.com and the key to yours. Each verification there is real, answered by the account’s bank, and uses one query. Requests in seven languages are under Example request.

Keeping the key safe
  • Send the key only in the x-api-key header, never in the URL: headers are not written to proxy, load-balancer and web-server access logs, query strings are.
  • Call the API from your server. A key embedded in browser JavaScript or a mobile app can be read from the source or the network traffic, and every verification made with it is charged to you.
  • Restrict the key to the IP addresses of your servers under Settings; requests from anywhere else are refused with IP_NOT_ALLOWED.
  • Keep the key out of source control; load it from an environment variable or a configuration file. A key cannot be regenerated from the client area: if it is exposed, contact support to have it replaced.

Request

Headers
HeaderRequiredValueDescription
x-api-keyyesstringYour personal key, shown under API Access. Missing: MISSING_API_KEY (400); wrong: INVALID_API_KEY (401). The header name is matched case-insensitively (X-API-KEY works). The key is checked before the body: a missing or wrong key is reported even when the body is malformed.
Content-Typeyesapplication/jsonThe only accepted media type, matched case-insensitively; a charset parameter is allowed (application/json; charset=utf-8, which some HTTP libraries add on their own). Any other type such as text/json, and a missing header, gives UNSUPPORTED_FORMAT (415).
Body

A JSON object with two fields. Other fields are ignored.

FieldRequiredValueDescription
IBANyesstringThe IBAN of the account to verify, in one of the supported countries, as a JSON string. Spaces and any other characters that are not letters or digits are removed, and letters are converted to upper case, before the checks, so DE63 3702 0500 0005 0233 07 is accepted and echoed back as DE63370205000005023307. The country code is checked first (COUNTRY_NOT_SUPPORTED, 400), then the check digits (INVALID_IBAN_CHECKSUM, 400, also for a value that is too short); both are refused before any bank is contacted. Missing or empty: MISSING_INPUT_IBAN (400). The field name is case-sensitive: iban is reported as missing.
nameyesstringThe name of the account holder as it should appear on the account: first and last name for a personal account, the legal name for a company (a trade name usually gives partial or no). UTF-8; accents and other non-ASCII characters are passed through unchanged. Missing or empty: MISSING_INPUT_NAME (400). Both fields are checked for presence before the IBAN is examined.

Supported countries

IBANs of these 21 countries can be verified. Any other country code is refused with COUNTRY_NOT_SUPPORTED before the bank is contacted, so it costs nothing to try.

AT  AustriaBE  BelgiumHR  Croatia
CY  CyprusEE  EstoniaFI  Finland
FR  FranceDE  GermanyGR  Greece
IE  IrelandIT  ItalyLV  Latvia
LT  LithuaniaLU  LuxembourgMT  Malta
NL  NetherlandsPT  PortugalSK  Slovakia
SI  SloveniaES  SpainGB  United Kingdom

Example request

Every example verifies the same account: the published donation account of the German Red Cross, DE63370205000005023307, held by Deutsches Rotes Kreuz e.V. Its response is shown in the next section. Replace YOUR_API_KEY with the key from API Access. Each run is a real verification and uses one query; to try the code for free, point it at the sandbox.

curl "https://api.iban.com/clients/api/verify/v3/" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"IBAN":"DE63370205000005023307","name":"Deutsches Rotes Kreuz e.V"}'

Response

A completed verification returns three members: query (what you sent), result (the bank’s answer) and error (the status code of the verification, empty for a match). The response to the example request, a match:

{
   "query": {
      "IBAN": "DE63370205000005023307",
      "name": "Deutsches Rotes Kreuz e.V",
      "success": true
   },
   "result": {
      "valid": true,
      "name_match": "yes",
      "bic": "BFSWDE33XXX"
   },
   "error": ""
}
FieldTypeDescription
query.IBANstringThe IBAN as checked: separators removed, upper case.
query.namestringThe name as you sent it, unchanged.
query.successbooleantrue when the verification was carried out. It is true for every 200 response, including partial match, no match and unavailable; a refused request has no query object at all (see Response codes).
result.validboolean or nullWhether the IBAN and the name are a valid pair: true for a match, a partial match and a verdict-less answer; false when the bank rejected the pair (the name does not match, NAME_NOT_MATCH); null when no verdict could be obtained (VERIFICATION_NOT_SUPPORTED, VOP_REQUEST_FAILED). It is a statement about the pair, not about the account alone: the same existing account answers true with the right name and false with a wrong one.
result.name_matchstringOutcome of the name check, one of four lower-case values: yes (match), partial (close match), no (no match), unavailable (no verdict: the bank could not check the name, or answered without a name verdict). See Reading the result.
result.bicstringBIC of the bank that issued the IBAN, 11 characters. Head office (branch code XXX) or a branch BIC, depending on the country and on what the bank directory holds for the IBAN; the recorded responses all returned the head-office form.
errorstringStatus code of the verification: empty for a match and for unavailable, NAME_PARTIAL_MATCH for a partial match, NAME_NOT_MATCH for no match, VERIFICATION_NOT_SUPPORTED or VOP_REQUEST_FAILED when the bank network could not answer (then valid is null). Not an indication that the request failed: query.success is true alongside it.
Partial match

The Italian Red Cross, sent as Associazione della Croce Rossa Italiana – ODV, the form published on its website; the bank holds the name slightly differently. error carries NAME_PARTIAL_MATCH while query.success stays true:

{
   "query": {
      "IBAN": "IT12T0623003204000030737371",
      "name": "Associazione della Croce Rossa Italiana – ODV",
      "success": true
   },
   "result": {
      "valid": true,
      "name_match": "partial",
      "bic": "CRPPIT2PXXX"
   },
   "error": "NAME_PARTIAL_MATCH"
}
No match

The German Red Cross account again, with the name John Doe. name_match is no, error is NAME_NOT_MATCH and valid is false: the account exists, as the first example shows, but the IBAN and this name are not a valid pair:

{
   "query": {
      "IBAN": "DE63370205000005023307",
      "name": "John Doe",
      "success": true
   },
   "result": {
      "valid": false,
      "name_match": "no",
      "bic": "BFSWDE33XXX"
   },
   "error": "NAME_NOT_MATCH"
}
Name could not be checked

A French foundation’s account: the name could not be checked. name_match is unavailable, valid is true and error is empty:

{
   "query": {
      "IBAN": "FR7630027174110002636660114",
      "name": "Fondation pour le Logement des Défavorisés",
      "success": true
   },
   "result": {
      "valid": true,
      "name_match": "unavailable",
      "bic": "CMCIFRPPXXX"
   },
   "error": ""
}
Refused request

A refused request has neither query nor result, only the code. The IBAN of the example with its last digit changed:

{"error":"INVALID_IBAN_CHECKSUM"}
Recorded verifications

The responses on this page were recorded on 8 October 2026 against published donation accounts of charities, with the account holder name exactly as each organisation publishes it, and once with a wrong name:

CountryIBANname sentvalidname_matcherrorbicTime
DEDE63370205000005023307Deutsches Rotes Kreuz e.Vtrueyes""BFSWDE33XXX0.3 s
NLNL13INGB0000004054Artsen zonder Grenzentrueyes""INGBNL2AXXX0.4 s
GBGB74BARC20792533979512St George Foundation Limitedtrueyes""BARCGB22XXX3.0 s
ITIT12T0623003204000030737371Associazione della Croce Rossa Italiana – ODVtruepartialNAME_PARTIAL_MATCHCRPPIT2PXXX0.7 s
FRFR7630027174110002636660114Fondation pour le Logement des Défavoriséstrueunavailable""CMCIFRPPXXX0.8 s
DEDE63370205000005023307John DoefalsenoNAME_NOT_MATCHBFSWDE33XXX0.4 s

The API does not return the name the bank holds, only the verdict.

Response codes

Every code is an upper-case string in the error field. The HTTP status separates the two kinds: a refused request (nothing was sent to a bank; nothing charged except INVALID_IBAN_CHECKSUM) and a completed verification with a status.

Refused requests
HTTPCodeMeaningWhat to do
400MISSING_API_KEYNo x-api-key header.Add the header.
401INVALID_API_KEYThe key is not known.Copy it exactly as shown under API Access.
401IP_NOT_ALLOWEDThe key is restricted to certain IP addresses and the request came from another one.Add the calling server’s address under Settings.
401USER_NOT_ALLOWEDThe BAV service is not active on this account.Order the service, or contact support if you have.
401SUBSCRIPTION_EXPIREDThe BAV service has expired.Renew it under My Services.
401NO_CREDITS_AVAILABLEThe query balance is used up.Top up under Pricing.
405METHOD_NOT_ALLOWEDThe request was not a POST.Use POST; there is no GET form of this API.
415UNSUPPORTED_FORMATThe Content-Type is not application/json, or the header is missing.Send the header and a JSON body; form-encoded bodies are not read.
400INVALID_JSONThe body could not be parsed as JSON. Also returned for an empty body.Build the body with your language’s JSON encoder.
400MISSING_INPUT_IBANNo IBAN field, or it is empty. Also returned when the body is not a JSON object (an array, for instance).Send the IBAN; the field name is case-sensitive.
400MISSING_INPUT_NAMENo name field, or it is empty.Send the account holder’s name.
400COUNTRY_NOT_SUPPORTEDThe first two characters of the IBAN are not a country on the supported list. Checked before the check digits, so a value that is not an IBAN at all gets this code.The account cannot be verified through this service.
400INVALID_IBAN_CHECKSUMThe country is supported but the IBAN fails the ISO 13616 check-digit test (or is too short): a typo or a made-up number. This validation uses one query.Correct the IBAN. Do not resend it unchanged. Checking the IBAN check digits on your side first saves the query.
429–More than 20 requests per second (see Rate limits).Wait and resend at a lower rate.
500INTERNAL_SERVER_ERROR:BNFDBSThe bank code in the IBAN is not in our bank directory, so no BIC could be determined for the verification. Not charged.Do not resend unchanged; check the IBAN. Contact support with the IBAN if it is a real account.
500INTERNAL_SERVER_ERROR:UNERSPThe verification request to the bank network failed before an answer came back. Uses one query.Retry later; contact support if it persists.
500INTERNAL_SERVER_ERROR:…Any other unexpected error on our side; the part after the colon is an internal tag. Match on the prefix INTERNAL_SERVER_ERROR.Retry later; contact support if it persists.
Completed verifications

HTTP 200, with query and result present and query.success true. One query is used. The first four rows were recorded; the last three follow the API source and were not observed during testing.

HTTPerrorname_matchvalidMeaning
200""yestrueThe name matches the name on the account.
200NAME_PARTIAL_MATCHpartialtrueThe name is close to, but not exactly, the name on the account.
200NAME_NOT_MATCHnofalseThe bank holds a different name for this account; the IBAN and the name sent are not a valid pair.
200""unavailabletrueThe bank network answered with a notice or warning that carries no name verdict. The name is unverified.
200""nofalseThe bank network rejected the IBAN and name pair for a reason other than the name.
200VERIFICATION_NOT_SUPPORTEDunavailablenullThe bank answered, but this account or bank does not take part in the verification of payee scheme (not applicable in the scheme’s terms).
200VOP_REQUEST_FAILEDunavailablenullThe verification request failed in the bank network: the bank did not answer.

Handling errors

What each outcome means for your code. A verification is answered by a bank, so the same input can give a different answer at a different time (a bank that was unreachable comes back, an account is renamed); the input errors, on the other hand, are deterministic.

SituationMeaningWhat to do
400 or 415The request is malformed, or the IBAN is wrong or from an unsupported country. Nothing was sent to a bank (a check-digit failure still uses one query).Fix the request or the input; do not resend it unchanged. Show the user the IBAN error.
401The key, the IP address or the subscription is the problem.Stop sending; every request will fail the same way until it is fixed. Check the key under API Access, the balance and expiry under My Services.
405The request was not a POST.Use POST.
429More than 20 requests per second. The request was rejected before reaching the API: not processed, not charged.Wait a moment and resend; lower the sending rate. Safe to retry.
500, timeout, connection errorTemporary problem on the way to the API or on the API itself.Retry with increasing delays (5 s, 30 s, 2 min, then give up and queue). A request that got no response may still have been completed and charged.
200 with unavailableThe verification could not be completed: the bank is unreachable, not in the scheme, or the account is of a kind that cannot be checked.Do not retry in a loop; every completed request is charged. Treat the name as unverified, and either retry later or ask the user to confirm the payee another way.
200 with partial or noThe verification completed; the name does not (fully) match.Do not retry with the same name: the answer is deterministic. Ask the user to check the name, then verify again before paying.

Reading the result

Decide on result.name_match and error, never on the HTTP status alone: a 200 response can say that the name does not match.

  • Match (yes): the name you sent is the name on the account. The payment can go ahead.
  • Close match (partial, NAME_PARTIAL_MATCH): the bank judged the names to differ only slightly: a middle name, an initial, an abbreviation, a legal-form suffix such as ODV, e.V. or Ltd. The scheme lets the bank disclose the name it holds in this case, but this version of the API does not return it; ask the payer to confirm the name before paying.
  • No match (no, NAME_NOT_MATCH): the account exists but is held under a different name. Common causes: a trade name instead of the legal company name, a spelling that differs between the local language and an English rendering, a wrong IBAN that happens to be valid. Do not pay without checking with the payee.
  • Unavailable (unavailable): no verdict. With VERIFICATION_NOT_SUPPORTED or VOP_REQUEST_FAILED (valid null) the name could not be checked; with an empty error (valid true) the bank network answered without a name verdict. Causes: the bank is not reachable or does not take part in the scheme; the bank’s system timed out, is overloaded or in maintenance; a problem in the scheme’s infrastructure; the account is closed, blocked or not found; the account is of a kind that is outside the scheme (savings, loan, card, investment and escrow accounts). The name is unverified, not wrong.
  • result.valid says whether the IBAN and the name are a valid pair: false means the bank holds a different name for the account. It does not say that the account exists or is open; a pair that could not be checked is null, a verdict-less answer true, so read it together with name_match and treat only yes as verified.
  • Send the name the account is held under. For a company that is the legal name from the register, including the legal form; for a person, first and last name as registered with their bank. Titles, “c/o” lines and department names lower the chance of a match.
  • Verify immediately before the payment, once. The answer is the bank’s view at that moment, and each completed verification uses one query.

Sandbox

A copy of the API with fixed answers for development and automated tests. It contacts no bank, needs no subscription and uses no queries. Same request format, same JSON bodies, same error codes.

POSThttps://sandbox.iban.com/clients/api/verify/v3/

Use the key testkey. The answer depends on the IBAN, the name and the key:

IBANnamex-api-keyHTTPResponse
FR7630006000011234567890189John Doetestkey200name_match yes, bic AGRIFRPPXXX
DE89370400440532013000John Doetestkey200name_match yes, bic COBADEFFXXX
DE89370400440532013000Johntestkey200name_match partial
NL91ABNA0417164300anytestkey200error VERIFICATION_NOT_SUPPORTED
BR9700360305000010009795493P1anytestkey400COUNTRY_NOT_SUPPORTED
FR7630006000011234567890183anytestkey400INVALID_IBAN_CHECKSUM
anyanynomorecredits401NO_CREDITS_AVAILABLE
anyanyserviceexpired401SUBSCRIPTION_EXPIRED
anyanyusernotallowed401USER_NOT_ALLOWED
anyanyipnotallowed401IP_NOT_ALLOWED
any other IBANanytestkey400SANDBOX_NOT_SUPPORTED_IBAN

IBANs are matched exactly after spaces are removed (the fixture in lower case is “any other IBAN”); SANDBOX_NOT_SUPPORTED_IBAN exists only on the sandbox.

The request-format errors (MISSING_API_KEY, INVALID_API_KEY, UNSUPPORTED_FORMAT, INVALID_JSON, MISSING_INPUT_IBAN, MISSING_INPUT_NAME, METHOD_NOT_ALLOWED) behave exactly as in production. The sandbox has no fixture for no or unavailable; test those paths with a mocked response.

Response times

  • A refused request is answered in about 0.1 s.
  • A completed verification takes as long as the bank takes. The recorded verifications took between 0.3 s (Germany) and 3.0 s (United Kingdom); banks answer quickly as a rule, UK banks are the slowest, and the scheme allows a bank up to three minutes. Set the client timeout to at least 180 s, and do not hold a web request open for it: verify in the background and tell the user when the answer is in.
  • A request that timed out on your side may still have been completed by the bank and charged.

Checking your balance

The services endpoint returns every service on your account with its remaining queries and expiry date, the same information as My Services. Poll it from your monitoring to catch a low balance before the API starts answering 401 NO_CREDITS_AVAILABLE. It takes your api_key and a format (json or xml) and does not use a query.

GEThttps://api.iban.com/clients/api/v4/services/?api_key=YOUR_API_KEY&format=json
GEThttps://api.iban.com/clients/api/v4/services/?api_key=YOUR_API_KEY&format=xml

The response, shortened to the entry of this service (the endpoint lists every service on your account, one object each):

{
   "services": [
      {
         "service_name": "BAV (2,500 queries)",
         "credits": 52,
         "expiry_date": "2027-05-15",
         "service_expired": 0
      }
   ],
   "errors": []
}

The HTTP status is 200 in every case. With a wrong key, services is empty and errors carries code 301 ({"services":[],"errors":[{"code":"301","message":"API Key is invalid"}]}); without format the body is empty.

FieldTypeDescription
service_namestringName of the service as shown under My Services. The BAV entry carries the plan size in brackets: BAV (2,500 queries). Match on the prefix BAV.
creditsnumber or stringRemaining queries. Services without a quota return the string Unlimited instead of a number, so test the type before comparing.
expiry_datestringExpiry date of the service, YYYY-MM-DD. Requests after this date are refused as expired.
service_expired0 or 11 when the service has expired.

Rate limits and queries

  • The shared API accepts up to 20 requests per second. Above that, requests are rejected with HTTP 429 Too Many Requests before they reach the API: not processed, not charged. Acceptance resumes as soon as your rate drops below the limit, so wait a moment and resend; keep the sending rate at or below 20 per second with a queue or a token bucket. If several processes send requests, apply the limit to their total.
  • Each completed verification (HTTP 200) uses one query, whatever the outcome, and so do INVALID_IBAN_CHECKSUM and INTERNAL_SERVER_ERROR:UNERSP; requests refused before the bank is identified use none. Your balance and expiry date are shown under My Services and by the services endpoint.
  • Need more throughput? A dedicated API server is available for your account with no per-second limit, on its own hostname. Contact support with your expected volume.

Glossary

TermMeaning
Verification of Payee (VoP)The scheme of the European Payments Council under which a payer’s bank asks the payee’s bank, before a SEPA credit transfer, whether the name given by the payer matches the name on the account. Mandatory for banks in the euro area since October 2025. This API gives you the same check through your own integration.
PayeeThe account holder who is to receive a payment; the person or company whose IBAN and name you send.
PSPPayment service provider: a bank or other licensed institution that holds accounts. The responding PSP is the one that holds the IBAN and answers the verification.
Match / close match / no matchThe three possible answers of the scheme when the check could be carried out; returned as yes, partial and no. A close match means the responding bank judged the names to differ only slightly (a middle name, an abbreviation, a legal-form suffix).
IBANInternational Bank Account Number (ISO 13616): country code, two check digits and the national account identifier. The check digits are verified before the request is sent to the bank.
BICBusiness Identifier Code (ISO 9362), the 8- or 11-character code of a bank or branch. The API returns the issuing bank’s BIC in the 11-character form, head office or branch.
QueryOne unit of your BAV balance, used by each completed verification.