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.640,11−0,17 %ETH2.354,39−0,06 %
Alle Artikel
GoldPythonTutorial

Goldpreis-API in Python: Preise abrufen, cachen und plotten

Goldpreis-API in Python nutzen: Live-Goldpreise mit requests abrufen, Fehler und Wiederholungen behandeln, Antworten cachen und Zeitreihen mit pandas plotten.

Von MetalAPI Team··7 Min. Lesezeit

Goldpreis-API in Python: Was Sie brauchen

Dieses Tutorial zeigt, wie Sie eine Goldpreis-API in Python nutzen, von der ersten Anfrage bis zu einem Client mit Cache und einem Chart der letzten zehn Monate. Es verwendet MetalAPI, eine JSON-REST-API für Edelmetalle, Industriemetalle, Energie, Währungen und Krypto, zusammen mit drei bekannten Bibliotheken: requests für HTTP, pandas für die Zeitreihe und matplotlib für den Chart. Jedes Snippet läuft mit Python 3.9 oder neuer.

Erstellen Sie ein kostenloses Konto und legen Sie im Dashboard unter API keys einen API-Schlüssel an. Schlüssel beginnen mit mk_live_ und werden nur einmal angezeigt. Kopieren Sie Ihren Schlüssel daher sofort in eine Umgebungsvariable. Der Free-Tarif erfordert keine Kreditkarte und umfasst 100 Anfragen pro Monat mit täglicher Preisaktualisierung, genug für alles in diesem Artikel.

Einrichtung
pip install requests pandas matplotlib

# macOS und Linux
export METALAPI_KEY="mk_live_your_key_here"

# Windows PowerShell
$env:METALAPI_KEY = "mk_live_your_key_here"

Lesen Sie den Schlüssel mit os.environ["METALAPI_KEY"] aus, statt ihn in ein Skript einzufügen. Fehlt die Variable, bricht dieser Aufruf mit einer deutlichen Fehlermeldung ab. Wenn Sie eine .env-Datei bevorzugen, lädt das Paket python-dotenv sie mit einem einzigen Aufruf von load_dotenv(). Tragen Sie .env in .gitignore ein, damit der Schlüssel nie in Ihr Repository gelangt.

So rufen Sie den Goldpreis in Python ab

Jeder MetalAPI-Endpunkt ist eine GET-Anfrage unter https://metalapi.com/api/v1. Der Schlüssel kann als Query-Parameter api_key, im Header Authorization: Bearer oder im Header X-API-Key übergeben werden. Verwenden Sie einen Header: Vollständige URLs landen in Logs, und manche requests-Exceptions geben die URL samt Query-String aus. Der Endpunkt /latest liefert aktuelle Preise, base legt die Währung fest, und currencies beschränkt die Antwort auf die Symbole, die Sie brauchen:

gold.py
import os

import requests

resp = requests.get(
    "https://metalapi.com/api/v1/latest",
    params={"base": "USD", "currencies": "XAU"},
    headers={"X-API-Key": os.environ["METALAPI_KEY"]},
    timeout=10,
)
data = resp.json()
if not data["success"]:
    raise SystemExit(f"MetalAPI error {data['error']['code']}: {data['error']['info']}")

price = data["rates"][data["base"] + "XAU"]
print(f"Gold: {price:,.2f} {data['base']} per troy ounce")

Die Antwort sieht so aus. Die Zahlen sind die Beispielwerte, die in der gesamten MetalAPI-Dokumentation verwendet werden, keine Live-Preise:

Beispielantwort
{
  "success": true,
  "timestamp": 1792834892,
  "date": "2026-10-24",
  "base": "USD",
  "rates": { "XAU": 0.0002501319, "USDXAU": 3997.89 },
  "rate_limit": { "limit": 100, "remaining": 99, "used": 1, "plan": "Free", "...": "..." }
}

Mit diesen Werten gibt das Skript Gold: 3,997.89 USD per troy ounce aus. rates["USDXAU"] ist der Preis einer Feinunze Gold in der Basiswährung. rates["XAU"] ist der Kehrwert, also Unzen Gold pro Dollar, und diesen Wert versehentlich zu lesen ist der häufigste Fehler in Goldpreis-Code. Weil der Schlüssel aus data["base"] gebildet wird, bleibt das Skript korrekt, wenn Sie zu base=EUR (Schlüssel EURXAU) oder base=INR wechseln. Ergänzen Sie unit=gram oder unit=kilogram für Preise pro Gramm oder Kilogramm. timestamp ist der Zeitpunkt der Preisdaten in Unix-Sekunden, nicht der Zeitpunkt Ihrer Anfrage.

Fehler der Goldpreis-API in Python behandeln

Ein fehlgeschlagener Aufruf liefert einen HTTP-Fehlerstatus und einen JSON-Body mit success: false sowie einem error-Objekt, das einen numerischen code, einen type und eine info-Meldung enthält. Verzweigen Sie anhand des Codes, niemals anhand des Meldungstexts. Nur bei wenigen Codes lohnt sich ein erneuter Versuch:

  • 104 (too_many_requests, HTTP 429): das Limit pro Minute. Warten Sie so viele Sekunden, wie der Header Retry-After angibt.
  • 500, 503 und Netzwerkfehler: mit exponentiellem Backoff wiederholen.
  • Alles andere, etwa 101 oder 102 (fehlender oder ungültiger Schlüssel), 105 (Monatskontingent aufgebraucht), 106 (Endpunkt nicht in Ihrem Tarif) und die 2xx-Validierungsfehler: Korrigieren Sie stattdessen Anfrage, Schlüssel oder Tarif.

Dieses kleine Modul kapselt diese Logik. Es verwendet eine einzige requests.Session wieder, sodass Verbindungen zwischen den Aufrufen offen bleiben, und löst eine Exception aus, die Sie auswerten können:

metalapi.py
import os
import time

import requests

API_URL = "https://metalapi.com/api/v1"
session = requests.Session()
session.headers["X-API-Key"] = os.environ["METALAPI_KEY"]


class MetalAPIError(Exception):
    def __init__(self, code, type_, info, retry_after=None):
        super().__init__(f"MetalAPI {code} ({type_}): {info}")
        self.code = code
        self.type = type_
        self.retry_after = retry_after


def _get(path, params):
    r = session.get(f"{API_URL}/{path}", params=params, timeout=10)
    try:
        data = r.json()
    except ValueError:  # kein JSON, z. B. eine HTML-Fehlerseite eines Proxys
        raise MetalAPIError(r.status_code, "http_error", r.reason) from None
    if not data.get("success"):
        err = data["error"]
        retry_after = r.headers.get("Retry-After")
        raise MetalAPIError(err["code"], err["type"], err["info"], int(retry_after) if retry_after else None)
    return data


def call(path, params=None, retries=3):
    """GET auf einen Endpunkt. Wiederholt nur bei Anfragelimits, Serverfehlern und Netzwerkausfällen."""
    for attempt in range(retries + 1):
        try:
            return _get(path, params or {})
        except (MetalAPIError, requests.RequestException) as exc:
            code = getattr(exc, "code", None)  # None bei Netzwerkfehlern
            if attempt == retries or (code is not None and code != 104 and code < 500):
                raise
            time.sleep(getattr(exc, "retry_after", None) or 2**attempt)

Der Backoff wartet 1, 2 und 4 Sekunden, bevor er aufgibt. Wiederholungen kosten hier nichts, denn Anfragen, die mit 104 oder einem Serverfehler abgelehnt werden, werden nicht berechnet. Validierungsfehler werden dagegen berechnet, ein weiterer Grund, sie nie in einer Schleife zu wiederholen. Die Anleitung zur Fehlerbehandlung listet jeden Code mit seinem HTTP-Status auf.

Goldpreise cachen und Anfragen sparen

Jeder Tarif aktualisiert die Preise in einem festen Intervall: einmal täglich im Free-Tarif, alle 30 Minuten im Lite-Tarif, alle 10 Minuten im Standard-Tarif und alle 60 Sekunden in Advanced und Pro. Die Aktualisierungen fallen auf Vielfache dieses Intervalls, gezählt ab 00:00 UTC. Eine erneute Abfrage vor der nächsten Aktualisierung liefert also dieselben Zahlen und verbraucht Kontingent ohne Nutzen. Dieser Cache behält jede Antwort bis zur nächsten Aktualisierung und liefert die letzten gültigen Preise aus, wenn die API nicht erreichbar ist:

prices.py
import math
import time

import requests

from metalapi import MetalAPIError, call

# Sekunden. Free: 86_400, Lite: 3_600 (stündlich passt zu den 1.000 Anfragen), Standard: 600
UPDATE_INTERVAL = 600
_cache = {}  # (base, symbols, unit) -> (expires_at, data)


def latest(base="USD", symbols="XAU,XAG", unit="troy_oz"):
    key = (base, symbols, unit)
    now = time.time()
    hit = _cache.get(key)
    if hit and now < hit[0]:
        return hit[1]
    try:
        data = call("latest", {"base": base, "currencies": symbols, "unit": unit})
    except (MetalAPIError, requests.RequestException):
        if not hit:
            raise
        _cache[key] = (now + 60, hit[1])  # letzte gültige Preise ausliefern, in einer Minute erneut versuchen
        return hit[1]
    next_update = (math.floor(now / UPDATE_INTERVAL) + 1) * UPDATE_INTERVAL
    _cache[key] = (next_update + 5, data)  # läuft einige Sekunden nach der nächsten Aktualisierung ab
    return data

latest("EUR", "XAU", "gram")["rates"]["EURXAU"] liefert den Euro-Preis pro Gramm. Fragen Sie alle benötigten Symbole in einem Aufruf ab: XAU,XAG,XPT,XPD kostet eine Anfrage, nicht vier. Bedenken Sie, dass dieser Cache im Arbeitsspeicher liegt. Ein per Cron gestartetes Skript beginnt jedes Mal mit einem leeren Cache, dort ist also der Zeitplan der Cache: Führen Sie das Skript höchstens einmal pro Aktualisierungsintervall aus. Um Ihr Kontingent im Blick zu behalten, rufen Sie /usage auf; dieser Endpunkt wird nie auf das Kontingent angerechnet.

Eine Goldpreis-Zeitreihe in pandas laden

Für die Historie liefert der Endpunkt /timeframe (Alias /timeseries) tägliche Schlusskurse zwischen start_date und end_date, mit dem Datum als Schlüssel. Eine Anfrage umfasst bis zu 30 Tage im Free-Tarif und 365 Tage in kostenpflichtigen Tarifen; ein längerer Zeitraum schlägt mit dem Fehler 207 fehl. Diese Funktion teilt jeden Zeitraum in zulässige Abschnitte auf und gibt eine pandas-Series zurück:

history.py
from datetime import date, timedelta

import pandas as pd

from metalapi import call


def gold_history(start, end, base="USD", max_days=30):  # max_days=365 in kostenpflichtigen Tarifen
    rates = {}
    chunk_start = start
    while chunk_start <= end:
        chunk_end = min(chunk_start + timedelta(days=max_days - 1), end)
        data = call("timeframe", {
            "start_date": chunk_start.isoformat(),
            "end_date": chunk_end.isoformat(),
            "currencies": "XAU",
            "base": base,
        })
        rates.update(data["rates"])
        chunk_start = chunk_end + timedelta(days=1)

    df = pd.DataFrame.from_dict(rates, orient="index")
    df.index = pd.to_datetime(df.index)
    return df[f"{base}XAU"].sort_index().rename(f"gold_{base.lower()}")


gold = gold_history(date(2026, 1, 1), date(2026, 10, 23))
gold.to_csv("gold_usd.csv")
print(f"{len(gold)} trading days, high {gold.max():,.2f}, low {gold.min():,.2f}")

Im Free-Tarif benötigen diese zehn Monate 10 Anfragen. Die Series hat eine Zeile pro Handelstag: Wochenenden und Börsenfeiertage fehlen, statt aufgefüllt zu werden. Rechnen Sie also mit weniger Zeilen als Kalendertagen. Wenn Sie für jeden Tag einen Wert brauchen, übernimmt gold.asfreq("D").ffill() den Schlusskurs vom Freitag über das Wochenende. Danach stehen die üblichen pandas-Werkzeuge zur Verfügung, etwa gold.pct_change() für tägliche Renditen oder gold.rolling(20).mean() für einen gleitenden Durchschnitt.

Vergangene Schlusskurse ändern sich nie, daher ist die CSV-Datei Ihr Cache für die Historie: Laden Sie sie beim nächsten Mal, statt denselben Zeitraum erneut abzufragen. Wie Sie die vollständige Historie seit 2000 in eine Datenbank laden, zeigt Historische Gold- und Silberpreise per API.

Den Goldpreis mit matplotlib plotten

Der Chart liest die CSV-Datei, das Neuzeichnen kostet also keine Anfragen. Er zeigt den täglichen Schlusskurs zusammen mit einem gleitenden 20-Tage-Durchschnitt:

plot.py
import matplotlib.pyplot as plt
import pandas as pd

gold = pd.read_csv("gold_usd.csv", index_col=0, parse_dates=True)["gold_usd"]
sma20 = gold.rolling(20).mean()

fig, ax = plt.subplots(figsize=(10, 5))
ax.plot(gold.index, gold, linewidth=1, label="Gold, USD per troy oz")
ax.plot(sma20.index, sma20, linewidth=2, label="20-day moving average")
ax.set_title(f"Gold price, {gold.index[0]:%d %b %Y} to {gold.index[-1]:%d %b %Y}")
ax.yaxis.set_major_formatter("{x:,.0f}")
ax.grid(alpha=0.3)
ax.legend()
fig.autofmt_xdate()
fig.tight_layout()
fig.savefig("gold.png", dpi=150)

Da der Index echte Datumswerte enthält, verteilt matplotlib die Punkte zeitlich korrekt, und der gleitende Durchschnitt beginnt am 20. Handelstag. Um Gold in einer anderen Währung darzustellen, rufen Sie gold_history mit base="EUR" auf: Die Kursschlüssel folgen demselben Muster, die Funktion funktioniert also unverändert.

Nächste Schritte

Sie haben jetzt, was die meisten Python-Integrationen brauchen: eine authentifizierte Anfrage, Fehlerbehandlung mit sinnvollen Wiederholungen, einen Cache, der dem Aktualisierungsintervall Ihres Tarifs folgt, und Historie in pandas. Entwickeln und testen Sie im Free-Tarif. Wenn Sie aktuellere Preise brauchen, wählen Sie einen Tarif nach Aktualisierungsintervall und Volumen: Ein Job, der alle 10 Minuten läuft, verbraucht etwa 4.500 Anfragen im Monat und passt damit in die 10.000 des Standard-Tarifs. Vergleichen Sie die Tarife auf der Preisseite.

Wenn die Preise auf einer Website erscheinen sollen, zeigt Goldpreis-API in JavaScript, wie Sie sie über eine Node.js-Route ausliefern, ohne Ihren Schlüssel preiszugeben.

Häufige Fragen

Gibt es eine kostenlose Goldpreis-API für Python?

Ja. Der Free-Tarif von MetalAPI umfasst 100 Anfragen pro Monat mit täglicher Preisaktualisierung, erfordert keine Kreditkarte, und jedes Snippet in diesem Artikel funktioniert damit. Die aktuellen Preise sind dort der Schlusskurs des Vortags, und eine /timeframe-Anfrage umfasst bis zu 30 Tage. Kostenlose Goldpreis-API erklärt den kostenlosen Tarif ausführlicher.

Warum ist der XAU-Kurs so eine kleine Zahl?

rates["XAU"] ist der Kehrwert: Feinunzen Gold pro Einheit der Basiswährung. Der Preis steht unter dem Schlüssel mit Präfix, rates["USDXAU"] für die Basis USD oder rates["EURXAU"] für die Basis EUR. Mit den Beispielwerten ergibt 1 ÷ 0,0002501319 wieder 3.997,89.

Wie erhalte ich den Goldpreis pro Gramm in Python?

Ergänzen Sie die Anfrage um unit=gram, zum Beispiel call("latest", {"base": "EUR", "currencies": "XAU", "unit": "gram"}). rates["EURXAU"] ist dann der Preis in Euro pro Gramm, und die Antwort enthält "unit": "gram". Vermeiden Sie unit=ounce: Das ist die Avoirdupois-Unze, nicht die Feinunze, in der Gold gehandelt wird.

Wie weit reicht die Goldpreis-Historie zurück?

Tägliche Goldpreise beginnen im Jahr 2000, und jeder Tarif kann die gesamte Historie abfragen. Die Tarife unterscheiden sich nur darin, wie viele Tage eine einzelne /timeframe-Anfrage umfassen darf: 30 im Free-Tarif und 365 in kostenpflichtigen Tarifen. Mit max_days=365 benötigt der gesamte Zeitraum von 2000 bis heute 27 Anfragen.

Wie oft sollte ein Python-Skript die API aufrufen?

Nicht öfter, als Ihr Tarif die Preise aktualisiert: im Free-Tarif einmal täglich nach 00:00 UTC; im Lite-Tarif stündlich, denn alle 30 Minuten wären etwa 1.490 Anfragen im Monat bei einem Kontingent von 1.000; im Standard-Tarif alle 10 Minuten. Rechnen Sie am Wochenende mit unveränderten Preisen, denn Spot-Gold wird von Freitag 17:00 bis Sonntag 17:00 New Yorker Zeit nicht gehandelt.

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