Letzten Monat stand ich vor einer Herausforderung: Ich musste mehrere Monate historische Order Book Daten von der Tardis API für eine Arbitrage-Strategieanalyse herunterladen. Nach mehreren Stunden Debugging, Rate-Limit-Problemen und einem kritischen 401 Unauthorized Fehler habe ich eine produktionsreife Lösung entwickelt, die ich in diesem Artikel teile.
Das Ausgangsproblem: ConnectionError und 401 Unauthorized
Mein erster Ansatz war simpel:
import requests
❌ FEHLERHAFT: Das führte zu mehreren Problemen
response = requests.get(
"https://api.tardis.dev/v1/exchanges/binance/orderbooks-snapshots",
params={"from": "2024-01-01", "to": "2024-01-31"}
)
print(response.json())
Das Ergebnis: ConnectionError: HTTPSConnectionPool(host='api.tardis.dev', port=443): Max retries exceeded und danach 401 Unauthorized - Invalid API Key. Der Grund: Tardis erfordert eine korrekte Authentifizierung, Paginierung und Ratenbegrenzung.
Tardis API Grundlagen
Tardis (tardis.dev) bietet historische Krypto-Marktdaten mit folgender Preisstruktur (Stand 2026):
| Plan | Monatlicher Preis | Enthaltene Credits | Preis pro Mio. API-Calls |
|---|---|---|---|
| Free Trial | $0 | 1.000 | - |
| Starter | $49 | 50.000 | $0.98 |
| Professional | $299 | 500.000 | $0.60 |
| Enterprise | Kontakt | Unbegrenzt | Verhandelbar |
Die korrekte Implementierung
Schritt 1: Authentifizierung und Session-Setup
import requests
import time
import os
from datetime import datetime, timedelta
class TardisDownloader:
"""Produktionsreifer Downloader für Tardis Order Book Snapshots"""
def __init__(self, api_key: str):
self.api_key = api_key
self.base_url = "https://api.tardis.dev/v1"
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
})
# Rate-Limiting: 10 Anfragen pro Sekunde
self.min_request_interval = 0.1
def _rate_limited_request(self, method: str, url: str, **kwargs) -> requests.Response:
"""Führt eine Anfrage mit automatischer Rate-Begrenzung aus"""
response = self.session.request(method, url, **kwargs)
# Rate-Limit-Handling
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 60))
print(f"⏳ Rate-Limit erreicht. Warte {retry_after}s...")
time.sleep(retry_after)
return self._rate_limited_request(method, url, **kwargs)
# Authentifizierungsprüfung
if response.status_code == 401:
raise PermissionError("❌ API-Schlüssel ungültig oder abgelaufen")
response.raise_for_status()
return response
def download_orderbook_snapshots(
self,
exchange: str,
symbol: str,
from_date: str,
to_date: str,
output_dir: str = "./data"
) -> list[str]:
"""Lädt Order Book Snapshots herunter"""
os.makedirs(output_dir, exist_ok=True)
downloaded_files = []
# API-Endpoint für Order Book Snapshots
url = f"{self.base_url}/exchanges/{exchange}/orderbooks-snapshots"
params = {
"symbol": symbol,
"from": from_date,
"to": to_date,
"limit": 1000 # Max pro Seite
}
page = 1
while True:
params["page"] = page
print(f"📥 Lade Seite {page} für {symbol}...")
response = self._rate_limited_request("GET", url, params=params)
data = response.json()
if not data.get("data"):
print(f"✅ Keine weiteren Daten auf Seite {page}")
break
# Speichere die Daten
filename = f"{exchange}_{symbol}_{from_date}_p{page}.json"
filepath = os.path.join(output_dir, filename)
with open(filepath, "w") as f:
f.write(response.text)
downloaded_files.append(filepath)
# Pause zwischen Anfragen
time.sleep(self.min_request_interval)
page += 1
return downloaded_files
✅ Verwendung
downloader = TardisDownloader(api_key="YOUR_TARDIS_API_KEY")
files = downloader.download_orderbook_snapshots(
exchange="binance",
symbol="BTCUSDT",
from_date="2024-06-01T00:00:00Z",
to_date="2024-06-30T23:59:59Z",
output_dir="./tardis_data"
)
print(f"📁 {len(files)} Dateien heruntergeladen")
Schritt 2: Batch-Download mit Parallelisierung
from concurrent.futures import ThreadPoolExecutor, as_completed
import pandas as pd
class BatchTardisDownloader(TardisDownloader):
"""Erweiterter Downloader für mehrere Symbole parallel"""
def __init__(self, api_key: str, max_workers: int = 5):
super().__init__(api_key)
self.max_workers = max_workers
def download_multiple_symbols(
self,
exchange: str,
symbols: list[str],
from_date: str,
to_date: str,
output_dir: str = "./batch_data"
) -> dict:
"""Lädt mehrere Symbole parallel herunter
Verwandte Ressourcen
Verwandte Artikel