MetalAPI
XAU3.997,89−1,54 %XAG53,936−1,51 %XPT1.554,47−0,83 %XPD1.047,03−0,44 %XCU6,5080+2,29 %WTI103,27+3,82 %BRENT111,70−1,30 %EUR1,120400−0,01 %GBP1,337500−0,01 %JPY0,0063761+0,14 %CHF1,213445−0,25 %TRY0,020125−0,12 %BTC80.329,75−0,55 %ETH2.329,36−1,13 %
Alle Artikel
GoldSilberHistorische DatenTutorial

Historische Gold- und Silberpreise per API: 25 Jahre Tagesdaten

Historische Gold- und Silberpreise seit 2000 per /historical, /timeframe, /change und /ohlc abrufen, jahresweise nachladen, lokal speichern und visualisieren.

Von MetalAPI Team··10 Min. Lesezeit

Welche Historie verfügbar ist

MetalAPI speichert pro Symbol und Tag einen Tagesbalken mit Eröffnungs-, Höchst-, Tiefst- und Schlusskurs. Für Gold und Silber sind das mehr als 25 Jahre Tagesdaten: genug, um langfristige Trends darzustellen, eine Preisregel an der Vergangenheit zu testen oder alte Rechnungen zum Preis des jeweiligen Tages neu zu bewerten.

KategorieSymboleTageshistorie seit
EdelmetalleXAU, XAG, XPT, XPD2000
Industriemetalle und EnergieXCU, ALU, ZNC, HRC, WTI, BRENT und weitere2000
Währungen57, darunter USD, EUR, GBP und JPY04.01.1999
Kryptowährungen13, darunter BTC und ETH2014

Jeder Tarif kann die gesamte Historie abfragen. Die Tarife unterscheiden sich nur darin, wie viele Tage eine einzelne Zeitraumanfrage umfassen darf. Datumsangaben haben das Format YYYY-MM-DD in UTC, und die Werte eines Tages sind dessen Schlusskurse. Ein Datum vor Beginn der Historie eines Symbols liefert Fehler 300 (no_results).

Metallpreise vor Oktober 2026 sind aus Frontmonats-Futures abgeleitet und können daher leicht vom Spotpreis desselben Tages abweichen. Denken Sie daran, wenn Sie alte Werte mit Spotpreisen aus einer anderen Quelle vergleichen.

Vier Endpunkte decken die Arbeit mit historischen Daten ab: /historical für einen Tag, /timeframe für einen Zeitraum, /change für die Veränderung zwischen zwei Tagen und /ohlc für die Kerze eines Tages. Die folgenden Abschnitte behandeln sie der Reihe nach und bauen dann ein kleines lokales Archiv mit einem Chart darauf.

Ein Tag: /historical

Für die Schlusskurse eines einzelnen Tages setzen Sie das Datum in den Pfad. /historical?date=YYYY-MM-DD bewirkt dasselbe, und /yesterday ist eine Abkürzung für den vorherigen Tag (UTC):

bash
curl "https://metalapi.com/api/v1/2024-01-15?currencies=XAU,XAG" \
  -H "X-API-Key: $METALAPI_KEY"

Die Antwort hat dieselbe Form wie bei /latest, ergänzt um historical: true. Dies sind die Beispielwerte aus der API-Referenz:

Beispielantwort
{
  "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 ist der Preis einer Feinunze Gold in der Basiswährung, XAU ihr Kehrwert. base und unit funktionieren wie gewohnt, base=EUR&unit=gram liefert also Euro pro Gramm für diesen Tag.

Wochenenden und Feiertage lassen die Anfrage nicht scheitern: Für einen Tag ohne Handel wird der Schlusskurs des letzten Handelstags zurückgegeben. Das date in der Antwort ist trotzdem der angefragte Tag. Wenn Sie wissen müssen, ob die Märkte geöffnet waren, führen Sie daher auf Ihrer Seite einen Handelskalender. Ein Datum in der Zukunft liefert Fehler 205 (invalid_date).

Zeiträume: /timeframe

Für eine Zeitreihe liefert /timeframe einen Eintrag pro Tag zwischen start_date und end_date (jeweils einschließlich), mit dem Datum als Schlüssel. /timeseries ist ein Alias, der die Antwort mit timeseries: true statt timeframe: true kennzeichnet.

bash
curl "https://metalapi.com/api/v1/timeframe?start_date=2026-10-19&end_date=2026-10-23&currencies=XAU" \
  -H "X-API-Key: $METALAPI_KEY"
Beispielantwort
{
  "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 }
  }
}

Anders als der Endpunkt für einzelne Tage füllt /timeframe keine Lücken. Spot-Metalle werden von Freitag 17:00 Uhr bis Sonntag 17:00 Uhr New Yorker Zeit nicht gehandelt, daher können Wochenenden und Börsenfeiertage im Ergebnis fehlen. Gehen Sie nicht von einem Eintrag pro Kalendertag aus. Und wenn Sie Symbole mit unterschiedlichen Handelskalendern mischen, etwa Gold und Bitcoin, prüfen Sie, ob ein Schlüssel existiert, bevor Sie ihn lesen.

Wie lang der Zeitraum einer einzelnen Anfrage sein darf, hängt vom Tarif ab:

TarifLängster Zeitraum pro AnfrageAbschnitt, der immer passt
Free30 TageEin Kalendermonat
Lite und höher365 TageEin Kalenderjahr

Das Limit bezieht sich auf den Abstand zwischen den beiden Daten. 2025-01-01 bis 2025-01-31 passt also in das Limit des Free-Tarifs und 2024-01-01 bis 2024-12-31 in das der kostenpflichtigen Tarife, selbst in einem Schaltjahr. Ein längerer Zeitraum liefert Fehler 207 (timeframe_too_long) mit einer upgrade_url. Jeder Aufruf von /timeframe zählt als eine Anfrage, egal wie viele Tage er zurückgibt.

Zwei Daten vergleichen: /change

Wenn Sie nur die Veränderung zwischen zwei Tagen brauchen, etwa die Wertentwicklung seit Jahresbeginn auf einem Dashboard, liefert /change Anfangskurs, Endkurs sowie absolute und prozentuale Veränderung, ohne dass Sie die Tage dazwischen herunterladen müssen:

bash
curl "https://metalapi.com/api/v1/change?start_date=2026-01-02&end_date=2026-10-23&currencies=XAU,XAG" \
  -H "X-API-Key: $METALAPI_KEY"
Beispielantwort (nur Gold)
{
  "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 }
  }
}

Lesen Sie den Schlüssel mit Präfix, USDXAU. Der Kehrwert-Schlüssel XAU bewegt sich in die entgegengesetzte Richtung, sein change_pct hat daher das umgekehrte Vorzeichen und einen anderen Betrag: In diesem Beispiel ist der Goldpreis um 7,43 % gefallen, während der Kehrwert um 8,03 % gestiegen ist. Den Prozentwert des Kehrwerts als Preisänderung anzuzeigen, ist ein Fehler, der leicht unbemerkt in die Produktion gelangt.

/change hat dieselben Zeitraumlimits wie /timeframe. Wie /historical verwendet der Endpunkt den letzten Schlusskurs, wenn ein Datum auf ein Wochenende oder einen Feiertag fällt.

Tageskerzen: /ohlc

/ohlc liefert Eröffnungs-, Höchst-, Tiefst- und Schlusskurs eines Tages in beiden Richtungen: USDXAU in Dollar pro Unze und XAU als Kehrwert. Ohne date oder mit date=latest erhalten Sie den bisherigen Balken des heutigen Tages.

bash
curl "https://metalapi.com/api/v1/ohlc?date=2026-10-23&currencies=XAU" \
  -H "X-API-Key: $METALAPI_KEY"
Beispielantwort
{
  "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 }
  }
}

Beachten Sie, dass im Kehrwert-Block das Hoch aus dem niedrigsten und das Tief aus dem höchsten Preis stammt, weil die Kehrwertbildung die Reihenfolge umkehrt. Verwenden Sie für Charts den Block USDXAU.

Jeder Aufruf umfasst einen Tag, ein Candlestick-Chart über ein ganzes Jahr kostet also eine Anfrage pro Handelstag. Für lange Charts sind Schlusskurse aus /timeframe weitaus günstiger. Nutzen Sie /ohlc für die jüngsten Kerzen oder um die Tagesspanne neben dem aktuellen Preis anzuzeigen.

Für Wochen- oder Monatskerzen über viele Jahre können Sie Balken auch aus gespeicherten Tagesschlusskursen bilden: Die Eröffnung ist der erste Schlusskurs des Zeitraums, der Schluss der letzte, und Hoch und Tief sind die Extremwerte der Schlusskurse. Intraday-Hochs und -Tiefs fehlen darin, kennzeichnen Sie solche Kerzen also als schlusskursbasiert. Für langfristige Charts eignen sie sich aber gut.

25 Jahre nachladen, ein Jahr pro Anfrage

Mit einem Limit von 365 Tagen pro Anfrage braucht die vollständige Gold- und Silberhistorie seit 2000 einen /timeframe-Aufruf pro Kalenderjahr: 27 Anfragen für 2000 bis 2026. Das passt problemlos in das Monatskontingent jedes kostenpflichtigen Tarifs. Im Free-Tarif wären mit 30-Tage-Fenstern mehr als 300 Anfragen nötig, etwa das Dreifache des Monatskontingents von 100. Ein vollständiges Nachladen ist dort also nicht praktikabel, einige Monate Historie dagegen schon.

Dieses Python-Skript lädt die Daten jahresweise, wartet das Limit pro Minute ab und schreibt die Preisschlüssel beider Metalle in SQLite. Speichern Sie es als backfill.py:

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"):
    """Daily closes between start and end (inclusive), keyed by date."""
    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:  # Limit pro Minute: warten, dann erneut versuchen (nicht berechnet)
            time.sleep(int(r.headers.get("Retry-After", "60")))
            continue
        if err["code"] == 300:  # keine Daten in diesem Zeitfenster
            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)

Das Skript kann gefahrlos erneut ausgeführt werden: insert or replace überschreibt bereits vorhandene Zeilen, ein unterbrochenes Nachladen lässt sich also einfach neu starten. Das laufende Jahr endet am heutigen Datum (UTC), weil ein end_date in der Zukunft Fehler 205 liefert. Die Limits pro Minute sind in den kleinsten Tarifen am niedrigsten (10 Anfragen pro Minute im Free-Tarif und 30 im Lite-Tarif). Deshalb beachtet das Skript bei Fehler 104 den Header Retry-After, statt abzubrechen.

Es speichert USDXAU und USDXAG, also die Preise, nicht die Kehrwert-Schlüssel. Wenn Sie die Historie in einer anderen Währung brauchen, übergeben Sie base oder speichern Sie zusätzlich die Dollarkurse der Währungen und rechnen lokal mit den Zeilen desselben Tages um.

Daten speichern und aktualisieren

Schlusskurse vergangener Tage ändern sich nicht. Eine lokale Kopie ist daher der günstigste und schnellste Weg, Charts und Berichte zu bedienen. Einige Regeln halten sie korrekt:

  • Zeilen nach Tag, Symbol und Basis schlüsseln. Die Tabelle oben verwendet (day, symbol, base) als Primärschlüssel. Wird ein Zeitraum zweimal abgerufen, werden Zeilen daher überschrieben statt dupliziert.
  • Festhalten, was Sie angefragt haben. Speichern Sie base und gegebenenfalls unit zusammen mit den Daten. Werte pro Gramm und pro Unze in einer Spalte zu mischen, ist eine klassische Ursache für Ausreißer in einem Chart.
  • Einmal täglich ergänzen. Nach dem Nachladen hält eine Anfrage pro Tag die Tabelle aktuell. Rufen Sie die letzten sieben Tage ab, damit ein ausgefallener Lauf oder ein verspäteter Schlusskurs automatisch nachgetragen wird.
  • Den heutigen Tag als vorläufig behandeln. Nur der Balken des laufenden Tages bewegt sich noch. Halten Sie ihn aus dem Archiv heraus oder überschreiben Sie ihn beim nächsten Lauf, wie es das Skript unten tut.
daily.py (einmal täglich nach 00:00 UTC ausführen)
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")

Bei einer Anfrage pro Tag kostet die Ergänzung etwa 30 Anfragen im Monat; das lässt sogar im Free-Tarif noch Spielraum. Für größere Archive funktioniert dasselbe Muster mit PostgreSQL, einer Parquet-Datei oder jeder Zeitreihendatenbank; nur die Funktion store ändert sich.

Prüfen Sie nachgeladene Daten, bevor Sie darauf aufbauen. Drei Prüfungen decken die meisten Probleme auf:

  • Zeilen pro Jahr. Jedes vollständige Jahr sollte für jedes Metall ungefähr eine Zeile pro Handelstag haben. Ein Jahr mit deutlich weniger Zeilen bedeutet, dass eine Anfrage fehlgeschlagen ist oder keine Daten geliefert hat.
  • Lücken. Ein normales Wochenende lässt drei Tage zwischen zwei Schlusskursen, ein langes Feiertagswochenende vier. Alles darüber verdient einen Blick, und das erneute Abrufen dieses Zeitfensters kostet nur eine Anfrage.
  • Sprünge. Eine Veränderung von einem Tag zum nächsten um etwa den Faktor 31 oder 1.000 deutet auf gemischte Einheiten hin, ein Wert nahe null auf einen versehentlich übernommenen Kehrwert-Schlüssel.

Die ersten beiden sind kurze Abfragen in SQLite:

checks.sql
-- Zeilen pro Jahr: Ein vollständiges Jahr hat etwa eine Zeile pro Handelstag
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;

-- Lücken von mehr als vier Tagen zwischen gespeicherten Schlusskursen
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;

Daten als Chart darstellen

Wenn die Daten lokal gespeichert sind, kosten Charts keine Anfragen. Dieses matplotlib-Skript zeichnet den Goldpreis in Dollar zusammen mit dem Gold-Silber-Verhältnis, also der Anzahl Unzen Silber, die man für eine Unze Gold erhält:

chart.py
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)

Auf einer Website liefern Sie die gespeicherte Zeitreihe aus Ihrem eigenen Backend aus und zeichnen sie im Browser. Ihr API-Schlüssel bleibt auf dem Server, und Seitenaufrufe verbrauchen kein Kontingent. Eine minimale Chart.js-Seite, die ein JSON-Array mit { day, price }-Objekten von Ihrem eigenen Endpunkt liest:

gold-chart.html
<canvas id="gold" height="320"></canvas>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.js"></script>
<script>
  // Ihr eigener Endpunkt, liefert [{ "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 Jahre Handelstage sind mehr als 6.000 Punkte pro Zeitreihe. Wenn Sie wie oben Punktmarkierungen und Animationen abschalten, bleibt der Chart flüssig; auf kleinen Bildschirmen können Sie die Daten auf Wochenschlusskurse ausdünnen.

Die Anleitung zu historischen Daten enthält eine JavaScript-Hilfsfunktion für Zeiträume und ein Beispiel für einen gleitenden Durchschnitt, und die API-Dokumentation listet alle Parameter auf. Wenn Sie mit KI-Assistenten arbeiten, kann der MCP-Server von MetalAPI dieselben Zeitreihen direkt im Chat abrufen. Für Schlüssel, Fehler und Caching im Allgemeinen beginnen Sie am besten mit dem Leitfaden zur Goldpreis-API. Zeitraumlimits und Kontingente vergleichen Sie auf der Preisseite.

Probieren Sie es mit Ihren eigenen Daten aus

Erstellen Sie ein kostenloses Konto und senden Sie Ihre erste Anfrage in unter zwei Minuten. 100 Anfragen pro Monat, keine Kreditkarte.

Weiterlesen