A lightweight Node.js wrapper for BitPay exchange rates, written in TypeScript.
Zero runtime dependencies, promise-based, ESM (and require() on Node 22.12+).
Talks to the official public
Rates API
(X-Accept-Version: 2.0.0).
- Node.js >= 22.12
npm install bitpay-rates- Promise-only. The legacy callback signature (
get(code, cb)) is gone — useasync/awaitor.then()/.catch(). - Named arguments.
get()now takes a single{ base, quote }object, so there is no argument order to remember:get('USD', 'ETH')becomesget({ base: 'ETH', quote: 'USD' }). get({ base })returns the whole table for that base, which v2 could not express.get({ quote })returns that one rate against BTC.- Dual ESM + CJS (the CJS file is gone in 3.1 — see below).
- Node.js >= 22 (3.1 requires >= 22.12).
- Requests time out after 10 seconds.
- Currency codes are validated (
/^[A-Z0-9]{2,10}$/); anything else rejects with aTypeErrorbefore a request is made.
v3.1 ships a single ESM file. require('bitpay-rates') still works on
Node 22.12+ (require(esm)). Node 22.0–22.11 need import or an upgrade.
import { get, type RateObj } from 'bitpay-rates';
const all: RateObj[] = await get();
// GET /rates/BTC → every rate against BTC
const vsEth: RateObj[] = await get({ base: 'ETH' });
// GET /rates/ETH → every rate against ETH
const usd: RateObj = await get({ quote: 'USD' });
// GET /rates/BTC/USD → { code: 'USD', name: 'US Dollar', rate: 76471.42 }
const ethUsd: RateObj = await get({ base: 'ETH', quote: 'USD' });
// GET /rates/ETH/USDbase is the cryptocurrency you are pricing (default BTC); quote is the
currency you want the price in. Omitting quote gives the full table. The
return type follows from that: RateObj[] without quote, RateObj with it.
The default export is a namespace object holding the same function, so the v2 import style keeps working:
import bitpayRates from 'bitpay-rates';
const usd = await bitpayRates.get({ quote: 'USD' });const { get } = require('bitpay-rates');
// or: const bitpayRates = require('bitpay-rates'); bitpayRates.get({ quote: 'USD' })
get({ quote: 'USD' })
.then((rate) => console.log(rate))
.catch((err) => console.error(err));All four styles — named or default, ESM or CommonJS — are asserted against the
built artifact on every CI run and before every publish (npm run smoke).
get() rejects when BitPay returns a non-2xx status, an { error } payload,
malformed JSON, a network failure, or when the request exceeds 10 seconds. It
rejects with a TypeError — before any request — when a code is not 2-10
alphanumeric characters.
It also rejects when the response shape does not match what you asked for.
GET /rates/{code} is polymorphic: a base with a rate table answers with a
list, anything else answers with a single rate. So get({ base: 'USD' })
rejects rather than handing you a RateObj typed as RateObj[].
import { get } from 'bitpay-rates';
get({ quote: 'INVALID' })
.then((rate) => console.log(rate))
.catch((err) => console.error(err));More examples in example/rates-example.mjs
(run npm run build first).
type RateObj = { code: string; name: string; rate: number };
type RateQuery = { base?: string; quote?: string };
function get(): Promise<RateObj[]>;
function get(query: { base?: string; quote?: undefined }): Promise<RateObj[]>;
function get(query: { base?: string; quote: string }): Promise<RateObj>;Both codes are uppercased automatically and must match /^[A-Z0-9]{2,10}$/.
Default base is BTC.
See CODES.md. It is regenerated from GET /rates/BTC on every
release PR (npm run update-codes). Codes containing _ (chain-specific
variants such as USDC_arb) appear in that table, but BitPay rejects them as a
base or quote, so they cannot be queried individually.
Zero runtime dependencies, published from CI only via npm Trusted Publishing (OIDC) with a provenance attestation, and every release is gated on a human publishing the draft GitHub Release. See SECURITY.md to report a vulnerability.
PRs only — see CONTRIBUTING.md. MIT licensed.