APIで金・銀の過去価格を取得する:25年分の日次データ
/historical、/timeframe、/change、/ohlcで2000年までさかのぼる金・銀の日次価格を取得し、長期間を年単位で分割して保存し、チャートにする方法を解説します。
利用できる過去データ
MetalAPIは、シンボルごと・1日ごとに、始値、高値、安値、終値を含む日次バーを1本保存しています。金と銀については25年以上の日次データがあり、長期トレンドのチャート化、価格ルールの過去データでの検証、古い請求書の当日価格での再評価に十分な期間です。
| カテゴリ | シンボル | 日次履歴の開始 |
|---|---|---|
| 貴金属 | XAU、XAG、XPT、XPD | 2000年 |
| 産業用金属とエネルギー | XCU、ALU、ZNC、HRC、WTI、BRENTなど | 2000年 |
| 通貨 | USD、EUR、GBP、JPYを含む57通貨 | 1999-01-04 |
| 暗号資産 | BTC、ETHを含む13銘柄 | 2014年 |
すべてのプランで全期間の履歴を照会できます。プランによって異なるのは、1回の期間指定リクエストでカバーできる日数だけです。日付はUTCのYYYY-MM-DD形式で、各日の値はその日の終値です。シンボルの履歴が始まる前の日付を指定すると、エラー300(no_results)が返されます。
2026年10月より前の金属価格は期近の先物から算出されているため、同じ日のスポット価格とわずかに異なる場合があります。古い値を他のソースのスポット価格と比較する際はご注意ください。
過去データを扱うエンドポイントは4つあります。1日分の/historical、期間指定の/timeframe、2つの日の間の変動を返す/change、1日分のローソク足を返す/ohlcです。以下のセクションでは、これらを順に取り上げたうえで、小さなローカルアーカイブを構築し、その上にチャートを作成します。
特定の1日:/historical
1日分の終値を取得するには、日付をパスに指定します。/historical?date=YYYY-MM-DDでも同じ結果が得られ、/yesterdayはUTCの前日を取得するショートカットです:
curl "https://metalapi.com/api/v1/2024-01-15?currencies=XAU,XAG" \
-H "X-API-Key: $METALAPI_KEY"レスポンスは/latestと同じ形式で、historical: trueが加わります。以下はAPIリファレンスに掲載されている例の値です:
{
"success": true,
"timestamp": 1705276800,
"historical": true,
"date": "2024-01-15",
"base": "USD",
"rates": {
"XAU": 0.0004874244,
"USDXAU": 2051.6,
"XAG": 0.0431741646,
"USDXAG": 23.162
}
}USDXAUは金1トロイオンスの基準通貨建て価格で、XAUはその逆数です。baseとunitは通常どおり使えるので、base=EUR&unit=gramとすればその日の1グラムあたりのユーロ建て価格が返されます。
週末や祝日を指定してもリクエストは失敗しません。取引のない日については、直近の取引日の終値が返されます。ただし、レスポンスのdateはリクエストした日のままなので、市場が開いていたかどうかを知る必要がある場合は、取引カレンダーを自前で管理してください。未来の日付を指定すると、エラー205(invalid_date)が返されます。
期間指定:/timeframe
時系列データを取得するには、/timeframeを使います。start_dateからend_dateまで(両端を含む)の1日ごとのエントリを、日付をキーとして返します。/timeseriesはエイリアスで、レスポンスにtimeframe: trueの代わりにtimeseries: trueが付きます。
curl "https://metalapi.com/api/v1/timeframe?start_date=2026-10-19&end_date=2026-10-23¤cies=XAU" \
-H "X-API-Key: $METALAPI_KEY"{
"success": true,
"timeframe": true,
"start_date": "2026-10-19",
"end_date": "2026-10-23",
"base": "USD",
"rates": {
"2026-10-19": { "XAU": 0.0002403788, "USDXAU": 4160.1 },
"2026-10-20": { "XAU": 0.0002413943, "USDXAU": 4142.6 },
"2026-10-21": { "XAU": 0.0002409639, "USDXAU": 4150 },
"2026-10-22": { "XAU": 0.0002462812, "USDXAU": 4060.4 },
"2026-10-23": { "XAU": 0.0002495072, "USDXAU": 4007.9 }
}
}1日分のエンドポイントとは異なり、/timeframeは欠けている日を補完しません。スポットの金属はニューヨーク時間の金曜日17:00から日曜日17:00まで取引が止まるため、週末や市場の休場日は結果に含まれないことがあります。暦日ごとに1件のエントリがあるとは想定しないでください。また、金とビットコインのように取引カレンダーが異なるシンボルを組み合わせる場合は、キーが存在することを確認してから値を読み取ってください。
1回のリクエストで指定できる最長期間は、プランによって異なります:
| プラン | 1リクエストあたりの最長期間 | 常に収まる分割単位 |
|---|---|---|
| Free | 30日 | 1暦月 |
| Lite以上 | 365日 | 1暦年 |
上限は2つの日付の間隔で判定されるため、2025-01-01から2025-01-31まではFreeの上限内、2024-01-01から2024-12-31まではうるう年であっても有料プランの上限内に収まります。それより長い期間を指定すると、upgrade_url付きでエラー207(timeframe_too_long)が返されます。/timeframeの呼び出しは、返される日数に関係なく1回につき1リクエストとしてカウントされます。
2つの日付の比較:/change
ダッシュボードの年初来騰落率のように、2つの日の間の変動だけが必要な場合は、/changeを使えば間の日のデータをダウンロードせずに、開始レート、終了レート、変動額、変動率を取得できます:
curl "https://metalapi.com/api/v1/change?start_date=2026-01-02&end_date=2026-10-23¤cies=XAU,XAG" \
-H "X-API-Key: $METALAPI_KEY"{
"success": true,
"change": true,
"start_date": "2026-01-02",
"end_date": "2026-10-23",
"base": "USD",
"rates": {
"XAU": { "start_rate": 0.0002309682, "end_rate": 0.0002495072, "change": 0.000018539, "change_pct": 8.02665 },
"USDXAU": { "start_rate": 4329.6, "end_rate": 4007.9, "change": -321.7, "change_pct": -7.43025 }
}
}読み取るのは通貨コード付きのキーUSDXAUです。逆数のキーXAUは反対方向に動くため、そのchange_pctは符号が逆で、大きさも異なります。この例では金価格が7.43%下落した一方、逆数は8.03%上昇しています。逆数の変動率を価格の変動として表示してしまうのは、見落としやすいミスです。
/changeの期間の上限は/timeframeと同じです。また/historicalと同様に、日付が週末や祝日に当たる場合は直近の終値が使われます。
日次ローソク足:/ohlc
/ohlcは1日分の始値、高値、安値、終値を両方向で返します。USDXAUは1オンスあたりの米ドル建て価格、XAUはその逆数です。dateを省略するかdate=latestを指定すると、当日のその時点までのバーが返されます。
curl "https://metalapi.com/api/v1/ohlc?date=2026-10-23¤cies=XAU" \
-H "X-API-Key: $METALAPI_KEY"{
"success": true,
"ohlc": true,
"date": "2026-10-23",
"base": "USD",
"rates": {
"USDXAU": { "open": 4042.2, "high": 4050.2, "low": 3989.1, "close": 4007.9 },
"XAU": { "open": 0.000247390, "high": 0.000250683, "low": 0.000246901, "close": 0.000249507 }
}
}逆数のブロックでは、逆数を取ると大小関係が反転するため、高値は最も低い価格から、安値は最も高い価格から求められている点に注意してください。チャートにはUSDXAUのブロックを使います。
1回の呼び出しでカバーできるのは1日分なので、1年分のローソク足チャートを作るには取引日ごとに1リクエストが必要です。長期間のチャートには、/timeframeの終値を使う方がはるかに安く済みます。/ohlcは、直近のローソク足や、現在の価格の横にその日の値幅を表示する用途に使ってください。
長期間にわたる週足や月足が必要な場合は、保存した日次の終値からバーを作ることもできます。始値は期間中最初の終値、終値は最後の終値、高値と安値は終値の最大値と最小値です。日中の高値・安値は反映されないため、終値ベースであることを明記する必要がありますが、長期のチャートには十分です。
1年分ずつ25年分をバックフィルする
1リクエストあたり365日の上限があるため、2000年以降の金と銀の全履歴は、暦年ごとに1回の/timeframe呼び出しで取得できます。2000年から2026年までで27リクエストです。これはどの有料プランの月間クォータにも余裕で収まります。Freeプランでは30日単位で300回以上のリクエストが必要になり、月間クォータ100の約3倍に当たるため、全期間のバックフィルは現実的ではありません。数か月分の履歴であれば可能です。
次のPythonスクリプトは、年単位でリクエストを分割し、1分あたりの上限に達したら待機し、両方の金属の価格キーをSQLiteに書き込みます。backfill.pyとして保存してください:
import os
import sqlite3
import time
from datetime import datetime, timezone
import requests
API = "https://metalapi.com/api/v1"
session = requests.Session()
session.headers["X-API-Key"] = os.environ["METALAPI_KEY"]
db = sqlite3.connect("metals.db")
db.execute(
"""create table if not exists daily_close (
day text not null,
symbol text not null,
base text not null,
price real not null,
primary key (day, symbol, base)
)"""
)
def fetch_timeframe(start, end, symbols, base="USD"):
"""startからendまで(両端を含む)の日次終値。日付をキーとする。"""
while True:
r = session.get(
f"{API}/timeframe",
params={"start_date": start, "end_date": end, "currencies": ",".join(symbols), "base": base},
timeout=30,
)
data = r.json()
if data.get("success"):
return data["rates"]
err = data["error"]
if err["code"] == 104: # 1分あたりの上限:待機してから再試行(課金されない)
time.sleep(int(r.headers.get("Retry-After", "60")))
continue
if err["code"] == 300: # この期間にはデータがない
return {}
raise RuntimeError(f"{err['code']} {err['type']}: {err['info']}")
def store(rates, symbols, base="USD"):
rows = [
(day, s, base, values[f"{base}{s}"])
for day, values in rates.items()
for s in symbols
if f"{base}{s}" in values
]
db.executemany("insert or replace into daily_close values (?, ?, ?, ?)", rows)
db.commit()
return len(rows)
def backfill(first_year, symbols=("XAU", "XAG"), base="USD"):
today = datetime.now(timezone.utc).date()
for year in range(first_year, today.year + 1):
start = f"{year}-01-01"
end = min(f"{year}-12-31", today.isoformat())
n = store(fetch_timeframe(start, end, symbols, base), symbols, base)
print(f"{year}: {n} rows")
if __name__ == "__main__":
backfill(2000)このスクリプトは何度実行しても安全です。insert or replaceは既存の行を上書きするため、バックフィルが中断しても、単にもう一度実行すれば済みます。未来のend_dateを指定すると205が返されるため、当年分はUTCの今日の日付で終わります。1分あたりの上限は小さいプランほど低く(Freeで毎分10リクエスト、Liteで毎分30リクエスト)、そのためスクリプトはエラー104で失敗させずにRetry-Afterに従って待機します。
保存するのは価格であるUSDXAUとUSDXAGで、逆数のキーではありません。別の通貨建ての履歴が必要な場合は、baseを指定するか、各通貨の米ドル建て価格も保存しておき、同じ日の行を使ってローカルで換算してください。
データの保存と更新
過去の日の終値は変わらないため、ローカルにコピーを持つのが、チャートやレポートを提供する最も安くて速い方法です。正しく保つためのルールがいくつかあります:
- 日付、シンボル、基準通貨を行のキーにする。 上のテーブルは
(day, symbol, base)を主キーにしているため、同じ期間を2回取得しても行が重複せずに上書きされます。 - リクエストした条件を記録する。
baseと、使用している場合はunitもデータと一緒に保存してください。グラム単位とオンス単位の行を1つの列に混在させるのは、チャートに急なスパイクが現れる典型的な原因です。 - 1日1回追加取得する。 バックフィルの後は、1日1回のリクエストでテーブルを最新に保てます。直近7日分を取得すれば、実行漏れや確定が遅れた終値も自動的に補完されます。
- 当日分は暫定値として扱う。 値が動いているのは当日のバーだけです。アーカイブに含めないか、以下のスクリプトのように次回の実行で上書きしてください。
from datetime import datetime, timedelta, timezone
from backfill import fetch_timeframe, store
SYMBOLS = ("XAU", "XAG")
today = datetime.now(timezone.utc).date()
start = (today - timedelta(days=7)).isoformat()
end = (today - timedelta(days=1)).isoformat()
print(store(fetch_timeframe(start, end, SYMBOLS), SYMBOLS), "rows updated")1日1リクエストなら、追加取得のコストは月に約30リクエストで、Freeプランでも余裕があります。より大きなアーカイブでも、PostgreSQL、Parquetファイル、任意の時系列データベースで同じパターンが使えます。変更が必要なのはstore関数だけです。
バックフィルしたデータの上に何かを構築する前に、データを検証してください。次の3つのチェックで、ほとんどの問題を発見できます:
- 年ごとの行数。 完全な年であれば、各金属について取引日ごとにおよそ1行あるはずです。行数が極端に少ない年は、リクエストが失敗したか、データが返されなかったことを意味します。
- 欠落。 通常の週末なら2つの終値の間は3日、連休を含む週末なら4日空きます。それより長い空白は確認する価値があり、その期間の再取得は1リクエストで済みます。
- 急な変動。 前日比で約31倍や1,000倍の変動がある場合は単位の混在が、ゼロに近い値がある場合は逆数のキーの混入が疑われます。
最初の2つは、SQLiteの短いクエリで確認できます:
-- 年ごとの行数:完全な年には取引日ごとにおよそ1行がある
select substr(day, 1, 4) as year, symbol, count(*) as days from daily_close
where base = 'USD' group by year, symbol order by year, symbol;
-- 保存済みの終値の間に4日を超える空白がある箇所
select symbol, prev_day, day from (
select symbol, day, lag(day) over (partition by symbol order by day) as prev_day
from daily_close where base = 'USD'
) where julianday(day) - julianday(prev_day) > 4;データをチャートにする
データをローカルに保存しておけば、チャートの作成にリクエストは一切かかりません。次のmatplotlibスクリプトは、米ドル建ての金価格と、金1オンスで買える銀のオンス数である金銀比価をあわせてプロットします:
import sqlite3
from datetime import date
import matplotlib.dates as mdates
import matplotlib.pyplot as plt
db = sqlite3.connect("metals.db")
rows = db.execute(
"""select g.day, g.price, s.price
from daily_close g
join daily_close s on s.day = g.day and s.base = g.base and s.symbol = 'XAG'
where g.symbol = 'XAU' and g.base = 'USD'
order by g.day"""
).fetchall()
days = [date.fromisoformat(day) for day, _, _ in rows]
gold = [g for _, g, _ in rows]
ratio = [g / s for _, g, s in rows]
fig, (top, bottom) = plt.subplots(2, 1, sharex=True, figsize=(10, 6))
top.plot(days, gold, linewidth=1)
top.set_ylabel("Gold, USD per troy oz")
bottom.plot(days, ratio, linewidth=1)
bottom.set_ylabel("Gold/silver ratio")
bottom.xaxis.set_major_locator(mdates.YearLocator(base=5))
fig.tight_layout()
fig.savefig("gold-silver.png", dpi=150)ウェブサイトでは、保存した時系列データを自社のバックエンドから配信し、ブラウザで描画します。APIキーはサーバー上にとどまり、ページビューがクォータを消費することもありません。自社のエンドポイントから{ day, price }オブジェクトのJSON配列を読み込む、最小限のChart.jsページの例です:
<canvas id="gold" height="320"></canvas>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.js"></script>
<script>
// 自社のエンドポイント。[{ "day": "2026-10-23", "price": 4007.9 }, ...]を返す
fetch("/api/gold-history")
.then((res) => res.json())
.then((points) => {
new Chart(document.getElementById("gold"), {
type: "line",
data: {
labels: points.map((p) => p.day),
datasets: [{ label: "Gold, USD per troy oz", data: points.map((p) => p.price), pointRadius: 0, borderWidth: 1 }],
},
options: {
animation: false,
scales: { x: { ticks: { maxTicksLimit: 10 } } },
},
});
});
</script>25年分の取引日は、1系列あたり6,000ポイントを超えます。上の例のようにポイントマーカーとアニメーションを無効にすると、チャートの応答性を保てます。小さな画面では、週次の終値に間引くことも検討してください。
過去データガイドには、JavaScriptで長い期間を分割するヘルパーと移動平均の例が掲載されており、APIドキュメントにはすべてのパラメーターが記載されています。AIアシスタントを使っている場合は、MetalAPI MCPサーバーを使ってチャットから同じ時系列データを取得できます。キー、エラー、キャッシュ全般については金価格APIガイドから始めてください。期間の上限とクォータは料金ページで比較できます。
関連記事
金価格API:開発者のための完全ガイド
金価格APIの使い方を解説します。APIキーの取得、curl・JavaScript・Pythonからの呼び出し、レートの正しい読み方から、過去データ、エラー、クォータ、キャッシュの扱いまで網羅しています。
Webサイトに金価格のライブウィジェットを追加する方法
WordPress、Shopify、通常のHTMLに金価格のライブウィジェットを追加する方法。MetalAPIダッシュボードでシンボル、レイアウト、テーマを選び、2行のコードを貼り付けるだけです。
金のカラット別価格を解説:24K・22K・18Kと価格の計算方法
24K・22K・18Kの意味、916や750といった品位表示との関係、カラット別の金価格を1グラムあたりで計算する方法、金価格APIから取得する方法をわかりやすく解説します。