|
Download Sidecar/knowledge/12_KEEPA_API.md from smlflg/Sidecar-Workspace: direct link, hf CLI and curl.
- Browser
- Download file 8.55 kB
-
https://huggingface.co/smlflg/Sidecar-Workspace/resolve/main/Sidecar/knowledge/12_KEEPA_API.md
- Command line
-
hf download hf://smlflg/Sidecar-Workspace/Sidecar/knowledge/12_KEEPA_API.md
-
curl -L -o 12_KEEPA_API.md https://huggingface.co/smlflg/Sidecar-Workspace/resolve/main/Sidecar/knowledge/12_KEEPA_API.md
8.55 kB
| # Keepa API β Referenz fuer das Keeper System | |
| ## Was ist Keepa? | |
| Keepa ist ein Amazon-Preisverfolgungs-Service. Die API liefert historische und aktuelle Preisdaten fuer Amazon-Produkte. Wir nutzen sie als **einzige Datenquelle** fuer Preis-Monitoring und Deal-Erkennung. | |
| **Unser Plan:** Nur `/product` und `/token` funktionieren. Die Endpoints `/query`, `/search`, `/bestsellers` geben 404/500 zurueck. Das ist kein Bug β unser API-Plan hat keinen Zugang zu diesen Endpoints. | |
| --- | |
| ## Endpoints die wir nutzen | |
| ### `/product` β Produktdaten abrufen | |
| **Kosten:** ~15 Tokens pro ASIN | |
| **Datei:** `src/services/keepa_api.py` β `KeepaAPIClient.query_product()` | |
| ```python | |
| # So rufen wir Produktdaten ab: | |
| products = self._api.query(asin, domain="DE", stats=90, history=True, offers=20) | |
| ``` | |
| **Was kommt zurueck:** | |
| | Feld | Typ | Beschreibung | | |
| |------|-----|-------------| | |
| | `title` | str | Produktname | | |
| | `csv` | list[list] | Preis-History-Arrays (Keepa-Format, siehe unten) | | |
| | `stats` | dict | Aggregierte Statistiken (90-Tage) | | |
| | `offers` | list | Aktuelle Angebote von Haendlern | | |
| | `rating` | float | Bewertung (1.0 - 5.0, manchmal *10) | | |
| | `categories` | list | Amazon-Kategorie-IDs | | |
| | `buyBoxPrice` | int | Buy-Box-Preis in Cents | | |
| ### `/token` β API-Status pruefen | |
| **Kosten:** 0 Tokens (gratis) | |
| ```python | |
| import httpx | |
| r = httpx.get(f"https://api.keepa.com/token?key={api_key}") | |
| # Response: {"tokensLeft": 150, "refillIn": 45, "refillRate": 5, ...} | |
| ``` | |
| NΓΌtzlich fuer Preflight-Checks und Monitoring. | |
| --- | |
| ## Keepa CSV-Format (das HerzstΓΌck) | |
| Keepa speichert Preise NICHT als einfache Zahlen, sondern als **verschachtelte Arrays**. Das ist das wichtigste Konzept: | |
| ``` | |
| csv[index] = [keepa_time, price_cents, keepa_time, price_cents, ...] | |
| ``` | |
| ### CSV-Index-Referenz | |
| | Index | Bedeutung | Beispiel | | |
| |-------|-----------|---------| | |
| | 0 | Amazon-Preis | Direkt von Amazon verkauft | | |
| | 1 | Marketplace Neu | Guenstigster neuer 3rd-Party | | |
| | 2 | Marketplace Gebraucht | Guenstigster gebrauchter | | |
| | 3 | Sales Rank | Verkaufsrang (KEIN Preis!) | | |
| | 4 | Listenpreis (UVP) | Herstellerpreis | | |
| | 5 | Collectible | Sammlerstueck | | |
| | 6 | Refurbished | Generalueberholt | | |
| | 7 | New FBA | Neuer Preis via Fulfillment by Amazon | | |
| | 8 | Lightning Deals | Blitzangebote | | |
| | 9 | Warehouse Deals | Amazon Warehouse | | |
| | 10 | New FBM Shipping | Versandkosten Marketplace | | |
| | 11 | **Buy Box** | Wer die Buy Box hat | | |
| | 12 | Used Like New | Gebraucht - Wie neu | | |
| | 13 | Used Very Good | Gebraucht - Sehr gut | | |
| | 14 | Used Good | Gebraucht - Gut | | |
| | 15 | Used Acceptable | Gebraucht - Akzeptabel | | |
| | 16 | **Rating** | Bewertung (Wert / 10.0) | | |
| | 17 | **Review Count** | Anzahl Bewertungen | | |
| | 18 | Buy Box Used | Buy Box fuer Gebrauchte | | |
| | 19 | Sales Rank Drops (30d) | Verkaufsrang-Aenderungen | | |
| ### Spezialwerte | |
| - **-1** = Nicht verfuegbar (z.B. Amazon verkauft das Produkt nicht direkt) | |
| - **-2** = Keine Daten (Keepa hat nie einen Preis erfasst) | |
| - **Preise in Cents!** β Immer durch 100 teilen fuer EUR/USD | |
| ### Unsere Preis-Prioritaet | |
| Wir suchen den "besten" aktuellen Preis in dieser Reihenfolge: | |
| ``` | |
| Amazon(0) > Buy Box(11) > New FBA(7) > New 3rd(1) > Used Like New(12) > Buy Box Used(18) > Warehouse(9) | |
| ``` | |
| Code: `keepa_api.py:466-474` | |
| --- | |
| ## Domain-IDs | |
| Keepa nutzt numerische IDs statt Laendernamen: | |
| | ID | Land | Amazon-Domain | | |
| |----|------|---------------| | |
| | 1 | US | amazon.com | | |
| | 2 | **GB** | amazon.co.uk | | |
| | 3 | **DE** | amazon.de | | |
| | 4 | **FR** | amazon.fr | | |
| | 5 | JP | amazon.co.jp | | |
| | 6 | CA | amazon.ca | | |
| | 8 | **IT** | amazon.it | | |
| | 9 | **ES** | amazon.es | | |
| | 10 | IN | amazon.in | | |
| **Fett** = Maerkte die wir aktiv ueberwachen (EU QWERTZ-Fokus). | |
| --- | |
| ## Token Bucket Rate Limiting | |
| Die Keepa API hat ein Token-Budget. Jeder API-Call kostet Tokens, und Tokens werden ueber Zeit aufgefuellt. | |
| ### Wie es funktioniert | |
| ``` | |
| βββββββββββββββββββββββββββββββββββββββββββ | |
| β Token Bucket (AsyncTokenBucket) β | |
| β β | |
| β tokens_available: 150 β | |
| β tokens_per_minute: 200 β | |
| β refill_interval: 60s β | |
| β β | |
| β API Call kommt rein (kostet 15) β | |
| β β tokens_available >= 15? β β | |
| β β tokens_available -= 15 β 135 β | |
| β β API Call ausfuehren β | |
| β β | |
| β Naechster Call (kostet 15) β | |
| β β tokens_available >= 15? β β | |
| β β Weiter... β | |
| β β | |
| β Nach 60s: tokens_available = 200 (Refill)β | |
| βββββββββββββββββββββββββββββββββββββββββββ | |
| ``` | |
| ### Token-Kosten pro Endpoint | |
| | Endpoint | Kosten | Unser Nutzungsfall | | |
| |----------|--------|--------------------| | |
| | `query` (product) | ~15 | Preis-Check, ASIN-Validierung | | |
| | `deals` | ~5 | Deal-Suche (404 auf unserem Plan) | | |
| | `category` | ~5 | Nicht genutzt | | |
| | `best_sellers` | ~3 | Nicht genutzt | | |
| ### Timeout-Verhalten | |
| ```python | |
| # Wenn keine Tokens verfuegbar: | |
| # 1. Warte bis zu 120 Sekunden | |
| # 2. Pruefe alle 5 Sekunden ob Tokens verfuegbar | |
| # 3. Nach 120s: TokenInsufficientError | |
| await self._token_bucket.wait_for_tokens(cost=15, max_wait=120.0) | |
| ``` | |
| --- | |
| ## Fehlerbehandlung | |
| | Exception | Bedeutung | Was tun | | |
| |-----------|-----------|---------| | |
| | `InvalidAsin` | ASIN ist nicht 10 Zeichen | ASIN pruefen | | |
| | `TokenLimitError` | Kein Token-Budget | Warten, spaeter erneut versuchen | | |
| | `TokenInsufficientError` | Timeout beim Warten auf Tokens | Intervall erhoehen | | |
| | `NoDealAccessError` | Deals-Endpoint nicht im Plan | Normal fuer uns β Fallback nutzen | | |
| | `KeepaAPIError` | Allgemeiner API-Fehler | Logs pruefen, ggf. API-Key checken | | |
| --- | |
| ## Deals API (Theorie β funktioniert NICHT auf unserem Plan) | |
| Falls wir mal upgraden: So wuerde die Deals API funktionieren: | |
| ```python | |
| deal_params = { | |
| "page": 0, | |
| "domainId": 3, # DE | |
| "includeCategories": [340843031], # Tastaturen | |
| "deltaPercentRange": [20, 90], # 20-90% Rabatt | |
| "currentRange": [1500, 30000], # 15-300 EUR (in Cents!) | |
| "hasReviews": True, | |
| "isFilterEnabled": True, | |
| "isRangeEnabled": True, | |
| } | |
| result = self._api.deals(deal_params, domain="DE") | |
| # Result: {"dr": [...deals...], "categoryNames": [...]} | |
| ``` | |
| ### Deal-Daten-Struktur | |
| Deals nutzen das gleiche `current[]` Array wie Products: | |
| - `current[0]` = Amazon-Preis (Cents) | |
| - `current[7]` = New FBA (Cents) | |
| - `current[1]` = New 3rd Party (Cents) | |
| - `current[16]` = Rating (/10) | |
| - `current[17]` = Review Count | |
| --- | |
| ## Unsere Fallback-Strategie | |
| Da die Deals API nicht funktioniert: | |
| ``` | |
| 1. Seed-ASIN-Pool laden (data/seed_asins_eu_qwertz.txt) | |
| 2. Jede ASIN via /product abfragen | |
| 3. Preis, Rating, Sales Rank extrahieren | |
| 4. Deal-Score berechnen (discount * rating * 1/rank) | |
| 5. Ergebnis indexieren (ES + Kafka + PostgreSQL) | |
| ``` | |
| Datei: `src/scheduler.py:594-608` (`_collect_seed_asin_deals`) | |
| --- | |
| ## Konfiguration | |
| In `.env`: | |
| ``` | |
| KEEPA_API_KEY=dein-api-key-hier | |
| ``` | |
| In `src/config.py`: | |
| ```python | |
| keepa_api_key: str = "" | |
| deal_source_mode: str = "product_only" # "product_only" oder "deals" | |
| deal_seed_asins: str = "" # Komma-getrennte ASINs | |
| deal_seed_file: str = "data/seed_asins_eu_qwertz.txt" | |
| deal_scan_interval_seconds: int = 3600 # 1 Stunde | |
| deal_scan_batch_size: int = 10 # ASINs pro Batch | |
| ``` | |
| --- | |
| ## Singleton-Pattern | |
| Es gibt genau EINEN KeepaAPIClient im System: | |
| ```python | |
| # In src/services/keepa_api.py | |
| _keepa_client: Optional[KeepaAPIClient] = None | |
| def get_keepa_client() -> KeepaAPIClient: | |
| global _keepa_client | |
| if _keepa_client is None: | |
| _keepa_client = KeepaAPIClient() | |
| return _keepa_client | |
| ``` | |
| Der Scheduler erstellt seinen eigenen Client (`self.keepa_client = KeepaAPIClient()`). | |
| --- | |
| ## Praxis-Tipps | |
| 1. **Immer Token-Status pruefen** bevor grosse Batch-Jobs laufen: `GET /token?key=...` | |
| 2. **Preise sind in Cents** β vergiss nie durch 100 zu teilen | |
| 3. **Rating ist manchmal *10** β `if rating > 10: rating /= 10` | |
| 4. **CSV-Arrays rueckwaerts lesen** fuer den aktuellsten Preis: `for i in range(len(arr)-1, 0, -2)` | |
| 5. **-1 und -2 sind keine Preise** β immer `if val > 0` pruefen | |
| 6. **Stats-Fallback nutzen** wenn CSV leer ist: `product["stats"]["current"][index]` | |