This guide covers everything you need to use an exchange rate API in JavaScript: a typed Node.js client built on the standard fetch, historical lookups, currency conversion, error handling for every status code, and a Next.js route handler that keeps your fxapi key on the server while your React components get the rates they need.
Setup
Sign up for a free key and put it in your server environment, for example in .env.local for Next.js:
FXAPI_KEY=your-api-key
Node.js 18+ has fetch built in, so the examples below have no dependencies. Every request goes to https://api.fxapi.com/v1/... with the key in the apikey header.
TypeScript types for the currency API
The latest, historical and convert endpoints share one response shape (range, fluctuation and average have their own – see the docs):
// fxapi.ts
export type RateEntry = { code: string; value: number };
export type RatesResponse = {
meta: { last_updated_at: string };
data: Record<string, RateEntry>;
};
export class FxApiError extends Error {
constructor(
public status: number,
message: string,
public errors?: Record<string, unknown>,
) {
super(`${status}: ${message}`);
}
}
An exchange rate API client in JavaScript (Node.js)
// fxapi.ts (continued)
const BASE_URL = "https://api.fxapi.com/v1";
export async function fxGet<T = RatesResponse>(
path: string,
params: Record<string, string | number> = {},
): Promise<T> {
const url = new URL(BASE_URL + path);
for (const [k, v] of Object.entries(params)) url.searchParams.set(k, String(v));
const res = await fetch(url, {
headers: { apikey: process.env.FXAPI_KEY ?? "" },
signal: AbortSignal.timeout(10_000),
});
if (res.ok) return (await res.json()) as T;
const body = await res.json().catch(() => ({}));
switch (res.status) {
case 401:
throw new FxApiError(401, "Invalid API key");
case 403:
throw new FxApiError(403, "Endpoint or option not in your plan");
case 422:
throw new FxApiError(422, "Validation error", body.errors);
case 429:
throw new FxApiError(
429,
`Rate limit reached, monthly quota left: ${res.headers.get("X-RateLimit-Remaining-Quota-Month")}`,
);
default:
throw new FxApiError(res.status, "Unexpected response");
}
}
The 422 body includes an errors object keyed by parameter, which you can return to your own API callers. 429 means either the monthly quota is used up or – on the Free plan only – more than 10 requests were sent in a minute.
Get latest exchange rates in Node.js
const latest = await fxGet("/latest", { base_currency: "EUR", currencies: "USD,GBP,CHF" });
console.log(latest.meta.last_updated_at);
console.log(latest.data.USD.value); // USD per 1 EUR
Leave out currencies to get all 190+ currencies in one call, or filter by type=crypto or type=metal. Details: latest endpoint docs.
Historical rate on a specific date
const hist = await fxGet("/historical", {
date: "2024-12-31",
base_currency: "USD",
currencies: "EUR,JPY",
});
console.log(hist.data.JPY.value);
date can be any day from 1999-01-01 up to yesterday; values are end-of-day rates in UTC. For full series use /v1/range on the Professional plan – see the time series API.
Currency conversion in JavaScript
With the Basic plan or higher, /v1/convert returns converted amounts:
const conv = await fxGet("/convert", { value: 99.5, base_currency: "USD", currencies: "EUR,GBP" });
console.log(conv.data.EUR.value);
On the Free plan, compute the conversion from the latest rates and format it with Intl.NumberFormat, which knows the correct decimals for each currency:
const { data } = await fxGet("/latest", { base_currency: "USD", currencies: "JPY" });
const yen = 99.5 * data.JPY.value;
const fmt = new Intl.NumberFormat("en-US", { style: "currency", currency: "JPY" });
console.log(fmt.format(yen)); // e.g. "¥14,925" – illustrative
For invoices and ledgers, work in integer minor units or a decimal library rather than floats – see currency rounding and precision.
Next.js route handler: keep the API key server-side
Never call fxapi with your key from browser code. Anything shipped to the client – including NEXT_PUBLIC_* variables – is visible to every visitor, who can then spend your quota. Instead, add a route handler that runs on the server and caches the upstream response:
// app/api/rates/route.ts
import { fxGet, FxApiError } from "@/lib/fxapi";
let cache: { body: unknown; at: number } | null = null;
const TTL_MS = 60 * 60 * 1000; // hourly updates on the Basic plan
export async function GET() {
if (cache && Date.now() - cache.at < TTL_MS) {
return Response.json(cache.body, { headers: { "Cache-Control": "public, max-age=300" } });
}
try {
const latest = await fxGet("/latest", { base_currency: "USD" }); // one call, all currencies
cache = { body: latest, at: Date.now() };
return Response.json(latest, { headers: { "Cache-Control": "public, max-age=300" } });
} catch (err) {
if (cache) return Response.json(cache.body); // serve stale rates instead of failing
const status = err instanceof FxApiError ? 502 : 500;
return Response.json({ error: "Rates unavailable" }, { status });
}
}
Your React components then call /api/rates and never see the key. The same pattern works in Express, Fastify, Cloudflare Workers or AWS Lambda: read the key from the environment, call fxapi from the server, cache, and return only what the client needs.
Serverless functions start cold and lose in-memory state, so for high traffic use a shared cache (Redis, Vercel KV, Cloudflare KV) as shown in the caching guide.
Caching and quota
- Match the TTL to your plan’s update interval: daily on Free, hourly on Basic, 60 seconds on Professional and Enterprise (pricing).
- One request, all currencies: fetch everything against one base and derive cross rates locally.
- Cache historical rates forever – a past date’s rate doesn’t change.
- Read the quota headers:
X-RateLimit-Remaining-Quota-MonthandX-Costare on every response;/v1/statusis free to call.
Using the official JavaScript SDK
@everapi/fxapi-js is an ES module with one method per endpoint (status, currencies, latest, historical, convert, range). Each takes an object of query parameters and returns a promise of the parsed JSON:
npm install --save @everapi/fxapi-js
import FxAPI from "@everapi/fxapi-js";
const fxApi = new FxAPI(process.env.FXAPI_KEY);
const latest = await fxApi.latest({ base_currency: "USD", currencies: "EUR" });
const hist = await fxApi.historical({ date: "2024-12-31", currencies: "EUR" });
The SDK resolves with the JSON body for every status code, so check for an errors field (or use the fxGet helper above) if you need distinct handling for 401, 403, 422 and 429.
Next steps
- Full API documentation and the OpenAPI spec for generating types.
- Building a multi-currency storefront? Read e-commerce multi-currency pricing.
- Other languages: Python, PHP, Go (Golang).
The examples in this guide use fxapi, a foreign exchange rate API with a free plan of 300 requests a month – no credit card required.
Frequently asked questions
Can I call the exchange rate API directly from the browser?
Which Node.js version do I need?
fetch, so the examples need no dependencies. On older versions, install undici or node-fetch.How do I convert currencies in JavaScript on the Free plan?
/v1/latest with your source currency as base_currency and multiply the amount by the target rate. /v1/convert does this server-side but needs the Basic plan or higher.How should I format converted amounts?
Intl.NumberFormat with style: 'currency' – it applies the right number of decimals per currency, such as 0 for JPY. See currency rounding and precision.Is there an official JavaScript SDK?
@everapi/fxapi-js on npm. It wraps each endpoint in a method that returns a promise of the parsed JSON.