SortWare API

Part of the SortWare service. One request validates a United Kingdom sort code and account number: it runs the modulus check digit algorithm of the bank, looks the sort code up in the Extended Industry Sort Code Directory, returns the branch with its BIC and address and the payment schemes the branch takes part in, and calculates the IBAN. Irish national sort codes are handled the same way. Every check comes back as its own coded result, so your code can act on the one it needs instead of parsing messages.

Endpoint

POSThttps://api.iban.com/clients/api/v4/sort/
GEThttps://api.iban.com/clients/api/v4/sort/?api_key=YOUR_API_KEY&format=json&sortcode=200415&account=38290008
  • HTTPS only. POST bodies are application/x-www-form-urlencoded; GET takes the same parameters in the query string. Both methods return the same response, and the parameters may be split between them.
  • The HTTP status is 200 for every processed request, whether the account is valid or not and also for account errors; the outcome is in the response body. The only other statuses are 429 when the rate limit is exceeded (a short HTML page) and 500 for a malformed parameter (empty body).
  • A 200 with an empty body means the request was not understood: api_key, format, sortcode or account is missing (for a search, search), or format is not exactly json or xml. Check for an empty body before parsing.
  • Responses are UTF-8. The Content-Type follows the format parameter.
  • Submitted sort codes and account numbers and the responses to them are not stored on our servers.
Your first request
  1. Copy your key from API Access.
  2. Run, with the key in place of YOUR_API_KEY:
curl "https://api.iban.com/clients/api/v4/sort/" -d api_key=YOUR_API_KEY -d format=json -d sortcode=200415 -d account=38290008

The full response to this request is in the Response section; the same request in six more languages is under Example request.

Keeping the key safe
  • Use POST in production. With GET the key is part of the URL and ends up in proxy, load-balancer and web-server access logs; with POST it travels in the request body.
  • 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.
  • Restrict the key to the IP addresses of your servers under Settings; requests from anywhere else are refused.
  • 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 parameters

ParameterRequiredValueDescription
api_keyyesstringYour personal key, shown under API Access.
formatyesjson or xmlResponse format, lower case. Missing or any other value (including JSON in upper case) gives an empty response body.
sortcodefor validationstring, 6 digitsThe sort code (United Kingdom) or national sort code (Ireland). Characters other than digits are reported by chars_sortcode and then removed; more than six characters are reported by length_sortcode and only the first six digits are used.
accountfor validationstring, 8 digitsThe account number. Characters other than digits are reported by chars_account and removed; more than eight characters are reported by length_account and only the first eight digits are used. Six- and seven-digit numbers pass the length check but are not padded, so send the full eight digits as printed on the account.
searchfor searchstring, 6 digitsLooks a sort code up in the directory without an account number. Ignored when sortcode is also sent.

Validate and search

The endpoint has two functions, chosen by the parameters you send:

  • Validate – sortcode and account. Runs all six checks, returns the branch, the payment schemes and, when the modulus check passes, the IBAN. One query.
  • Search – search only. Looks the sort code up and returns the branch and the payment schemes with the three checks that concern the sort code (chars_sortcode, length_sortcode, sort_found). No account number, no modulus check, no IBAN. One query.
GEThttps://api.iban.com/clients/api/v4/sort/?api_key=YOUR_API_KEY&format=json&search=200415

When search and sortcode are both sent, the request is treated as a validation and search is ignored; if account is then missing, the body is empty.

Example request

Every example validates the same account: sort code 200415 with account number 38290008, a Barclays test pair; its response is shown in the next section. Replace YOUR_API_KEY with the key from API Access.

curl "https://api.iban.com/clients/api/v4/sort/" \
  -d api_key=YOUR_API_KEY \
  -d format=json \
  -d sortcode=200415 \
  -d account=38290008

Response

A validation response has five objects: errors, validations, account_data, bank_data, payment_schemes (a search response has three). JSON and XML list them in a different order, and the order of the members inside validations differs too, so address everything by name. The response to the example request:

{
   "errors": [],
   "validations": {
      "chars_sortcode": {
         "code": "006",
         "message": "Sort Code does not contain illegal characters"
      },
      "chars_account": {
         "code": "007",
         "message": "Account Number does not contain illegal characters"
      },
      "length_sortcode": {
         "code": "008",
         "message": "Sort Code length is correct"
      },
      "length_account": {
         "code": "009",
         "message": "Account Number length is correct"
      },
      "modulus": {
         "code": "001",
         "message": "Modulus Success: Check digit is valid"
      },
      "sort_found": {
         "code": "002",
         "message": "Sort Code found in bank directory"
      }
   },
   "account_data": {
      "sortcode": "200415",
      "account": "38290008",
      "iban": "GB46BUKB20041538290008"
   },
   "bank_data": {
      "bic": "BUKBGB22XXX",
      "bank": "BARCLAYS BANK UK PLC",
      "branch": "BARCLAYBANK B'CARD N'PTON",
      "address": " ",
      "city": "Leicester",
      "zip": "LE87 2BB",
      "phone": "0345 7345345",
      "country": "GB"
   },
   "payment_schemes": {
      "DD": "NO",
      "FPS_PAYMENTS": "YES",
      "CHAPS": "YES",
      "BACS": "YES",
      "CCC_PAYMENTS": "YES"
   }
}
account_data
FieldDescription
sortcodeThe sort code as used by the API: digits only, at most six. A string; keep leading zeros.
accountThe account number as used: digits only, at most eight, not padded. A string.
ibanThe IBAN calculated from the sort code and account number when the modulus check passed and the sort code was found (GB… or IE…). Empty when the modulus check failed (an empty string, null for Irish sort codes); null when the sort code was not found.
bank_data

The branch that owns the sort code, from the Extended Industry Sort Code Directory. All fields are null in JSON and empty elements in XML when the sort code was not found.

FieldDescription
bicBIC of the bank and branch, 11 characters.
bankName of the bank that owns the sort code.
branchName of the branch the sort code is assigned to.
addressStreet address of the branch; may be a single space or empty.
cityCity of the branch.
zipPostal code of the branch; may be empty, as it is for most Irish branches.
phoneTelephone number of the branch; empty or null when the directory has none.
countryGB or IE.
payment_schemes

Which clearings the branch takes part in, YES or NO. In a validation response the five fields are null when the sort code was not found and for Irish sort codes, which are outside the UK schemes; in a search response they are empty strings in those cases.

FieldDescription
DDThe branch accepts Bacs Direct Debits.
FPS_PAYMENTSThe branch is reachable through the Faster Payments Service.
CHAPSThe branch accepts CHAPS sterling payments.
BACSThe branch accepts Bacs credits.
CCC_PAYMENTSThe branch takes part in cheque clearing (Cheque and Credit Clearing Company, now the Image Clearing System).
validations

Six members, one per check, each with a code and a message. Codes are three-character strings with a leading zero ("006", not 6); compare them as strings. modulus is absent in JSON (empty in XML) when the sort code was not found, because no bank algorithm can be chosen. The codes are listed under Result codes.

errors

Empty when the request was accepted. On an account error it holds one entry with code and message; in JSON the other four objects are then omitted, in XML they are present and empty.

Response to a failed modulus check

The same sort code with the last digit of the account number changed. The branch is still returned; iban is empty:

{
   "errors": [],
   "validations": {
      "chars_sortcode": {
         "code": "006",
         "message": "Sort Code does not contain illegal characters"
      },
      "chars_account": {
         "code": "007",
         "message": "Account Number does not contain illegal characters"
      },
      "length_sortcode": {
         "code": "008",
         "message": "Sort Code length is correct"
      },
      "length_account": {
         "code": "009",
         "message": "Account Number length is correct"
      },
      "modulus": {
         "code": "201",
         "message": "Modulus Failed: Check digit is not valid"
      },
      "sort_found": {
         "code": "002",
         "message": "Sort Code found in bank directory"
      }
   },
   "account_data": {
      "sortcode": "200415",
      "account": "38290009",
      "iban": ""
   },
   "bank_data": {
      "bic": "BUKBGB22XXX",
      "bank": "BARCLAYS BANK UK PLC",
      "branch": "BARCLAYBANK B'CARD N'PTON",
      "address": " ",
      "city": "Leicester",
      "zip": "LE87 2BB",
      "phone": "0345 7345345",
      "country": "GB"
   },
   "payment_schemes": {
      "DD": "NO",
      "FPS_PAYMENTS": "YES",
      "CHAPS": "YES",
      "BACS": "YES",
      "CCC_PAYMENTS": "YES"
   }
}
Response to an unknown sort code

Sort code 999999: no modulus member; iban, the bank data and the payment schemes are null, the sort code and account number are echoed:

{
   "errors": [],
   "validations": {
      "chars_sortcode": {
         "code": "006",
         "message": "Sort Code does not contain illegal characters"
      },
      "chars_account": {
         "code": "007",
         "message": "Account Number does not contain illegal characters"
      },
      "length_sortcode": {
         "code": "008",
         "message": "Sort Code length is correct"
      },
      "length_account": {
         "code": "009",
         "message": "Account Number length is correct"
      },
      "sort_found": {
         "code": "202",
         "message": "Sort Code not found in bank directory"
      }
   },
   "account_data": {
      "sortcode": "999999",
      "account": "12345678",
      "iban": null
   },
   "bank_data": {
      "bic": null,
      "bank": null,
      "branch": null,
      "address": null,
      "city": null,
      "zip": null,
      "phone": null,
      "country": null
   },
   "payment_schemes": {
      "DD": null,
      "FPS_PAYMENTS": null,
      "CHAPS": null,
      "BACS": null,
      "CCC_PAYMENTS": null
   }
}
Response to a search

Search for the same sort code. JSON carries only the three objects that apply; XML has all five with the others empty. An unknown sort code gives sort_found 202 and empty strings:

{
   "bank_data": {
      "bic": "BUKBGB22XXX",
      "bank": "BARCLAYS BANK UK PLC",
      "branch": "BARCLAYBANK B'CARD N'PTON",
      "address": " ",
      "city": "Leicester",
      "zip": "LE87 2BB",
      "phone": "0345 7345345",
      "country": "GB"
   },
   "payment_schemes": {
      "DD": "NO",
      "FPS_PAYMENTS": "YES",
      "CHAPS": "YES",
      "BACS": "YES",
      "CCC_PAYMENTS": "YES"
   },
   "validations": {
      "length_sortcode": {
         "code": "008",
         "message": "Sort Code length is correct"
      },
      "chars_sortcode": {
         "code": "006",
         "message": "Sort Code does not contain illegal characters"
      },
      "sort_found": {
         "code": "002",
         "message": "Sort Code found in bank directory"
      }
   }
}
Response for an Irish national sort code

Bank of Ireland, national sort code 905838: the IBAN is Irish, country is IE and the payment schemes are null:

{
   "errors": [],
   "validations": {
      "chars_sortcode": {
         "code": "006",
         "message": "Sort Code does not contain illegal characters"
      },
      "chars_account": {
         "code": "007",
         "message": "Account Number does not contain illegal characters"
      },
      "length_sortcode": {
         "code": "008",
         "message": "Sort Code length is correct"
      },
      "length_account": {
         "code": "009",
         "message": "Account Number length is correct"
      },
      "modulus": {
         "code": "001",
         "message": "Account number check digit is valid"
      },
      "sort_found": {
         "code": "002",
         "message": "Sort Code found in bank directory"
      }
   },
   "account_data": {
      "sortcode": "905838",
      "account": "71924374",
      "iban": "IE42BOFI90583871924374"
   },
   "bank_data": {
      "bic": "BOFIIE2DXXX",
      "bank": "Bank of Ireland",
      "branch": "Tralee",
      "address": "Lower Castle Street",
      "city": "Tralee",
      "zip": "",
      "phone": null,
      "country": "IE"
   },
   "payment_schemes": {
      "DD": null,
      "FPS_PAYMENTS": null,
      "CHAPS": null,
      "BACS": null,
      "CCC_PAYMENTS": null
   }
}
Responses to account errors

One entry in errors. 301 shown; 302 to 305 have the same shape with the messages from the code table:

{
   "errors": [
      {
         "code": "301",
         "message": "API Key is invalid"
      }
   ]
}

XML schema of the response: sortware-api-v4.xsd

Show the schema
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
  <!-- SortWare API v4 response. Every leaf element is a string and may be empty: codes keep their leading zero ("006"),
       sort codes and account numbers keep theirs too. -->
  <xs:complexType name="check">
    <xs:sequence>
      <xs:element name="code" type="xs:string"/>
      <xs:element name="message" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>
  <xs:element name="result">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="account_data">
          <xs:complexType>
            <xs:sequence minOccurs="0">
              <xs:element name="sortcode" type="xs:string"/>
              <xs:element name="account" type="xs:string"/>
              <xs:element name="iban" type="xs:string"/>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element name="bank_data">
          <xs:complexType>
            <xs:sequence minOccurs="0">
              <xs:element name="bic" type="xs:string"/>
              <xs:element name="bank" type="xs:string"/>
              <xs:element name="branch" type="xs:string"/>
              <xs:element name="address" type="xs:string"/>
              <xs:element name="city" type="xs:string"/>
              <xs:element name="zip" type="xs:string"/>
              <xs:element name="phone" type="xs:string"/>
              <xs:element name="country" type="xs:string"/>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element name="payment_schemes">
          <xs:complexType>
            <xs:sequence minOccurs="0">
              <xs:element name="DD" type="xs:string"/>
              <xs:element name="FPS_PAYMENTS" type="xs:string"/>
              <xs:element name="CHAPS" type="xs:string"/>
              <xs:element name="BACS" type="xs:string"/>
              <xs:element name="CCC_PAYMENTS" type="xs:string"/>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element name="validations">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="modulus" type="check"/>
              <xs:element name="chars_account" type="check"/>
              <xs:element name="chars_sortcode" type="check"/>
              <xs:element name="length_sortcode" type="check"/>
              <xs:element name="length_account" type="check"/>
              <xs:element name="sort_found" type="check"/>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element name="errors">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="error" type="check" minOccurs="0" maxOccurs="unbounded"/>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Result codes

Each check answers with one success code or one failure code. Success codes start with 0, failure codes with 2.

MemberSuccessFailure
chars_sortcode006 Sort Code does not contain illegal characters206 Sort Code contains illegal characters
chars_account007 Account Number does not contain illegal characters207 Account Number contains illegal characters
length_sortcode008 Sort Code length is correct208 Sort Code length is not correct
length_account009 Account Number length is correct209 Account Number length is not correct
modulus001 Modulus Success: Check digit is valid
Irish sort codes: Account number check digit is valid
201 Modulus Failed: Check digit is not valid
Irish sort codes: Account number check digit is not valid
sort_found002 Sort Code found in bank directory202 Sort Code not found in bank directory
Account errors

Returned in errors when the request itself is refused. No validation is performed.

CodeMessageWhat to do
301API Key is invalidCopy the key exactly as shown under API Access.
302Subscription expiredRenew the SortWare service under My Services.
303No queries availableYour query balance is used up; top up under Pricing.
304You have no access to this APIThe SortWare service is not active on this account.
305IP Address not allowedThe key is restricted to certain IP addresses; add the calling server's address under Settings.

Handling errors

What each outcome means for your code. Validation results are deterministic for a given input, so there is never a reason to resend one; the directory changes weekly, so if you cache responses, give the cache a limited lifetime.

SituationMeaningWhat to do
HTTP 200, empty bodyThe request was not understood: api_key, format, sortcode or account is missing (for a search, search), or format is not exactly json or xml.Fix the request. Do not retry it unchanged.
errors 301 or 304The key is wrong, or the SortWare service is not on the account.Stop sending; every request will fail the same way. Check the key under API Access, or contact support for 304.
errors 302 or 303The service has expired, or the query balance is used up.Stop sending and alert whoever handles the subscription. Poll the services endpoint to catch this before it happens.
errors 305The calling address is not on the key’s IP allow-list.Add the address under Settings, then resend.
HTTP 429, HTML bodyMore than 20 requests per second. The request was rejected by the web server before reaching the API: not processed, not charged.Wait a moment and resend; lower the sending rate. Safe to retry. There is no Retry-After header.
HTTP 500, empty bodyA parameter was sent in a form the API cannot read, such as sortcode[].Send plain string parameters.
HTTP 5xx, timeout, connection errorTemporary problem on the way to the API or on the API itself.Retry with increasing delays (1 s, 2 s, 4 s, then give up and queue). A request that got no response may still have been processed, so a retry costs at most one extra query.
A 2xx code in validationsThe request was fine; the sort code or account number is not.Do not retry: the same input gives the same result. Show the user the failed check, log the code.

Reading the result

  • A sort code and account number are valid when all six codes start with 0: modulus 001, sort_found 002, chars_sortcode 006, chars_account 007, length_sortcode 008, length_account 009. Only then is iban the account’s IBAN.
  • The character and length checks describe the input as sent; the modulus check and the lookup run on the cleaned input. A sort code sent as 20-04-15 is reported with 206 and 208 and still validated as 200415; a nine-digit account number gets 209 and is checked on its first eight digits. Treat a 2xx in any of the four as a reason to correct the input, even when the modulus passed.
  • Six- and seven-digit account numbers are not padded: 8290008 is checked as sent and fails, 38290008 passes. Some banks print seven-digit numbers; add the leading zero before calling.
  • 001 is also returned for a bank that has no published modulus rule; it means the number was not rejected, not that it was proven. sort_found 002 with the branch details is the stronger evidence.
  • Irish national sort codes are looked up in the Irish directory: country is IE, the IBAN starts with IE, the account check digit is verified, and the payment scheme flags are null because the branch is outside the UK clearings.
  • Act on codes, not messages. Messages are English text and may change; the modulus messages already differ between UK and Irish sort codes.
  • address, zip and phone can be empty, a single space or null; trim before use.

Checking your balance

Each validation or search uses one query from your SortWare balance; requests answered with an account error (301–305) do not. The services endpoint returns every service active on your account with its remaining queries and expiry date; it takes api_key and format and does not use a query. It is documented with a sample response on the Validation API page; the SortWare entry is named SortWare.

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

Rate limits and queries

  • The shared API accepts up to 20 requests per second. A short burst of a few dozen requests above that rate is absorbed; beyond it every request is rejected with HTTP 429 Too Many Requests and a small HTML body. A rejected request never reaches the API: it is not processed and 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.
  • 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
Sort codeSix-digit identifier of a bank branch in the United Kingdom, usually printed as 20-04-15. The first two digits identify the bank.
NSCNational Sort Code, the Irish equivalent (six digits, 90-58-38). The API accepts them in sortcode and returns an IE IBAN.
Modulus checkThe UK account-number check digit algorithm (modulus 10, 11 or double alternate) published by Vocalink with per-sort-code weightings and exceptions. The modulus member reports its result; a bank without a published rule reports 001.
EISCDExtended Industry Sort Code Directory, Vocalink’s weekly directory of sort codes, branches and clearing memberships. The bank data and payment scheme flags come from it; the file itself is available as the EISCD Data File.
BacsThe UK bulk clearing for Direct Debits and Direct Credits (three-day cycle). Flags BACS and DD.
FPSFaster Payments Service, the UK near-real-time clearing. Flag FPS_PAYMENTS.
CHAPSClearing House Automated Payment System, the UK same-day high-value sterling clearing. Flag CHAPS.
C&CCC / ICSCheque and Credit Clearing Company, operator of the Image Clearing System for cheques. Flag CCC_PAYMENTS.
IBANInternational Bank Account Number. For the UK: GB, two check digits, the four-letter bank code from the BIC, sort code, account number (22 characters). For Ireland: IE, check digits, bank code, NSC, account number (22 characters).