npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ibanforge/sdk

v1.7.0

Published

Official TypeScript SDK for the IBANforge API — IBAN validation, BIC/SWIFT lookup, Swiss BC-Nummer, SEPA/VoP and compliance risk scoring, with API-key auth, typed errors and x402 support.

Readme

@ibanforge/sdk

Official TypeScript/JavaScript SDK for the IBANforge API — IBAN validation, BIC/SWIFT lookup, Swiss BC-Nummer clearing, SEPA + VoP reachability and compliance risk scoring. Zero runtime dependencies (uses native fetch).

Every code block on this page is executed by the test suite, against recorded responses from the real API, and each // comment on a console.log is checked against what actually printed. A block that stops being true fails CI.

Install

npm install @ibanforge/sdk

Quick start

import { IBANforge } from '@ibanforge/sdk';

// Free format check — no API key needed
const fmt = await new IBANforge().formatIban('CH1000230000000012345');
console.log(fmt.valid);                    // true
console.log(fmt.bban?.bank_code);          // '00230'

// Authenticated calls (or pay per call via x402 — see below)
const client = new IBANforge({ apiKey: 'ifk_...' });

const r = await client.validateIban('CH1000230000000012345');
console.log(r.valid);                      // true
console.log(r.bic?.code);                  // 'UBSWCHZH'
console.log(r.bic?.bank_name);             // 'UBS Switzerland AG'
console.log(r.sepa?.member);               // true
console.log(r.clearing?.iid);              // '00230'
console.log(r.clearing?.qr_iid);           // '30005'
console.log(r.bank_code_check?.status);    // 'verified'

baseUrl and apiKey also read IBANFORGE_API_BASE and IBANFORGE_API_KEY from the environment, so the same variables configure the SDK and the MCP server.

The answer nobody else gives: is this bank code real?

An IBAN can pass its mod-97 checksum and still name a bank that does not exist. bank_code_check says which:

import { IBANforge } from '@ibanforge/sdk';

const client = new IBANforge({ apiKey: 'ifk_...' });

// The IBAN the SWIFT registry uses as an illustration. Structurally perfect,
// bank code 00762 allocated to nobody:
const ghost = await client.validateIban('CH9300762011623852957');
console.log(ghost.valid);                     // true
console.log(ghost.bank_code_check?.status);   // 'not_in_register'
console.log(ghost.bank_code_check?.authoritative); // true
console.log(ghost.bic);                       // null
console.log(ghost.clearing);                  // null
console.log(ghost.next_steps?.[0].code);      // 'bank_code_not_allocated'

valid: true and not_in_register is the correct pair: the number is well-formed, the bank is not there. Do not send.

Need an IBAN that does resolve — for a fixture, a demo, a test suite? Ask for one, with its proof:

import { IBANforge } from '@ibanforge/sdk';

const t = await new IBANforge().testIban({ country: 'CH' });   // free, no key
console.log(t.test_ibans[0].proof.bank_code_check.status);     // 'verified'
console.log(t.test_ibans[0].proof.bank_code_check.authoritative); // true

Get a reusable key without e-mail

import { IBANforge } from '@ibanforge/sdk';

const key = await IBANforge.generateApiKey();
console.log(key.monthly_limit);            // 25
// key.api_key is shown ONCE — store it now.

No address, card or confirmation is needed. Store the key in your secret configuration, reuse it for subsequent calls, and read usage() for its actual allowance. Normally this key provides 25 REST requests per month; temporary creation protection can lower that allowance, so read monthly_limit from the response. Do not create a key per request.

After the key has served a call, POST /v1/keys/claim with Authorization: Bearer ifk_… can raise its allowance: an explicitly supplied mailbox and a six-digit code give 200 requests per month; an x402 payment made on the key gives 200 requests once. An exhausted quota does not authorize sending an address or making a payment on the user's behalf. Follow the API's error details and retry delay.

The existing email argument and code option remain supported for callers who explicitly choose that route. No email is inferred from environment variables or other accounts.

All methods

| Method | Cost | What it does | |---|---|---| | formatIban(iban) | free | mod-97 + structure only. Pre-filter before paying. | | validateIban(iban) | $0.005 | Full enrichment — BIC, issuer/EMI class, SEPA + VoP, bank-code register check, Swiss BC-Nummer | | validateBatch([...]) | $0.002 / IBAN | Up to 100 in one call | | lookupBic(code) | $0.003 | BIC → bank, country, city, LEI, registered address | | lookupChClearing(iid) | $0.003 | Swiss BC-Nummer / IID → SIX rail participation + QR-IID | | checkCompliance(iban) | $0.02 | Sanctions (bank BIC) + FATF + SEPA + VoP + risk score 0–100 | | validateReference(reference) | free | QR-bill (QRR), ISO 11649 (RF/SCOR), Belgian OGM/VCS or Finnish reference, checked against the dated document that publishes the rule | | checkAddress(scheme, address) | free | A structured ISO 20022 postal address measured against a scheme's rules (sps, hvps_plus, fedwire), each finding citing its guideline | | ibanStructures() | free | Every supported country and its IBAN length | | ibanStructure(country) | free | One country's BBAN template | | testIban({country}) | free | Test IBANs with a REAL bank code, plus the register row proving it | | creditBundles() | free | Prepaid packs and their per-call price | | demo() | free | Worked examples of every endpoint | | usage() | free | This key's quota for the current month | | health() | free | API version, database size | | IBANforge.generateApiKey(email?) | free | Reusable key without e-mail; read its returned quota |

Batch and the two lookups, in practice:

import { IBANforge } from '@ibanforge/sdk';

const client = new IBANforge({ apiKey: 'ifk_...' });

const batch = await client.validateBatch(['CH1000230000000012345', 'DE89370400440532013000']);
console.log(batch.count);                  // 2
console.log(batch.valid_count);            // 2

const bic = await client.lookupBic('UBSWCHZH80A');
console.log(bic.found);                    // true
console.log(bic.institution);              // 'UBS Switzerland AG'
console.log(bic.lei);                      // '549300WOIFUSNYH0FL22'

const ch = await client.lookupChClearing('230');
console.log(ch.institution?.name);         // 'UBS Switzerland AG'
console.log(ch.payment_services?.sic);     // true
console.log(ch.qr_iid);                    // '30005'

const u = await client.usage();
console.log(u.limit);                      // 200

The reference endpoints need no key at all:

import { IBANforge } from '@ibanforge/sdk';

const client = new IBANforge();

const structures = await client.ibanStructures();
console.log(structures.total);              // 89

const ch = await client.ibanStructure('CH');
console.log(ch.iban_length);                // 21
console.log(ch.bban_pattern);               // '5!n12!c'

const packs = await client.creditBundles();
console.log(packs.bundles[0].credits);      // 1000
console.log(packs.bundles[0].price_usdc);   // 5

const d = await client.demo();
console.log((d.iban_examples ?? []).length > 0); // true

Compliance result shape

The score is nested under compliance. There is no top-level risk_score, and no recommended_action.

import { IBANforge } from '@ibanforge/sdk';

const client = new IBANforge({ apiKey: 'ifk_...' });

const c = await client.checkCompliance('GB29NWBK60161331926819');
console.log(c.compliance.risk_score);              // 10
console.log(c.compliance.risk_level);              // 'low'
console.log(c.compliance.sanctions.matched_lists); // []
console.log(c.compliance.sanctions.fatf_status);   // 'member'
console.log(c.compliance.reachability.sct);        // true
console.log(c.meta?.scope);                        // 'bank_bic_only'

Sanctions screening is at the bank (BIC8) level — it does not screen the beneficiary name and is not a regulated AML/CFT product. risk_level: 'unassessable' means nothing could be screened; it is the absence of a verdict, never a favourable one.

A malformed IBAN is not an error

This one surprises people, so it is worth one block: a syntactically wrong IBAN comes back 200 with valid: false, not an exception. Exceptions are for transport and authorization failures.

import { IBANforge } from '@ibanforge/sdk';

const bad = await new IBANforge().formatIban('CH93007620116238529XX');
console.log(bad.valid);                    // false
console.log(bad.error);                    // 'checksum_failed'

Typed errors

Every failure throws a typed subclass of IBANforgeError, carrying status, code (the API's error slug) and the parsed body:

import { IBANforge, AuthError, InvalidInputError } from '@ibanforge/sdk';

try {
  await new IBANforge({ apiKey: 'ifk_wrong' }).usage();
} catch (err) {
  if (err instanceof AuthError) {
    console.log(err.status);               // 401
    console.log(err.code);                 // 'invalid_key'
  }
}

try {
  await new IBANforge().lookupBic('NOTABIC');
} catch (err) {
  if (err instanceof InvalidInputError) {
    console.log(err.code);                 // 'invalid_bic_format'
    console.log(err.status);               // 400
  }
}

| Class | HTTP | When | |---|---|---| | AuthError | 401 / 403 | Missing, revoked or mistyped key; mailbox verification required | | PaymentRequiredError | 402 | No key and no credit. err.accepts carries the x402 challenge — pay and retry, no dead end | | QuotaExhaustedError | 429 | Monthly free quota spent (the API usually answers 402 instead, so you can pay through) | | RateLimitError | 429 | Too fast — back off | | PayloadTooLargeError | 413 | The body is over the limit — split it, do not retry the same payload | | InvalidInputError | other 4xx | Malformed request (a malformed IBAN is a 200, see above) | | APIError | 5xx | Server-side failure — retry with backoff |

Config

import { IBANforge } from '@ibanforge/sdk';

const client = new IBANforge({
  apiKey: 'ifk_...',   // or the IBANFORGE_API_KEY environment variable
  baseUrl: undefined,  // or IBANFORGE_API_BASE; defaults to api.ibanforge.com
  timeoutMs: 30_000,   // default 30s
});

const h = await client.health();
console.log(h.status);                     // 'ok'

Full documentation

ibanforge.com/docs · agent guide · OpenAPI

License

MIT