ibanchecker

package module
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 27, 2026 License: MIT Imports: 10 Imported by: 0

README

ibanchecker-go

Official Go client for the ibanchecker.cash IBAN validation API.

Validate IBANs across 92 countries, validate up to 100 IBANs per request, extract IBANs from free text, look up country format specifications, and resolve SWIFT/BIC codes. No IBAN data is stored or logged; all validation runs in memory at the edge.

Go Reference

Install

go get github.com/koraykoylu/ibanchecker-go

Requires Go 1.21 or newer. There are no dependencies: the client is built on net/http and encoding/json from the standard library.

Quick start

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	ibanchecker "github.com/koraykoylu/ibanchecker-go"
)

func main() {
	// Every method except CountryFormat needs an API key.
	client := ibanchecker.New(os.Getenv("IBANCHECKER_API_KEY"))

	result, err := client.Validate(context.Background(), "DE89 3704 0044 0532 0130 00")
	if err != nil {
		log.Fatal(err)
	}

	if result.Valid {
		fmt.Println(result.CountryName) // Germany
		fmt.Println(result.BankName)    // Commerzbank AG Cologne
		fmt.Println(result.BIC)         // COBADEFFXXX
	} else {
		fmt.Println(result.Error)     // human-readable reason
		fmt.Println(result.ErrorCode) // e.g. INVALID_COUNTRY
	}
}

Every method takes a context.Context as its first argument, so a caller's deadline and cancellation reach the request.

Authentication

Every method except CountryFormat needs an API key, LookupBIC included. Without one the API answers HTTP 401 and the client returns ErrAuthentication. A free key covers 100 requests a month and arrives by email in seconds: request it at ibanchecker.cash/api-docs. Paid plans are at ibanchecker.cash/pricing.

What a key can call follows its plan:

  • A free key covers Validate only.
  • ValidateBulk and LookupBIC need the Basic plan or above (Basic, Starter, Growth, Enterprise).
  • Extract needs the Growth plan or above (Growth, Enterprise).

A key whose email address has a verified account at ibanchecker.cash/dashboard can try the methods its plan lacks: ValidateBulk with up to 10 IBANs per call, LookupBIC, and Extract with up to 5,000 characters per call. The trial applies to any plan that lacks the method, so a Basic key with a verified account can try Extract. A trial call over that size gets HTTP 400 with the code TOO_MANY_IBANS (bulk) or TEXT_TOO_LONG (extraction), returned as ErrBadRequest.

CountryFormat works without a key, limited to 100 requests an hour per IP; beyond that the API answers HTTP 429 with the code RATE_LIMIT_EXCEEDED. The hourly limit applies to country formats only.

client := ibanchecker.New("YOUR_API_KEY")
client := ibanchecker.New(os.Getenv("IBANCHECKER_API_KEY"))

formats := ibanchecker.New("") // CountryFormat only

The key is sent as Authorization: Bearer <key>. Requests made with it count against its monthly quota: Validate and LookupBIC count one request per call, ValidateBulk one per IBAN in the call, and Extract one per IBAN found (at least one per call). Once the quota is used up the API answers HTTP 429 with the code QUOTA_EXCEEDED until the 1st of the next month (UTC), and a call that costs more than the requests left this month gets the same answer.

A method outside the key's plan

Outside the trial, a call the key's plan does not include gets HTTP 403 with the code PLAN_REQUIRED. The client has no sentinel of its own for 403, so the error unwraps to ErrAPI; tell it apart by Status or Code, and read required_plan ("basic" or "growth") and upgrade_url from Response:

var apiErr *ibanchecker.Error
if errors.As(err, &apiErr) && apiErr.Code == "PLAN_REQUIRED" {
	fmt.Println("Needs the", apiErr.Response["required_plan"], "plan:", apiErr.Response["upgrade_url"])
}

Methods

Method API key Description
Validate(ctx, iban) required, any plan Validate a single IBAN. Returns a *ValidationResult.
ValidateBulk(ctx, ibans) required, Basic or above Validate up to 100 IBANs (10 on the trial). Returns a *BatchResult.
Extract(ctx, text) required, Growth or above Find and validate IBANs in free text (up to 50,000 chars; 5,000 on the trial). Returns a *BatchResult.
CountryFormat(ctx, country) optional IBAN format spec for an ISO country code. Returns a *FormatSpec.
LookupBIC(ctx, bic) required, Basic or above Resolve an 8 or 11 character BIC. Returns a *BankRecord.
Bulk validation
batch, err := client.ValidateBulk(ctx, []string{
	"DE89370400440532013000",
	"GB29NWBK60161331926819",
	"XX00",
})
if err != nil {
	log.Fatal(err)
}

fmt.Println(batch.ValidCount, "of", batch.Count, "valid")

for _, r := range batch.Results { // results come back in input order
	fmt.Println(r.IBAN, r.Valid)
}
Extract from text
batch, err := client.Extract(ctx, "Please wire to DE89 3704 0044 0532 0130 00 by Friday.")
for _, r := range batch.Results {
	fmt.Println(r.IBAN, r.BankName)
}
Country format and BIC lookup

CountryFormat works without a key, limited to 100 requests an hour per IP. LookupBIC needs a key on the Basic plan or above, or a key with a verified account on the trial.

spec, err := client.CountryFormat(ctx, "DE")
fmt.Println(spec.Length, spec.Example) // 22 DE89370400440532013000

for _, f := range spec.BbanFields {
	fmt.Printf("%s (%d)\n", f.Label, f.Length) // BLZ (8), Account No. (10)
}

bank, err := client.LookupBIC(ctx, "DEUTDEFF")
fmt.Println(bank.BankName, bank.City) // Deutsche Bank AG Frankfurt  FRANKFURT AM MAIN
Tri-state fields are pointers

NationalCheckValid, SEPA and SWIFT are *bool, not bool. The API distinguishes false from absent and Go's zero value cannot, so a nil pointer means "not known for this IBAN" rather than "no".

result, err := client.Validate(ctx, "DE84100100100532013000")
if result.Valid && result.NationalCheckValid != nil && !*result.NationalCheckValid {
	fmt.Println("Valid IBAN, but the account number looks mistyped.")
}

NationalCheckValid reports a domestic account check digit run on top of the ISO 13616 checksum, such as Germany's per-bank Prüfziffer or the UK sort-code and account modulus check. It is advisory: an IBAN with Valid true is a valid IBAN whatever this says.

Error handling

A malformed IBAN is not an error: Validate returns a *ValidationResult with Valid false. Errors come back for transport, authentication, plan, quota and server-side problems only.

Compare with errors.Is for the common branches, and reach for errors.As when you need the detail:

bank, err := client.LookupBIC(ctx, "ZZZZZZZZ")
switch {
case errors.Is(err, ibanchecker.ErrNotFound):
	fmt.Println("No bank for that BIC")
case errors.Is(err, ibanchecker.ErrRateLimit):
	fmt.Println("The monthly quota is used up, or this call costs more than is left")
case errors.Is(err, ibanchecker.ErrAuthentication):
	fmt.Println("Missing or invalid API key")
}

var apiErr *ibanchecker.Error
if errors.As(err, &apiErr) {
	fmt.Println(apiErr.Status, apiErr.Code, apiErr.Message, apiErr.Response)
}

A QUOTA_EXCEEDED error also carries upgrade_url in apiErr.Response. A PLAN_REQUIRED error (HTTP 403) has no sentinel of its own and unwraps to ErrAPI; it carries required_plan and upgrade_url in apiErr.Response.

Sentinel Returned when
ErrBadRequest HTTP 400, the request was malformed, or a trial call went over the trial size (TOO_MANY_IBANS, TEXT_TOO_LONG)
ErrAuthentication HTTP 401, the API key is missing (every method except CountryFormat needs one, LookupBIC included), invalid or inactive
ErrNotFound HTTP 404, no such country code or BIC
ErrRateLimit HTTP 429, the key's monthly quota is used up or the call costs more than is left (QUOTA_EXCEEDED), or CountryFormat without a key went over 100 an hour (RATE_LIMIT_EXCEEDED)
ErrAPI any other error status, including HTTP 403 PLAN_REQUIRED (the key's plan does not include the method), or a body that could not be read
ErrTransport the request never reached the API: DNS, TLS, connection, timeout

Timeouts and your own HTTP stack

client := ibanchecker.New(apiKey, ibanchecker.WithTimeout(3*time.Second))

client := ibanchecker.New(apiKey, ibanchecker.WithHTTPClient(myClient))

WithHTTPClient replaces the whole client, so the timeout and the redirect policy below come from yours.

Redirects are refused, not followed

The default client stops at a redirect and returns an ErrTransport naming the target. That is deliberate, for two reasons. net/http turns a POST into a GET on a 301, as the RFC asks, so an http:// base URL would reach the https endpoint as a GET and come back 405 Method Not Allowed with nothing to explain it. And a redirect to another host is how an API key travels somewhere it was never meant to go.

Use an https base URL and the situation does not arise.

Raw responses

Every model keeps the untouched response body in Raw, so a field added to the API later is reachable without waiting for a client release.

result, _ := client.Validate(ctx, "DE89370400440532013000")
fmt.Println(result.Raw["transfer_type"]) // SEPA+SWIFT

Tests

go test ./...

The suite runs against httptest servers, so it needs no network.

Clients for other languages: Python, PHP, JavaScript, Ruby, and an MCP server.

License

MIT

Documentation

Overview

Package ibanchecker is the official Go client for the ibanchecker.cash IBAN validation API.

Validate IBANs across 92 countries, validate up to 100 IBANs per request, extract IBANs from free text, look up country format specifications and resolve SWIFT/BIC codes.

Every method except CountryFormat needs an API key, LookupBIC included; without one the API answers HTTP 401, returned as ErrAuthentication. A free key covers 100 requests a month and arrives by email in seconds; request it at https://ibanchecker.cash/api-docs, and see https://ibanchecker.cash/pricing for paid plans.

What a key can call follows its plan. A free key covers Validate only; ValidateBulk and LookupBIC need the Basic plan or above (Basic, Starter, Growth, Enterprise), and Extract the Growth plan or above (Growth, Enterprise). A key whose email address has a verified account at https://ibanchecker.cash/dashboard can try the methods its plan lacks: ValidateBulk with up to 10 IBANs per call, LookupBIC, and Extract with up to 5,000 characters per call. Outside the trial, a call the key's plan does not include gets HTTP 403 with the code "PLAN_REQUIRED"; this client has no sentinel for 403, so the *Error unwraps to ErrAPI.

Validate and LookupBIC count one request against the monthly quota, ValidateBulk one per IBAN in the call, and Extract one per IBAN found (at least one per call). CountryFormat works without a key, limited to 100 requests an hour per IP.

client := ibanchecker.New(os.Getenv("IBANCHECKER_API_KEY"))

result, err := client.Validate(context.Background(), "DE89 3704 0044 0532 0130 00")
if err != nil {
	log.Fatal(err)
}
if result.Valid {
	fmt.Println(result.BankName, result.BIC)
}

A malformed IBAN is not an error: Validate returns a ValidationResult with Valid false and an Error plus ErrorCode explaining why. Errors are returned for transport, authentication, plan, quota and server-side problems only.

Index

Constants

View Source
const DefaultBaseURL = "https://ibanchecker.cash/api/v1"

DefaultBaseURL is the production API.

View Source
const Version = "0.1.2"

Version goes out in the User-Agent header.

Variables

View Source
var (
	// ErrBadRequest is returned for HTTP 400: the request was malformed, or a
	// trial call went over the trial size (Code "TOO_MANY_IBANS" for
	// ValidateBulk, "TEXT_TOO_LONG" for Extract).
	ErrBadRequest = errors.New("bad request")

	// ErrAuthentication is returned for HTTP 401: the API key is missing,
	// invalid or inactive. Every method except CountryFormat needs a key, so
	// a LookupBIC call without one also gets it from the API's 401.
	ErrAuthentication = errors.New("authentication failed")

	// ErrNotFound is returned for HTTP 404: no such country code or BIC.
	ErrNotFound = errors.New("not found")

	// ErrRateLimit is returned for HTTP 429: the key's monthly quota was
	// used up, or the call costs more than the requests left this month
	// (Code "QUOTA_EXCEEDED"; ValidateBulk counts one request per IBAN and
	// Extract one per IBAN found), or a CountryFormat call without a key went
	// over 100 requests an hour per IP (Code "RATE_LIMIT_EXCEEDED"). The
	// hourly limit applies to CountryFormat only.
	ErrRateLimit = errors.New("rate limited")

	// ErrAPI is returned for any other error status, and for a response body
	// that could not be read as JSON. That includes HTTP 403 with Code
	// "PLAN_REQUIRED", a call the key's plan does not include; Response then
	// carries "required_plan" ("basic" or "growth") and "upgrade_url".
	ErrAPI = errors.New("api error")

	// ErrTransport is returned when the request never reached the API: DNS,
	// TLS, connection or timeout.
	ErrTransport = errors.New("transport error")
)

Sentinel errors for the failures a caller usually branches on. Compare them with errors.Is; reach for errors.As and *Error when you need the status, the machine-readable code or the decoded body.

Functions

This section is empty.

Types

type BankRecord

type BankRecord struct {
	BIC          string `json:"bic"`
	BIC8         string `json:"bic8"`
	BankCode     string `json:"bank_code"`
	CountryCode  string `json:"country_code"`
	LocationCode string `json:"location_code"`
	BranchCode   string `json:"branch_code"`
	BankName     string `json:"bank_name"`
	City         string `json:"city"`
	CountryName  string `json:"country_name"`

	SEPA   *bool  `json:"sepa"`
	Type   string `json:"type"`
	Status string `json:"status"`

	// Raw is the untouched response body.
	Raw map[string]any `json:"-"`
}

BankRecord is the institution behind a SWIFT/BIC code.

func (*BankRecord) UnmarshalJSON

func (b *BankRecord) UnmarshalJSON(data []byte) error

type BatchResult

type BatchResult struct {
	Count        int                `json:"count"`
	ValidCount   int                `json:"valid_count"`
	InvalidCount int                `json:"invalid_count"`
	Results      []ValidationResult `json:"results"`

	// Raw is the untouched response body.
	Raw map[string]any `json:"-"`
}

BatchResult is the result of a bulk validation or a text extraction. Results come back in the same order as the input.

func (*BatchResult) UnmarshalJSON

func (b *BatchResult) UnmarshalJSON(data []byte) error

type BbanField

type BbanField struct {
	Label       string `json:"label"`
	Length      int    `json:"length"`
	Type        string `json:"type"`
	Description string `json:"description"`

	// Raw is the untouched field object.
	Raw map[string]any `json:"-"`
}

BbanField is one segment of a country's BBAN, in the order it appears in the IBAN.

func (*BbanField) UnmarshalJSON

func (f *BbanField) UnmarshalJSON(data []byte) error

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client talks to the ibanchecker.cash API. The zero value is not usable; call New. A Client is safe for concurrent use by multiple goroutines.

func New

func New(apiKey string, opts ...Option) *Client

New returns a Client. Every method except CountryFormat needs an API key. An empty apiKey is accepted, but then only CountryFormat succeeds, limited to 100 requests an hour per IP.

func (*Client) CountryFormat

func (c *Client) CountryFormat(ctx context.Context, country string) (*FormatSpec, error)

CountryFormat returns the IBAN format specification for an ISO 3166-1 alpha-2 country code, for example "DE".

It is the only method that works without a key, limited to 100 requests an hour per IP; beyond that the API answers HTTP 429 with the code "RATE_LIMIT_EXCEEDED", returned as ErrRateLimit.

func (*Client) Extract

func (c *Client) Extract(ctx context.Context, text string) (*BatchResult, error)

Extract scans free text (emails, invoices) for IBAN-shaped strings and validates each candidate. Up to 50,000 characters per request.

It needs a key on the Growth plan or above; a key with a verified account can try it with up to 5,000 characters per call, and over that the API answers HTTP 400 with the code "TEXT_TOO_LONG". Each IBAN found counts one request, and a call counts at least one.

func (*Client) LookupBIC

func (c *Client) LookupBIC(ctx context.Context, bic string) (*BankRecord, error)

LookupBIC resolves an 8 or 11 character ISO 9362 BIC to a bank record.

It needs an API key: without one the API answers HTTP 401, returned as ErrAuthentication. The key needs the Basic plan or above, or a verified account for the trial; otherwise the API answers HTTP 403 with the code "PLAN_REQUIRED", returned as an *Error that unwraps to ErrAPI. Each call counts one request.

func (*Client) Validate

func (c *Client) Validate(ctx context.Context, iban string) (*ValidationResult, error)

Validate validates a single IBAN. Any key can call it, the free one included, and each call counts one request.

A malformed IBAN is not an error: the result comes back with Valid false and an Error plus ErrorCode explaining why.

func (*Client) ValidateBulk

func (c *Client) ValidateBulk(ctx context.Context, ibans []string) (*BatchResult, error)

ValidateBulk validates up to 100 IBANs in one request. Results come back in the same order as the input.

It needs a key on the Basic plan or above; a key with a verified account can try it with up to 10 IBANs per call, and over that the API answers HTTP 400 with the code "TOO_MANY_IBANS". Each IBAN in the call counts one request.

type Error

type Error struct {
	// Status is the HTTP status, or 0 when the request never completed.
	Status int

	// Code is the machine-readable code from the API body, for example
	// "BIC_NOT_FOUND". Empty when the API sent none.
	Code string

	// Message is the human-readable reason.
	Message string

	// Response is the decoded response body, when the API sent one.
	Response map[string]any
	// contains filtered or unexported fields
}

Error carries everything the API said about a failure.

A malformed IBAN is not an error: Validate returns a ValidationResult with Valid false. These are returned for transport, authentication, plan, quota and server-side problems only.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap reports the sentinel, so errors.Is(err, ErrNotFound) works.

type FormatSpec

type FormatSpec struct {
	CountryCode  string `json:"country_code"`
	CountryName  string `json:"country_name"`
	Length       int    `json:"length"`
	Currency     string `json:"currency"`
	CurrencyName string `json:"currency_name"`

	SEPA  *bool `json:"sepa"`
	SWIFT *bool `json:"swift"`

	FormatString string      `json:"format_string"`
	Example      string      `json:"example"`
	BbanFields   []BbanField `json:"bban_fields"`

	// Raw is the untouched response body.
	Raw map[string]any `json:"-"`
}

FormatSpec is the IBAN format specification for one country.

func (*FormatSpec) UnmarshalJSON

func (s *FormatSpec) UnmarshalJSON(data []byte) error

type Option

type Option func(*Client)

Option configures a Client.

func WithBaseURL

func WithBaseURL(baseURL string) Option

WithBaseURL points the client at a different host. Any trailing slash is trimmed.

func WithHTTPClient

func WithHTTPClient(hc *http.Client) Option

WithHTTPClient replaces the HTTP client, which is how an application routes these calls through its own stack, and how the tests run against an httptest server. It also overrides WithTimeout and the redirect policy below, so set both on the client you pass in.

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout sets the timeout on the default HTTP client.

type ValidationResult

type ValidationResult struct {
	Valid       bool   `json:"valid"`
	IBAN        string `json:"iban"`
	Formatted   string `json:"formatted"`
	CheckDigits string `json:"check_digits"`
	BBAN        string `json:"bban"`
	Country     string `json:"country"`
	CountryName string `json:"country_name"`

	BankName      string `json:"bank_name"`
	BankType      string `json:"bank_type"`
	BIC           string `json:"bic"`
	BankCity      string `json:"bank_city"`
	BankCode      string `json:"bank_code"`
	BranchCode    string `json:"branch_code"`
	AccountNumber string `json:"account_number"`

	// NationalCheckValid reports a domestic account check digit run on top of
	// the ISO 13616 checksum, such as Germany's per-bank Pruefziffer or the UK
	// sort-code and account modulus check. It is advisory: an IBAN with Valid
	// true is a valid IBAN whatever this says. False usually means a
	// transcription error in the account number. Nil where the country has no
	// such scheme.
	NationalCheckValid *bool `json:"national_check_valid"`

	Currency     string `json:"currency"`
	CurrencyName string `json:"currency_name"`
	TransferType string `json:"transfer_type"`

	// SEPA reports whether the IBAN's country is in the SEPA zone. Nil when
	// the API did not say.
	SEPA *bool  `json:"sepa"`
	Flag string `json:"flag"`

	// Error and ErrorCode explain why Valid is false.
	Error     string `json:"error"`
	ErrorCode string `json:"error_code"`

	// Raw is the untouched response body, so a field added to the API later is
	// reachable without waiting for a client release.
	Raw map[string]any `json:"-"`
}

ValidationResult is the result of validating one IBAN.

Valid is the primary flag. When it is false only IBAN, Formatted, Country, CountryName, Error and ErrorCode are populated.

func (*ValidationResult) UnmarshalJSON

func (r *ValidationResult) UnmarshalJSON(data []byte) error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL