Accessing Historical Data
Query daily prices back to 1999, fetch date ranges, compare two dates, get OHLC bars and analyse price trends.
What history is available
MetalAPI stores one daily bar (open, high, low and close) per symbol and day:
- Currencies since 1999
- Precious metals, industrial metals and energy since 2000
- Crypto since 2014
Metal prices before October 2026 are derived from front-month futures, so they can differ slightly from the spot price on the same day. All plans can query the full history. Plans only differ in how many days a single range request may cover.
A single day
Use the date as the path, the /historical endpoint with a date parameter, or /yesterday. The response contains that day’s closing prices (UTC). For GET /api/v1/2026-10-01?currencies=XAU,XAG:
{
"success": true,
"timestamp": 1790812800,
"historical": true,
"date": "2026-10-01",
"base": "USD",
"rates": {
"XAU": 0.0002583044893,
"USDXAU": 3871.4,
"XAG": 0.02097315436,
"USDXAG": 47.68
}
}Date ranges with /timeframe
/timeframe returns one entry per day between start_date and end_date, keyed by date. For GET /api/v1/timeframe?start_date=2026-10-19&end_date=2026-10-23¤cies=XAU:
{
"success": true,
"timeframe": true,
"start_date": "2026-10-19",
"end_date": "2026-10-23",
"base": "USD",
"rates": {
"2026-10-19": { "XAU": 0.0002537298285, "USDXAU": 3941.2 },
"2026-10-20": { "XAU": 0.0002526049889, "USDXAU": 3958.75 },
"2026-10-21": { "XAU": 0.0002517369852, "USDXAU": 3972.4 },
"2026-10-22": { "XAU": 0.0002506642603, "USDXAU": 3989.4 },
"2026-10-23": { "XAU": 0.0002501319446, "USDXAU": 3997.89 }
}
}Days without a closing price, such as weekends or market holidays, can be missing. Don't assume one entry per calendar day. /timeseries is an alias that returns "timeseries": true instead of "timeframe": true.
Ranges longer than your plan allows
A single request can span up to 30 days on Free and 365 days on paid plans. Longer spans return error 207. Split long ranges into chunks, where each chunk is one request:
const DAY = 86_400_000;
const MAX_DAYS = 365; // 30 on the Free plan
const iso = (d) => d.toISOString().slice(0, 10);
async function fetchRange(start, end, currencies) {
const rates = {};
let from = new Date(`${start}T00:00:00Z`);
const last = new Date(`${end}T00:00:00Z`);
while (from <= last) {
const to = new Date(Math.min(from.getTime() + MAX_DAYS * DAY, last.getTime()));
const params = new URLSearchParams({ start_date: iso(from), end_date: iso(to), currencies });
const res = await fetch(`https://metalapi.com/api/v1/timeframe?${params}`, {
headers: { "X-API-Key": process.env.METALAPI_KEY },
});
const data = await res.json();
if (!data.success) throw new Error(`${data.error.code}: ${data.error.info}`);
Object.assign(rates, data.rates);
from = new Date(to.getTime() + DAY);
}
return rates;
}
// Ten years of daily gold prices in 10 requests
const gold = await fetchRange("2016-01-01", "2025-12-31", "XAU");Changes between two dates
/change compares two days directly, so you don't have to download the range in between. For GET /api/v1/change?start_date=2026-09-30&end_date=2026-10-23¤cies=XAU:
{
"success": true,
"change": true,
"start_date": "2026-09-30",
"end_date": "2026-10-23",
"base": "USD",
"rates": {
"XAU": { "start_rate": 0.0002591613539, "end_rate": 0.0002501319446, "change": -0.000009029409258, "change_pct": -3.48409 },
"USDXAU": { "start_rate": 3858.6, "end_rate": 3997.89, "change": 139.29, "change_pct": 3.60986 }
}
}Use the prefixed keys such as USDXAU for price changes. The inverse keys move in the opposite direction.
Daily OHLC bars
/ohlc returns the open, high, low and close of one day, which is what you need for candlestick charts and daily ranges. For GET /api/v1/ohlc?date=2026-10-22¤cies=XAU:
{
"success": true,
"ohlc": true,
"date": "2026-10-22",
"base": "USD",
"rates": {
"USDXAU": { "open": 3962.1, "high": 4012.75, "low": 3951.3, "close": 3989.4 },
"XAU": { "open": 0.0002523914086, "high": 0.0002530812644, "low": 0.000249205657, "close": 0.0002506642603 }
}
}Historical conversions
Add date to /convert to convert at a past closing price. For example, this is what 10 troy ounces of silver were worth in euros on 1 October 2026:
GET /api/v1/convert?api_key=YOUR_API_KEY&from=XAG&to=EUR&amount=10&date=2026-10-01Analysing trends
The /timeframe response is easy to turn into a series. This example computes a 20-day simple moving average of the gold price:
// data = response of /timeframe?currencies=XAU
const series = Object.entries(data.rates)
.sort(([a], [b]) => a.localeCompare(b))
.map(([date, rates]) => ({ date, price: rates.USDXAU }));
function movingAverage(points, period) {
return points.slice(period - 1).map((point, i) => ({
date: point.date,
sma: points.slice(i, i + period).reduce((sum, p) => sum + p.price, 0) / period,
}));
}
const sma20 = movingAverage(series, 20);Closing prices of past days never change, so store them permanently instead of requesting them again. Only today’s bar is still moving.
Related guides
Ready to start building?
Create a free account to get your API key, or explore every endpoint in the API reference.