boc-exchange-rate 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # Bank of Canada Exchange Rate API client
2
+
3
+ Official **Bank of Canada** (Canada) daily exchange rates in Node.js / TypeScript — ~27 currencies against the CAD, with history back to 2017. Zero dependencies, works in Node 18+, Bun, Deno, and edge runtimes (uses global `fetch`).
4
+
5
+ These are the *published central bank rates* required for tax filings, customs valuations, audits, and compliant invoicing — not moving market rates. Every response carries the bank's own publication date.
6
+
7
+ Powered by [AllRatesToday](https://allratestoday.com/central-bank-rates-api/boc/). Get a free API key at [allratestoday.com/register](https://allratestoday.com/register) — 300 requests/month, no credit card.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install boc-exchange-rate
13
+ ```
14
+
15
+ ## Quick start
16
+
17
+ ```js
18
+ import { getRate, getLatestRates } from 'boc-exchange-rate';
19
+
20
+ // One pair at the official Bank of Canada rate
21
+ const pair = await getRate('USD', 'CAD', { apiKey: 'art_live_...' });
22
+ console.log(pair.rate, pair.rate_date); // e.g. USD -> CAD on the bank's own date
23
+
24
+ // The bank's full published table
25
+ const table = await getLatestRates({ apiKey: 'art_live_...' });
26
+ console.log(table.rate_date, table.rates.length);
27
+ ```
28
+
29
+ ## Historical data (paid plans)
30
+
31
+ ```js
32
+ import { getRatesForDate, getHistory } from 'boc-exchange-rate';
33
+
34
+ // The official table for an invoice date — weekends/holidays return the
35
+ // most recent published date, flagged via published_on_requested_date.
36
+ const day = await getRatesForDate('2026-06-30', { apiKey: 'art_live_...' });
37
+
38
+ // Daily series for one pair
39
+ const series = await getHistory(
40
+ { source: 'USD', target: 'CAD', from: '2026-01-01' },
41
+ { apiKey: 'art_live_...' }
42
+ );
43
+ ```
44
+
45
+ ## Published vs derived rates
46
+
47
+ If Bank of Canada does not print a pair directly, the API resolves it from the bank's table (inverse, or a cross rate via CAD) and flags it `derived: true` with the `method` — so official and computed values are never confused.
48
+
49
+ ## Notes
50
+
51
+ - Every request counts toward your AllRatesToday monthly quota. Rates change once per business day — cache a day's table locally and a small quota goes a long way.
52
+ - Latest rates are on every plan (including free); historical dates and time series need a [paid plan](https://allratestoday.com/pricing/).
53
+ - Full API reference: [allratestoday.com/docs#central-bank](https://allratestoday.com/docs/#central-bank) · All covered banks: [central bank rates API](https://allratestoday.com/central-bank-rates-api/)
54
+
55
+ ## License
56
+
57
+ MIT
package/index.cjs ADDED
@@ -0,0 +1,47 @@
1
+ 'use strict';
2
+ // Bank of Canada exchange rates via the AllRatesToday API.
3
+ const BASE = 'https://allratestoday.com/api/v1/central-bank/boc';
4
+ const KEY_HINT = 'An apiKey is required — get a free key at https://allratestoday.com/register (300 requests/month, no credit card).';
5
+
6
+ async function request(path, params, options) {
7
+ const apiKey = options && options.apiKey;
8
+ if (!apiKey) throw new Error(KEY_HINT);
9
+ const url = new URL(BASE + path);
10
+ for (const [k, v] of Object.entries(params || {})) {
11
+ if (v !== undefined && v !== null) url.searchParams.set(k, v);
12
+ }
13
+ const res = await fetch(url, { headers: { Authorization: 'Bearer ' + apiKey } });
14
+ let body;
15
+ try { body = await res.json(); } catch { body = {}; }
16
+ if (!res.ok) {
17
+ const err = new Error(body.error || ('Request failed: HTTP ' + res.status));
18
+ err.status = res.status;
19
+ err.body = body;
20
+ throw err;
21
+ }
22
+ return body;
23
+ }
24
+
25
+ /** Latest published table (all plans, including free). */
26
+ async function getLatestRates(options) {
27
+ return request('/latest', {}, options);
28
+ }
29
+
30
+ /** Latest rate for one pair, e.g. getRate('USD', 'CAD', { apiKey }). */
31
+ async function getRate(source, target, options) {
32
+ return request('/latest', { source, target }, options);
33
+ }
34
+
35
+ /** Published table for a date (YYYY-MM-DD). Paid plans. Weekends/holidays return the most recent published date. */
36
+ async function getRatesForDate(date, options) {
37
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(String(date))) throw new Error('date must be YYYY-MM-DD');
38
+ return request('/' + date, options && options.source ? { source: options.source, target: options.target } : {}, options);
39
+ }
40
+
41
+ /** Daily time series. Pass { symbol } or { source, target }, optional from/to (YYYY-MM-DD). Paid plans. */
42
+ async function getHistory(query, options) {
43
+ const { symbol, source, target, from, to } = query || {};
44
+ return request('/history', { symbol, source, target, from, to }, options);
45
+ }
46
+
47
+ module.exports = { getLatestRates, getRate, getRatesForDate, getHistory };
package/index.d.ts ADDED
@@ -0,0 +1,52 @@
1
+ export interface RequestOptions {
2
+ /** AllRatesToday API key — free at https://allratestoday.com/register */
3
+ apiKey: string;
4
+ }
5
+
6
+ export interface RateEntry {
7
+ base: string;
8
+ quote: string;
9
+ type: string;
10
+ value: number;
11
+ }
12
+
13
+ export interface LatestRates {
14
+ bank: 'boc';
15
+ name: string;
16
+ rate_date: string;
17
+ rates: RateEntry[];
18
+ disclaimer: string;
19
+ }
20
+
21
+ export interface PairRate {
22
+ bank: 'boc';
23
+ name: string;
24
+ rate_date: string;
25
+ source: string;
26
+ target: string;
27
+ rate: number;
28
+ rate_type: string;
29
+ /** true when the pair was computed (inverse/cross) rather than published directly */
30
+ derived: boolean;
31
+ method: 'published' | 'inverse' | 'cross';
32
+ disclaimer: string;
33
+ }
34
+
35
+ export interface DatedRates extends Omit<LatestRates, 'rate_date'> {
36
+ requested_date: string;
37
+ rate_date: string;
38
+ published_on_requested_date: boolean;
39
+ }
40
+
41
+ export interface HistoryQuery {
42
+ symbol?: string;
43
+ source?: string;
44
+ target?: string;
45
+ from?: string;
46
+ to?: string;
47
+ }
48
+
49
+ export declare function getLatestRates(options: RequestOptions): Promise<LatestRates>;
50
+ export declare function getRate(source: string, target: string, options: RequestOptions): Promise<PairRate>;
51
+ export declare function getRatesForDate(date: string, options: RequestOptions & { source?: string; target?: string }): Promise<DatedRates>;
52
+ export declare function getHistory(query: HistoryQuery, options: RequestOptions): Promise<unknown>;
package/index.mjs ADDED
@@ -0,0 +1,44 @@
1
+ // Bank of Canada exchange rates via the AllRatesToday API.
2
+ const BASE = 'https://allratestoday.com/api/v1/central-bank/boc';
3
+ const KEY_HINT = 'An apiKey is required — get a free key at https://allratestoday.com/register (300 requests/month, no credit card).';
4
+
5
+ async function request(path, params, options) {
6
+ const apiKey = options && options.apiKey;
7
+ if (!apiKey) throw new Error(KEY_HINT);
8
+ const url = new URL(BASE + path);
9
+ for (const [k, v] of Object.entries(params || {})) {
10
+ if (v !== undefined && v !== null) url.searchParams.set(k, v);
11
+ }
12
+ const res = await fetch(url, { headers: { Authorization: 'Bearer ' + apiKey } });
13
+ let body;
14
+ try { body = await res.json(); } catch { body = {}; }
15
+ if (!res.ok) {
16
+ const err = new Error(body.error || ('Request failed: HTTP ' + res.status));
17
+ err.status = res.status;
18
+ err.body = body;
19
+ throw err;
20
+ }
21
+ return body;
22
+ }
23
+
24
+ /** Latest published table (all plans, including free). */
25
+ export async function getLatestRates(options) {
26
+ return request('/latest', {}, options);
27
+ }
28
+
29
+ /** Latest rate for one pair, e.g. getRate('USD', 'CAD', { apiKey }). */
30
+ export async function getRate(source, target, options) {
31
+ return request('/latest', { source, target }, options);
32
+ }
33
+
34
+ /** Published table for a date (YYYY-MM-DD). Paid plans. Weekends/holidays return the most recent published date. */
35
+ export async function getRatesForDate(date, options) {
36
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(String(date))) throw new Error('date must be YYYY-MM-DD');
37
+ return request('/' + date, options && options.source ? { source: options.source, target: options.target } : {}, options);
38
+ }
39
+
40
+ /** Daily time series. Pass { symbol } or { source, target }, optional from/to (YYYY-MM-DD). Paid plans. */
41
+ export async function getHistory(query, options) {
42
+ const { symbol, source, target, from, to } = query || {};
43
+ return request('/history', { symbol, source, target, from, to }, options);
44
+ }
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "boc-exchange-rate",
3
+ "version": "1.0.0",
4
+ "description": "Official Bank of Canada (Canada) daily exchange rates — ~27 currencies vs CAD, history since 2017. Zero-dependency Node.js/TypeScript client.",
5
+ "keywords": [
6
+ "boc",
7
+ "bank",
8
+ "of",
9
+ "canada",
10
+ "exchange-rate",
11
+ "exchange-rates",
12
+ "currency",
13
+ "forex",
14
+ "fx",
15
+ "cad",
16
+ "central-bank",
17
+ "official-rates",
18
+ "canada",
19
+ "api",
20
+ "typescript"
21
+ ],
22
+ "homepage": "https://allratestoday.com/central-bank-rates-api/boc/",
23
+ "bugs": {
24
+ "url": "https://allratestoday.com/contact/"
25
+ },
26
+ "license": "MIT",
27
+ "author": "AllRatesToday <hello@allratestoday.com> (https://allratestoday.com)",
28
+ "type": "module",
29
+ "main": "./index.cjs",
30
+ "module": "./index.mjs",
31
+ "types": "./index.d.ts",
32
+ "exports": {
33
+ ".": {
34
+ "types": "./index.d.ts",
35
+ "import": "./index.mjs",
36
+ "require": "./index.cjs"
37
+ }
38
+ },
39
+ "files": [
40
+ "index.mjs",
41
+ "index.cjs",
42
+ "index.d.ts",
43
+ "README.md"
44
+ ],
45
+ "engines": {
46
+ "node": ">=18"
47
+ },
48
+ "sideEffects": false
49
+ }