@allratestoday/sdk 1.4.5 โ†’ 1.5.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 CHANGED
@@ -5,546 +5,411 @@
5
5
  [![license](https://img.shields.io/npm/l/@allratestoday/sdk.svg)](https://github.com/AllRates-Today/exchange-rates-api/blob/main/LICENSE)
6
6
  [![TypeScript](https://badges.frapsoft.com/typescript/code/typescript.svg?v=101)](https://www.typescriptlang.org/)
7
7
  [![zero dependencies](https://img.shields.io/badge/dependencies-0-brightgreen.svg)](https://www.npmjs.com/package/@allratestoday/sdk)
8
- [![Security: Snyk](https://img.shields.io/badge/Security-Snyk-4C4A73?logo=snyk&logoColor=white)](https://snyk.io/test/github/allratestoday/exchange-rates-api)
9
8
 
10
- **Real-time mid-market currency exchange rates for Node.js and browsers. The most elegant way to access 160+ currencies.**
11
-
12
- ## ๐Ÿš€ Why Choose This Client?
13
-
14
- - โšก **Lightning Fast** โ€” Zero runtime dependencies, pure TypeScript performance
15
- - ๐Ÿ“ก **Real-Time Data** โ€” Rates updated every 60 seconds
16
- - ๐Ÿ’น **Mid-Market Rates** โ€” No retail spread or markup baked in
17
- - ๐ŸŒ **160+ Currencies** โ€” Major, minor, and exotic currency pairs
18
- - ๐Ÿ›๏ธ **Official Rates Too** โ€” latest tables from 121 central banks + 3 tax authorities via `officialRates()`, no key needed
19
- - ๐Ÿ”ท **Type-Safe** โ€” Full TypeScript support with intelligent autocomplete
20
- - ๐ŸŒ **Universal** โ€” Works seamlessly in Node.js 18+ and modern browsers
21
- - ๐Ÿง‘โ€๐Ÿ’ป **Developer-Friendly** โ€” Simple API, extensive documentation, and great DX
22
-
23
- ## ๐Ÿ”‘ Get Your API Key
24
-
25
- Ready to start? Get your free API key from [allratestoday.com/register](https://allratestoday.com/register).
26
-
27
- ## ๐Ÿ“ฆ Installation
9
+ Official JavaScript/TypeScript client for [AllRatesToday](https://allratestoday.com): live mid-market exchange rates for 160+ currencies, and the official rates published by 121 central banks and 3 tax authorities. Zero dependencies, ESM and CommonJS, full TypeScript types.
28
10
 
29
11
  ```bash
30
12
  npm install @allratestoday/sdk
31
13
  ```
32
14
 
33
- ```bash
34
- yarn add @allratestoday/sdk
35
- ```
15
+ ## Contents
36
16
 
37
- ```bash
38
- pnpm add @allratestoday/sdk
39
- ```
17
+ - [Two kinds of rates](#two-kinds-of-rates)
18
+ - [Quick start](#quick-start)
19
+ - [API key and configuration](#api-key-and-configuration)
20
+ - [Browser and frontend apps](#browser-and-frontend-apps)
21
+ - [What a rate means](#what-a-rate-means)
22
+ - [Methods](#methods)
23
+ - [Errors, retries and troubleshooting](#errors-retries-and-troubleshooting)
24
+ - [Quotas and billing](#quotas-and-billing)
25
+ - [Compatibility](#compatibility)
26
+ - [TypeScript](#typescript)
27
+ - [Use it from an AI agent](#use-it-from-an-ai-agent)
28
+ - [Testing, contributing and support](#testing-contributing-and-support)
40
29
 
41
- ## ๐Ÿ Quick Start
30
+ ## Two kinds of rates
42
31
 
43
- Get up and running in seconds:
32
+ | | Mid-market rates | Official rates |
33
+ | --- | --- | --- |
34
+ | Use for | Pricing, checkout, conversion widgets, dashboards | Invoices, VAT/GST, customs, accounting, audits |
35
+ | What it is | Live mid-market rate, no retail spread | The figure a central bank or tax authority published |
36
+ | Updates | About every minute | Once per publication (usually each business day) |
37
+ | History | Last 365 days (`forDate`, `timeSeries`, `getHistoricalRates`) | Each bank's full archive (`officialRatesOn`), e.g. ECB from 1999 |
38
+ | API key | Required | Latest table: **no key** (`officialRates`). Past dates and cross pairs: key (`officialRatesOn`) |
39
+ | Methods | `latest`, `getRate`, `getRates`, `convert`, `forDate`, `timeSeries`, `getHistoricalRates` | `officialRates`, `officialRatesOn`, `officialSources` |
44
40
 
45
- ```typescript
41
+ ## Quick start
42
+
43
+ ```ts
46
44
  import AllRatesToday from '@allratestoday/sdk';
47
45
 
48
- // Set your API key
49
- const client = new AllRatesToday({ apiKey: 'YOUR_API_KEY' });
46
+ const client = new AllRatesToday({ apiKey: process.env.ALLRATESTODAY_API_KEY });
50
47
 
51
- // Get latest exchange rates
52
48
  const { rates } = await client.latest({ base: 'USD', symbols: ['EUR', 'GBP', 'JPY'] });
53
- console.log(rates);
49
+ console.log(rates); // { EUR: 0.889521, GBP: 0.750666, JPY: 157.95 }
54
50
  ```
55
51
 
56
- ## ๐Ÿ“š API Reference
57
-
58
- - [Latest Rates](#-latest-exchange-rates) โ€” Get current exchange rates
59
- - [Historical Data](#-historical-exchange-rates) โ€” Fetch rates for specific dates
60
- - [Currency Conversion](#-currency-conversion) โ€” Convert between currencies
61
- - [Time Series](#-time-series-data) โ€” Get rates over date ranges
62
- - [Available Currencies](#-available-currencies) โ€” List all supported currencies
63
- - [Single Rate](#-single-rate) โ€” Get one currency pair
64
- - [Historical by Period](#-historical-rates-by-period) โ€” Preset period lookups
52
+ No key yet? Official rates work without one:
65
53
 
66
- ---
67
-
68
- ### Latest Exchange Rates
54
+ ```ts
55
+ const usdInr = await new AllRatesToday().officialRates('rbi', { source: 'USD', target: 'INR' });
56
+ console.log(usdInr.rate_date, usdInr.rate); // 2026-10-15 95.0691
57
+ ```
69
58
 
70
- Get the most current exchange rates with a single call:
59
+ Get a free key (300 requests, no card) at [allratestoday.com/register](https://allratestoday.com/register).
71
60
 
72
- ```typescript
73
- // Get all rates from USD
74
- const data = await client.latest();
75
- console.log(data);
61
+ ## API key and configuration
76
62
 
77
- // Target specific currencies
78
- const data = await client.latest({ base: 'USD', symbols: ['CHF', 'GBP', 'JPY'] });
79
- console.log(data);
63
+ ```ts
64
+ const client = new AllRatesToday({
65
+ apiKey: process.env.ALLRATESTODAY_API_KEY, // needed for market rates and dated official rates
66
+ baseUrl: 'https://allratestoday.com', // optional
67
+ timeout: 10000, // optional, ms
68
+ });
80
69
  ```
81
70
 
82
- **Response:**
71
+ | Option | Type | Default | Description |
72
+ | --- | --- | --- | --- |
73
+ | `apiKey` | `string` | โ€” | Sent as `Authorization: Bearer <key>`. Not needed for `officialRates`, `officialSources`, `symbols`. |
74
+ | `baseUrl` | `string` | `https://allratestoday.com` | Origin only. The SDK adds paths such as `/api/v1/rates`, so a proxy at another origin must serve the same paths. |
75
+ | `timeout` | `number` | `10000` | Per HTTP request, in ms. Applies to every method. On expiry the SDK throws `AllRatesTodayError` with `code: 'timeout'`. |
83
76
 
84
- ```javascript
85
- {
86
- base: 'USD',
87
- date: '2026-04-09T14:30:00Z',
88
- rates: {
89
- CHF: 0.8821,
90
- GBP: 0.7891,
91
- JPY: 151.42
92
- }
93
- }
94
- ```
77
+ **Keep the key out of your code and your repo.** Load it from the environment (`.env` with your framework's loader, or your host's secret settings) and add `.env` to `.gitignore`:
95
78
 
96
- ### Historical Exchange Rates
79
+ ```bash
80
+ # .env (never commit this file)
81
+ ALLRATESTODAY_API_KEY=art_live_...
82
+ ```
97
83
 
98
- Travel back in time to get rates from any date:
84
+ **Retries are not automatic.** Every method makes its requests once; see [retrying](#retrying-transient-failures) for a pattern.
99
85
 
100
- ```typescript
101
- // Using a string date
102
- const data = await client.forDate('2026-01-15', { base: 'USD', symbols: ['EUR'] });
103
- console.log(data);
86
+ **Per-request key override**, for multi-tenant apps: every method accepts `{ apiKey }` in its options.
104
87
 
105
- // Using a Date object
106
- const data = await client.forDate(new Date('2026-01-15'), { base: 'EUR', symbols: ['USD', 'GBP'] });
107
- console.log(data);
88
+ ```ts
89
+ await client.latest({ symbols: ['EUR'], apiKey: tenant.key });
90
+ await client.convert('USD', 'EUR', 100, { apiKey: tenant.key });
108
91
  ```
109
92
 
110
- **Response:**
111
-
112
- ```javascript
113
- {
114
- base: 'USD',
115
- date: '2026-01-15',
116
- rates: {
117
- EUR: 0.9187
118
- }
119
- }
120
- ```
93
+ ## Browser and frontend apps
121
94
 
122
- ### Currency Conversion
95
+ > [!WARNING]
96
+ > Never put your API key in browser, mobile or desktop app code. Anyone can read it from the bundle or the network tab and spend your quota.
123
97
 
124
- Convert any amount between currencies โ€” including historical conversions:
98
+ - **Keyless methods work in the browser.** `officialRates()`, `officialSources()` and `symbols()` call endpoints that send `Access-Control-Allow-Origin: *`.
99
+ - **Keyed methods are server-side only.** The keyed endpoints do not send CORS headers, so browsers block them anyway. Call them from your backend, an API route or a serverless function, and expose only what your frontend needs:
125
100
 
126
- ```typescript
127
- // Current conversion
128
- const result = await client.convert('USD', 'EUR', 1000);
129
- console.log(result);
130
- ```
101
+ ```ts
102
+ // app/api/rates/route.ts (Next.js route handler โ€” runs on the server)
103
+ import AllRatesToday from '@allratestoday/sdk';
131
104
 
132
- **Response:**
105
+ const client = new AllRatesToday({ apiKey: process.env.ALLRATESTODAY_API_KEY });
133
106
 
134
- ```javascript
135
- {
136
- from: 'USD',
137
- to: 'EUR',
138
- amount: 1000,
139
- result: 923.4,
140
- rate: 0.9234
107
+ export async function GET() {
108
+ const { rates, date } = await client.latest({ base: 'USD', symbols: ['EUR', 'GBP'] });
109
+ return Response.json({ rates, date }, { headers: { 'Cache-Control': 'public, max-age=60' } });
141
110
  }
142
111
  ```
143
112
 
144
- ```typescript
145
- // Historical conversion โ€” what was $1,000 worth on Jan 15?
146
- const result = await client.convert('USD', 'EUR', 1000, { date: '2026-01-15' });
147
- console.log(result);
113
+ ```ts
114
+ // In the browser: call your own endpoint, not AllRatesToday
115
+ const { rates } = await fetch('/api/rates').then((r) => r.json());
148
116
  ```
149
117
 
150
- **Response:**
118
+ In SSR frameworks, create the client only in server code (route handlers, server components, `getServerSideProps`, loaders), never in a component that also runs in the browser.
151
119
 
152
- ```javascript
153
- {
154
- from: 'USD',
155
- to: 'EUR',
156
- amount: 1000,
157
- result: 918.7,
158
- rate: 0.9187,
159
- date: '2026-01-15'
160
- }
161
- ```
120
+ ## What a rate means
162
121
 
163
- ### Available Currencies
122
+ - **Direction.** Every rate is *units of the target per 1 unit of the base*. `latest({ base: 'USD', symbols: ['EUR'] })` โ†’ `rates.EUR = 0.8895` means 1 USD = 0.8895 EUR. For the reverse, use `1 / rate` or swap base and target.
123
+ - **Market timestamps** (`date` in `latest`, `time` in `getRate`/`getRates`) are UTC, ISO 8601, e.g. `2026-10-16T08:34:01+0000`.
124
+ - **Market history dates** are UTC calendar days (`YYYY-MM-DD`). `1d` history has hourly timestamps.
125
+ - **Official `rate_date`** is the day the institution published the table, not the time of your request. Cite it on invoices and filings.
126
+ - **Weekends and holidays.** Banks publish nothing on those days. `officialRatesOn()` returns the last earlier table and sets `published_on_requested_date: false`. `forDate()` uses the closest earlier point and reports it in `rateDates`.
127
+ - **Stale sources.** `stale: true` on an official response means the institution is behind its own publication schedule; the table is its latest one. `officialSources()` lists which sources are stale right now.
128
+ - **Derived official pairs.** `method: 'published'` is the bank's own figure; `'inverse'` is 1 / a published rate; `'cross'` is computed through the bank's base currency (only from `officialRatesOn`). `derived: true` for the last two.
164
129
 
165
- Discover all 160+ supported currency symbols and names:
130
+ ## Methods
166
131
 
167
- ```typescript
168
- const data = await client.symbols();
169
- console.log(data);
170
- ```
132
+ All methods return a Promise. Codes are ISO 4217 and case-insensitive. Dates are `YYYY-MM-DD` strings or `Date` objects (a `Date` is taken as its UTC day). "Calls" is how many requests the method counts against your quota.
171
133
 
172
- **Response:**
173
-
174
- ```javascript
175
- {
176
- symbols: {
177
- AED: 'United Arab Emirates Dirham',
178
- AFN: 'Afghan Afghani',
179
- ALL: 'Albanian Lek',
180
- AMD: 'Armenian Dram',
181
- AUD: 'Australian Dollar',
182
- BDT: 'Bangladeshi Taka',
183
- BRL: 'Brazilian Real',
184
- CAD: 'Canadian Dollar',
185
- CHF: 'Swiss Franc',
186
- CNY: 'Chinese Yuan',
187
- EUR: 'Euro',
188
- GBP: 'British Pound Sterling',
189
- INR: 'Indian Rupee',
190
- JPY: 'Japanese Yen',
191
- KRW: 'South Korean Won',
192
- USD: 'United States Dollar',
193
- // ...160+ currencies
194
- }
195
- }
196
- ```
134
+ | Method | Key | Calls |
135
+ | --- | --- | --- |
136
+ | [`latest(options?)`](#latestoptions) | yes | 1 |
137
+ | [`getRate(from, to, amount?, options?)`](#getratefrom-to-amount-options) | yes | 1 |
138
+ | [`getRates(source, target, options?)`](#getratessource-target-options) | yes | 1 |
139
+ | [`convert(from, to, amount, options?)`](#convertfrom-to-amount-options) | yes | 1 (historical: as `forDate`) |
140
+ | [`forDate(date, options)`](#fordatedate-options) | yes | per symbol: 1, or 12 if over 29 days ago |
141
+ | [`timeSeries(start, end, options)`](#timeseriesstart-end-options) | yes | per symbol: 1, or 12 if start over 29 days ago |
142
+ | [`getHistoricalRates(source, target, period?, options?)`](#gethistoricalratessource-target-period-options) | yes | 1, or 12 for `1y` |
143
+ | [`symbols(options?)`](#symbolsoptions) | no | โ€” |
144
+ | [`officialRates(bank, options?)`](#officialratesbank-options) | no | โ€” |
145
+ | [`officialRatesOn(bank, date, options?)`](#officialratesonbank-date-options) | yes | 1 |
146
+ | [`officialSources(options?)`](#officialsourcesoptions) | no | โ€” |
197
147
 
198
- ### Time Series Data
148
+ ### `latest(options?)`
199
149
 
200
- Get exchange rates over a date range for trend analysis and charting:
150
+ Live mid-market rates from one base.
201
151
 
202
- ```typescript
203
- const data = await client.timeSeries('2026-01-01', '2026-03-31', {
204
- base: 'USD',
205
- symbols: ['EUR', 'GBP'],
206
- });
207
- console.log(data);
208
- ```
152
+ | Option | Type | Default | |
153
+ | --- | --- | --- | --- |
154
+ | `base` | `string` | `'USD'` | Base currency |
155
+ | `symbols` | `string[]` | all (~160) | Target currencies; still one call |
209
156
 
210
- **Response:**
211
-
212
- ```javascript
213
- {
214
- base: 'USD',
215
- startDate: '2026-01-01',
216
- endDate: '2026-03-31',
217
- rates: {
218
- '2026-01-01': {
219
- EUR: 0.9187,
220
- GBP: 0.7834
221
- },
222
- '2026-01-02': {
223
- EUR: 0.9195,
224
- GBP: 0.7841
225
- },
226
- '2026-01-03': {
227
- EUR: 0.9201,
228
- GBP: 0.7856
229
- },
230
- // ...
231
- }
232
- }
157
+ ```ts
158
+ const data = await client.latest({ base: 'USD', symbols: ['EUR', 'GBP', 'JPY'] });
159
+ // { base: 'USD', date: '2026-10-16T08:37:01+0000', rates: { EUR: 0.889521, GBP: 0.750666, JPY: 157.95 } }
233
160
  ```
234
161
 
235
- ### Single Rate
162
+ ### `getRate(from, to, amount?, options?)`
236
163
 
237
- Get a single exchange rate between two currencies:
164
+ One pair, optionally converted.
238
165
 
239
- ```typescript
240
- const rate = await client.getRate('USD', 'EUR');
241
- console.log(rate);
242
- ```
243
-
244
- **Response:**
166
+ ```ts
167
+ await client.getRate('USD', 'EUR');
168
+ // { from: { currency: 'USD', amount: 1 }, to: { currency: 'EUR', amount: 0.889521 },
169
+ // rate: 0.889521, source: 'interbank', time: '2026-10-16T08:38:01+0000' }
245
170
 
246
- ```javascript
247
- {
248
- from: { currency: 'USD', amount: 1 },
249
- to: { currency: 'EUR', amount: 0.9234 },
250
- rate: 0.9234,
251
- source: 'wise'
252
- }
171
+ await client.getRate('USD', 'EUR', 500);
172
+ // { from: { currency: 'USD', amount: 500 }, to: { currency: 'EUR', amount: 444.7605 }, rate: 0.889521, source: 'interbank' }
253
173
  ```
254
174
 
255
- ```typescript
256
- // With amount
257
- const rate = await client.getRate('USD', 'EUR', 500);
258
- console.log(`$500 = โ‚ฌ${rate.to.amount}`);
259
- ```
175
+ `source` is the rate type label (`interbank` = mid-market), not a data vendor.
260
176
 
261
- ### Official Central-Bank & Tax-Authority Rates (no key needed)
177
+ ### `getRates(source, target, options?)`
262
178
 
263
- The methods above return live mid-market rates. For the **official rate an
264
- institution published** โ€” what invoices, VAT returns, customs declarations and
265
- audits require โ€” use `officialRates()`. It covers 121 central banks plus HMRC,
266
- the US Treasury and Swiss BAZG, and the latest table needs **no API key**:
179
+ Raw rate rows. `target` may be comma-separated (`'EUR,GBP'`) for several rows in one call.
267
180
 
268
- ```typescript
269
- const client = new AllRatesToday(); // no key
181
+ ```ts
182
+ await client.getRates('USD', 'EUR,GBP');
183
+ // [ { rate: 0.889403, source: 'USD', target: 'EUR', time: '2026-10-16T08:34:01+0000' },
184
+ // { rate: 0.750723, source: 'USD', target: 'GBP', time: '2026-10-16T08:34:01+0000' } ]
185
+ ```
270
186
 
271
- // Full ECB reference table
272
- const table = await client.officialRates('ecb');
273
- console.log(table.rate_date, table.rates); // [{ base: 'EUR', quote: 'USD', type: 'reference', value: 1.17 }, โ€ฆ]
187
+ ### `convert(from, to, amount, options?)`
274
188
 
275
- // One pair (cross-computed inside the bank's own table if not published directly โ†’ `derived: true`)
276
- const usd = await client.officialRates('ecb', { source: 'EUR', target: 'USD' });
277
- console.log(`ECB ${usd.rate_date}: 1 EUR = ${usd.rate} USD`);
189
+ Convert at the live rate, or at a past date's rate with `{ date }` (last 365 days).
278
190
 
279
- // Which sources exist, and which are behind their publication schedule
280
- const { sources } = await client.officialSources();
281
- console.log(sources.map((s) => `${s.code} ${s.latest}${s.stale ? ' (stale)' : ''}`));
191
+ ```ts
192
+ await client.convert('USD', 'EUR', 1000);
193
+ // { from: 'USD', to: 'EUR', amount: 1000, result: 889.403, rate: 0.889403 }
194
+
195
+ await client.convert('USD', 'EUR', 1000, { date: '2026-10-01' });
196
+ // { from: 'USD', to: 'EUR', amount: 1000, result: 882.768, rate: 0.882768, date: '2026-10-01', rateDate: '2026-10-01' }
282
197
  ```
283
198
 
284
- Always cite the returned `rate_date` โ€” it is the institution's publication date,
285
- not the time of your call. Dated tables, history and publication calendars sit
286
- behind the free key on `/api/v1/central-bank/{bank}/{date}`; see the
287
- [central bank rates API](https://allratestoday.com/central-bank-rates-api/).
199
+ For money, round once at the end in the currency's minor unit. `result` is not rounded to cents.
288
200
 
289
- ### Historical Rates by Period
201
+ ### `forDate(date, options)`
290
202
 
291
- Get historical rates using preset periods โ€” no date math needed:
203
+ Mid-market rates for a past date in the last 365 days.
292
204
 
293
- ```typescript
294
- const history = await client.getHistoricalRates('USD', 'EUR', '30d');
295
- console.log(history);
296
- ```
205
+ | Option | Type | Default | |
206
+ | --- | --- | --- | --- |
207
+ | `symbols` | `string[]` | **required** | One request per symbol |
208
+ | `base` | `string` | `'USD'` | |
297
209
 
298
- **Response:**
299
-
300
- ```javascript
301
- {
302
- source: 'USD',
303
- target: 'EUR',
304
- period: '30d',
305
- current: { rate: 0.9234, time: '2026-04-09T14:30:00Z' },
306
- rates: [
307
- { rate: 0.9198, time: '2026-03-10T00:00:00Z' },
308
- { rate: 0.9210, time: '2026-03-11T00:00:00Z' },
309
- // ...
310
- ]
311
- }
210
+ ```ts
211
+ await client.forDate('2026-10-01', { base: 'EUR', symbols: ['USD', 'GBP'] });
212
+ // { base: 'EUR', date: '2026-10-01', rates: { USD: 1.1328, GBP: 0.85395 },
213
+ // rateDates: { USD: '2026-10-01', GBP: '2026-10-01' } }
312
214
  ```
313
215
 
314
- **Available periods:** `1d` | `7d` | `30d` | `1y`
216
+ Dates up to 29 days ago use daily data. Older dates use the 1-year series, which has **weekly** points, so `rateDates` may be a few days earlier than `date`, and each symbol costs 12 calls. Dates over a year old throw `invalid_argument`; use [`officialRatesOn()`](#officialratesonbank-date-options) for those.
315
217
 
316
- ---
218
+ ### `timeSeries(start, end, options)`
317
219
 
318
- ## ๐Ÿ”ง Configuration
220
+ Rates for every available day between two dates (inclusive), in the last 365 days. Same options, data density and costs as `forDate`.
319
221
 
320
- ```typescript
321
- const client = new AllRatesToday({
322
- apiKey: 'YOUR_API_KEY', // Required โ€” get one free at allratestoday.com/register
323
- baseUrl: 'https://allratestoday.com', // Optional โ€” API base URL
324
- timeout: 10000, // Optional โ€” request timeout in ms
325
- });
222
+ ```ts
223
+ const ts = await client.timeSeries('2026-10-05', '2026-10-08', { symbols: ['EUR', 'GBP'] });
224
+ // { base: 'USD', startDate: '2026-10-05', endDate: '2026-10-08', rates: {
225
+ // '2026-10-05': { EUR: 0.888573, GBP: 0.755116 },
226
+ // '2026-10-06': { EUR: 0.891504, GBP: 0.756544 }, ... } }
227
+
228
+ // For a chart
229
+ const labels = Object.keys(ts.rates).sort();
230
+ const values = labels.map((d) => ts.rates[d].EUR);
326
231
  ```
327
232
 
328
- | Option | Type | Default | Description |
329
- | --------- | -------- | --------------------------- | -------------------------------- |
330
- | `apiKey` | `string` | โ€” | Your API key |
331
- | `baseUrl` | `string` | `https://allratestoday.com` | API base URL |
332
- | `timeout` | `number` | `10000` | Request timeout in milliseconds |
233
+ ### `getHistoricalRates(source, target, period?, options?)`
333
234
 
334
- ---
235
+ A preset window ending today: `'1d'` (hourly), `'7d'` (default) and `'30d'` (daily), `'1y'` (weekly, 12 calls).
335
236
 
336
- ## ๐Ÿ”€ Per-Request API Key Override
237
+ ```ts
238
+ const h = await client.getHistoricalRates('USD', 'EUR', '7d');
239
+ // { source: 'USD', target: 'EUR', period: '7d',
240
+ // current: { rate: 0.889521, time: '2026-10-16' },
241
+ // rates: [ { rate: 0.892738, time: '2026-10-10' }, ... ], // oldest first
242
+ // data: [ { date: '2026-10-10', rate: 0.892738, timestamp: 1791590400000 }, ... ] }
243
+ ```
337
244
 
338
- Every method supports a per-request API key override โ€” useful for multi-tenant apps:
245
+ ### `symbols(options?)`
339
246
 
340
- ```typescript
341
- const client = new AllRatesToday({ apiKey: 'default_key' });
247
+ Every supported currency. No key needed.
342
248
 
343
- // Override for a specific request
344
- const data = await client.latest({ apiKey: 'other_users_key' });
345
- const result = await client.convert('USD', 'EUR', 100, { apiKey: 'tenant_key' });
346
- const { symbols } = await client.symbols({ apiKey: 'another_key' });
249
+ ```ts
250
+ const { symbols, currencies, count } = await client.symbols();
251
+ // symbols: { USD: 'US Dollar', EUR: 'Euro', GBP: 'British Pound', ... }
252
+ // currencies: [ { code: 'USD', name: 'US Dollar', symbol: '$' }, ... ]
253
+ // count: 163
347
254
  ```
348
255
 
349
- ---
256
+ ### `officialRates(bank, options?)`
350
257
 
351
- ## ๐Ÿ›ก๏ธ Error Handling
258
+ The latest table an institution published. No key needed. `bank` is a source code from `officialSources()`, e.g. `ecb`, `fed`, `boe`, `rbi`, `cbuae`, `hmrc`.
352
259
 
353
- All errors are thrown as `AllRatesTodayError` with an optional HTTP status code:
260
+ | Option | Type | |
261
+ | --- | --- | --- |
262
+ | `source`, `target` | `string` | Give both to get one pair. It must be published, or the inverse of a published rate. |
354
263
 
355
- ```typescript
356
- import AllRatesToday, { AllRatesTodayError } from '@allratestoday/sdk';
264
+ ```ts
265
+ const table = await client.officialRates('ecb');
266
+ // { bank: 'ecb', rate_date: '2026-10-15', stale: false, cadence: 'daily',
267
+ // rates: [ { base: 'EUR', quote: 'AUD', type: 'reference', value: 1.6218 }, ... ],
268
+ // attribution: { source: 'Official rates published by ECB, served by AllRatesToday', url: '...', terms: '...' } }
357
269
 
358
- try {
359
- const rate = await client.getRate('USD', 'INVALID');
360
- } catch (err) {
361
- if (err instanceof AllRatesTodayError) {
362
- console.log(err.message); // "Currency not found"
363
- console.log(err.status); // 404
364
- }
365
- }
270
+ const pair = await client.officialRates('ecb', { source: 'EUR', target: 'USD' });
271
+ // { bank: 'ecb', rate_date: '2026-10-15', source: 'EUR', target: 'USD', rate: 1.1208,
272
+ // rate_type: 'reference', derived: false, method: 'published', stale: false, ... }
366
273
  ```
367
274
 
368
- | Status | Meaning |
369
- | ------ | ------------------------------------ |
370
- | โ€” | Missing API key (thrown before request) |
371
- | `401` | Invalid API key |
372
- | `404` | Currency code not found |
373
- | `429` | Rate limit exceeded |
374
- | `500` | Server error |
375
-
376
- ---
377
-
378
- ## ๐Ÿ”ท TypeScript
379
-
380
- The SDK is written in TypeScript and ships full type definitions. All interfaces are exported:
381
-
382
- ```typescript
383
- import AllRatesToday, {
384
- AllRatesTodayError,
385
- AllRatesTodayOptions,
386
- RequestOptions,
387
- RateResponse,
388
- AuthRateResponse,
389
- LatestOptions,
390
- LatestResponse,
391
- ForDateOptions,
392
- ForDateResponse,
393
- ConvertOptions,
394
- ConvertResponse,
395
- TimeSeriesOptions,
396
- TimeSeriesResponse,
397
- HistoricalRateResponse,
398
- SymbolsResponse,
399
- } from '@allratestoday/sdk';
400
- ```
275
+ A pair the bank does not cover (e.g. USD/GBP from the ECB) throws a 404; use `officialRatesOn()` for cross pairs. Keyless use requires the visible attribution in `attribution.terms`.
401
276
 
402
- ---
277
+ ### `officialRatesOn(bank, date, options?)`
403
278
 
404
- ## ๐Ÿ“ฆ CommonJS
279
+ The table, or one pair, as published on a date. Needs a key. Covers each bank's archive, and any pair inside the table.
405
280
 
406
- ```javascript
407
- const AllRatesToday = require('@allratestoday/sdk').default;
281
+ ```ts
282
+ await client.officialRatesOn('rbi', '2026-09-30', { source: 'USD', target: 'INR' });
283
+ // { bank: 'rbi', name: 'Reserve Bank of India', requested_date: '2026-09-30', rate_date: '2026-09-30',
284
+ // published_on_requested_date: true, source: 'USD', target: 'INR', rate: 95.9832,
285
+ // rate_type: 'reference', derived: false, method: 'published', disclaimer: '...' }
408
286
 
409
- const client = new AllRatesToday({ apiKey: 'YOUR_API_KEY' });
410
- client.latest().then(data => console.log(data.rates));
287
+ await client.officialRatesOn('ecb', '2019-03-15', { source: 'USD', target: 'GBP' });
288
+ // { ..., rate_date: '2019-03-15', rate: 0.7553501946, derived: true, method: 'cross' }
411
289
  ```
412
290
 
413
- ---
291
+ ### `officialSources(options?)`
414
292
 
415
- ## ๐Ÿ’ก Examples
416
-
417
- ### Currency Dropdown (React)
418
-
419
- ```tsx
420
- import { useEffect, useState } from 'react';
421
- import AllRatesToday from '@allratestoday/sdk';
293
+ Every official source with its latest publication date. No key needed.
422
294
 
423
- const client = new AllRatesToday({ apiKey: 'YOUR_API_KEY' });
424
-
425
- function CurrencySelect({ value, onChange }) {
426
- const [currencies, setCurrencies] = useState([]);
427
-
428
- useEffect(() => {
429
- client.symbols().then(({ symbols }) => {
430
- setCurrencies(
431
- Object.entries(symbols).map(([code, name]) => ({ code, name }))
432
- );
433
- });
434
- }, []);
435
-
436
- return (
437
- <select value={value} onChange={e => onChange(e.target.value)}>
438
- {currencies.map(c => (
439
- <option key={c.code} value={c.code}>{c.code} โ€” {c.name}</option>
440
- ))}
441
- </select>
442
- );
443
- }
295
+ ```ts
296
+ const { sources, stale_count } = await client.officialSources();
297
+ // sources: [ { code: 'amcm', name: 'Monetary Authority of Macao', latest: '2026-10-16', stale: false, cadence: 'daily' }, ... ]
444
298
  ```
445
299
 
446
- ### Price Display with Caching
300
+ ## Errors, retries and troubleshooting
447
301
 
448
- ```typescript
449
- import AllRatesToday from '@allratestoday/sdk';
302
+ Every failure is an `AllRatesTodayError` with a `code`, and for HTTP errors a `status` and the parsed response body in `details`.
450
303
 
451
- const client = new AllRatesToday({ apiKey: 'YOUR_API_KEY' });
452
- let cache: { rates: Record<string, number>; time: number } | null = null;
453
- const TTL = 5 * 60 * 1000; // 5 minutes
304
+ ```ts
305
+ import AllRatesToday, { AllRatesTodayError } from '@allratestoday/sdk';
454
306
 
455
- async function getPrice(amountUSD: number, currency: string): Promise<string> {
456
- if (!cache || Date.now() - cache.time > TTL) {
457
- const data = await client.latest({ base: 'USD' });
458
- cache = { rates: data.rates, time: Date.now() };
307
+ try {
308
+ await client.latest({ symbols: ['EUR'] });
309
+ } catch (err) {
310
+ if (err instanceof AllRatesTodayError) {
311
+ console.log(err.code, err.status, err.message, err.details);
459
312
  }
460
- const converted = (amountUSD * cache.rates[currency]).toFixed(2);
461
- return `${converted} ${currency}`;
462
313
  }
463
-
464
- console.log(await getPrice(99.99, 'EUR')); // "92.32 EUR"
465
- console.log(await getPrice(99.99, 'GBP')); // "78.90 GBP"
466
314
  ```
467
315
 
468
- ### Historical Chart Data
469
-
470
- ```typescript
471
- import AllRatesToday from '@allratestoday/sdk';
472
-
473
- const client = new AllRatesToday({ apiKey: 'YOUR_API_KEY' });
474
-
475
- const data = await client.timeSeries('2026-01-01', '2026-04-01', {
476
- base: 'USD',
477
- symbols: ['EUR'],
478
- });
479
-
480
- // Format for charting libraries (Chart.js, Recharts, etc.)
481
- const labels = Object.keys(data.rates).sort();
482
- const values = labels.map(date => data.rates[date].EUR);
316
+ | `code` | `status` | Cause | What to do |
317
+ | --- | --- | --- | --- |
318
+ | `missing_key` | โ€” | Keyed method called without `apiKey` (no request sent) | Pass `apiKey` |
319
+ | `invalid_argument` | โ€” | Bad date format, future date, market date over a year old, missing `symbols`, bad bank code, lone `source`/`target` | Fix the call; the message says which |
320
+ | `http` | `400` | Unknown currency code or bad parameter | Check codes against `symbols()` |
321
+ | `http` | `401` | Missing or invalid key | Check the key in your [dashboard](https://allratestoday.com/profile) |
322
+ | `http` | `403` | Your plan does not include this data (e.g. market rates on an official-rates-only plan) | See `details.error`; change plan |
323
+ | `http` | `404` | The bank does not publish that pair / no table | Try `officialRatesOn()` or another source |
324
+ | `http` | `429` | Request quota used up | `details` has `used`, `limit`, `plan`, `resets_at`, `upgrade_url` |
325
+ | `http` | `5xx` | Temporary server or upstream error | Retry with backoff |
326
+ | `timeout` | โ€” | No response within `timeout` ms | Retry; raise `timeout` if it keeps happening |
327
+ | `network` | โ€” | DNS, TLS or connection failure (`err.cause` has the original error) | Retry; check connectivity |
328
+
329
+ ### Retrying transient failures
330
+
331
+ Retry only `timeout`, `network` and `5xx`. Retrying `4xx` (including `429` quota errors) just fails again.
332
+
333
+ ```ts
334
+ async function withRetry<T>(fn: () => Promise<T>, attempts = 3): Promise<T> {
335
+ for (let i = 1; ; i++) {
336
+ try {
337
+ return await fn();
338
+ } catch (err) {
339
+ const retryable =
340
+ err instanceof AllRatesTodayError &&
341
+ (err.code === 'timeout' || err.code === 'network' || (err.status ?? 0) >= 500);
342
+ if (!retryable || i >= attempts) throw err;
343
+ await new Promise((r) => setTimeout(r, 500 * 2 ** (i - 1))); // 0.5s, 1s, ...
344
+ }
345
+ }
346
+ }
483
347
 
484
- console.log({ labels, values });
485
- // { labels: ['2026-01-01', '2026-01-02', ...], values: [0.92, 0.921, ...] }
348
+ const data = await withRetry(() => client.latest({ symbols: ['EUR'] }));
486
349
  ```
487
350
 
488
- ---
489
-
490
- ## ๐Ÿ“– Methods Reference
351
+ **Cache instead of polling.** Market rates move about once a minute and official rates once a day, so cache `latest()` for at least 60 seconds and official tables until the next publication.
491
352
 
492
- | Method | Description |
493
- | ------ | ----------- |
494
- | `latest(options?)` | Get latest rates for one or more currencies |
495
- | `forDate(date, options?)` | Get rates for a specific historical date |
496
- | `convert(from, to, amount, options?)` | Convert amount between currencies (supports historical date) |
497
- | `timeSeries(start, end, options?)` | Get rates across a custom date range |
498
- | `symbols(options?)` | List all 160+ supported currency codes and names |
499
- | `getRate(from, to, amount?, options?)` | Get a single exchange rate |
500
- | `getRates(source, target, options?)` | Get rates with full metadata |
501
- | `getHistoricalRates(source, target, period?, options?)` | Historical rates by preset period (1d/7d/30d/1y) |
502
- | `officialRates(bank, options?)` | Latest official table (or one pair) published by a central bank / tax authority โ€” **keyless** |
503
- | `officialSources(options?)` | Every covered official source with latest publication date and freshness โ€” **keyless** |
353
+ ## Quotas and billing
504
354
 
505
- ---
355
+ - The free plan includes **300 requests in total** (they do not reset). Paid plans have monthly quotas; see [pricing](https://allratestoday.com/pricing).
356
+ - Keyless methods (`officialRates`, `officialSources`, `symbols`) do not use your quota.
357
+ - History is billed by volume: a `1y` window is 12 calls; `7d`, `30d` and `1d` are 1. `forDate` and `timeSeries` make one history request per symbol.
358
+ - Your current usage is in the [dashboard](https://allratestoday.com/profile). When you run out, calls fail with `429` and `details` says when the quota resets.
506
359
 
507
- ## ๐Ÿงช Testing
360
+ ## Compatibility
508
361
 
509
- Run isolated unit tests (no API calls needed):
362
+ - **Node.js 18 or later** (uses the built-in `fetch` and `AbortSignal.timeout`). Other runtimes with those globals (Deno, Bun, edge workers, modern browsers) should work but are not part of the test suite. In browsers, use keyless methods only (see above).
363
+ - **ESM and CommonJS** builds with an `exports` map:
510
364
 
511
- ```bash
512
- npm test
365
+ ```js
366
+ import AllRatesToday, { AllRatesTodayError } from '@allratestoday/sdk'; // ESM / TypeScript
367
+ const { default: AllRatesToday } = require('@allratestoday/sdk'); // CommonJS
513
368
  ```
514
369
 
515
- Run tests with coverage report:
370
+ - **TypeScript** types are bundled (`dist/index.d.ts`); no `@types` package needed.
371
+ - **Bundlers**: zero dependencies and `sideEffects: false`, so it tree-shakes.
516
372
 
517
- ```bash
518
- npm run test:coverage
519
- ```
373
+ ## TypeScript
520
374
 
521
- Watch mode for development:
375
+ All request and response types are exported:
522
376
 
523
- ```bash
524
- npm run test:watch
377
+ ```ts
378
+ import type {
379
+ AllRatesTodayOptions, RequestOptions, AllRatesTodayErrorCode,
380
+ LatestOptions, LatestResponse, RateResponse, AuthRateResponse,
381
+ ConvertOptions, ConvertResponse, ForDateOptions, ForDateResponse,
382
+ TimeSeriesOptions, TimeSeriesResponse, HistoryPeriod, HistoryPoint, HistoricalRateResponse,
383
+ SymbolsResponse, CurrencyInfo,
384
+ OfficialRateOptions, OfficialRatesResponse, DatedOfficialRatesResponse, OfficialRateEntry,
385
+ OfficialSource, OfficialSourcesResponse,
386
+ } from '@allratestoday/sdk';
525
387
  ```
526
388
 
527
- ---
528
-
529
- ## ๐Ÿค– Use it from an AI agent
389
+ ## Use it from an AI agent
530
390
 
531
391
  The same rates are available to coding agents and LLM tools without a second integration:
532
392
 
533
393
  - **Claude Code plugin** (no key): `/plugin marketplace add AllRates-Today/claude-code-plugin` then `/plugin install allratestoday@allratestoday`
534
394
  - **Hosted MCP endpoint**: `https://allratestoday.com/api/mcp` โ€” Streamable HTTP, keyless, nothing to install
535
395
  - **Local MCP servers**: `npx -y @allratestoday/mcp-server` (live mid-market rates) ยท `npx -y @allratestoday/central-bank-mcp` (official central-bank rates)
536
- - **Trial key for agents, no signup**: `POST https://allratestoday.com/api/agent/keys` (send `Content-Type: application/json`) returns a 100-call key valid 7 days, enough to try history and dated tables; both MCP servers expose it as `request_trial_key`
537
- - **Daily Reference Rate** (first published 2026-10-12): one fixed USD table per business day, the 16:00 London median of minute samples, never revised โ€” keyless at `https://allratestoday.com/api/open/reference-rate`, `get_reference_rate` in the MCP server
396
+ - **Trial key for agents, no signup**: `POST https://allratestoday.com/api/agent/keys` (send `Content-Type: application/json`) returns a 100-call key valid 7 days; both MCP servers expose it as `request_trial_key`
397
+ - **Daily Reference Rate**: one fixed USD table per business day, the 16:00 London median of minute samples, never revised โ€” keyless at `https://allratestoday.com/api/open/reference-rate`
538
398
  - Machine-readable site guide: [llms.txt](https://allratestoday.com/llms.txt)
539
399
 
540
- ## ๐Ÿ”— Links
400
+ ## Testing, contributing and support
401
+
402
+ ```bash
403
+ npm test # unit tests: fetch is mocked, no network or API key needed
404
+ npm run test:coverage
405
+ npm run build
406
+ ```
541
407
 
542
- - [API Documentation](https://allratestoday.com/developers)
543
- - [Register (Free)](https://allratestoday.com/register)
544
- - [Dashboard](https://allratestoday.com/profile)
545
- - [Status](https://allratestoday.com/status)
546
- - [GitHub](https://github.com/AllRates-Today/exchange-rates-api)
408
+ - **Changelog**: [CHANGELOG.md](https://github.com/AllRates-Today/exchange-rates-api/blob/main/sdks/javascript/CHANGELOG.md)
409
+ - **Bugs and feature requests**: [GitHub issues](https://github.com/AllRates-Today/exchange-rates-api/issues)
410
+ - **Contributing**: pull requests welcome. Add or update tests in `src/index.test.ts` and run `npm test` before opening one.
411
+ - **Support**: [info@allratestoday.com](mailto:info@allratestoday.com) ยท [API docs](https://allratestoday.com/docs/) ยท [status](https://allratestoday.com/status)
547
412
 
548
- ## ๐Ÿ“œ License
413
+ ## License
549
414
 
550
415
  MIT