Eine Kopie aktuell halten
Der Change-Feed listet jedes neue Item, jede Änderung und jede Entfernung der Reihe nach. Mit einem Cursor gelesen, bekommen Sie jede Änderung genau einmal.
Der Change-Feed (/changes) ist der Weg, Ihre eigene Kopie eines Datasets aktuell zu halten: Statt alles neu zu
lesen, lesen Sie, was seit dem letzten Mal passiert ist. Er ist in den bezahlten Plänen enthalten; in Free antwortet er
mit 402.
So funktioniert es
Jede Änderung hat eine id, und ids werden nur größer. Sie fragen nach den Änderungen nach der letzten id, die Sie haben; die Antwort liefert die nächsten, die ältesten zuerst, und den Cursor, mit dem es weitergeht:
curl -H "Authorization: Bearer $PRICANA_KEY" "https://pricana.io/api/v1/changes?after=0&limit=1000"{
"changes": [
{
"id": 812,
"kind": "CHANGED",
"observedAt": "2026-10-06T21:03:00Z",
"price": 1399.0,
"previousPrice": 1499.0,
"changes": { "price": [1499.0, 1399.0] },
"item": { "id": 77, "title": "Lenovo ThinkPad X1 Carbon Gen 12", "url": "https://…", "currency": "EUR" }
}
],
"nextCursor": 812,
"hasMore": true
}- Beginnen Sie mit
after=0(dem Anfang der Historie Ihres Plans) oder mit einem gespeicherten Cursor. - Verarbeiten Sie die Änderungen. Übergeben Sie dann
nextCursoralsafterin der nächsten Anfrage. - Solange
hasMoretrueist, fragen Sie sofort wieder. Ist esfalse, sind Sie auf dem neuesten Stand: Speichern Sie den Cursor und fragen Sie später wieder (alle paar Minuten genügt).
So gelesen, bekommen Sie jede Änderung genau einmal, in der richtigen Reihenfolge, auch nach einer Pause: Der Feed reicht so weit zurück wie die Historie Ihres Plans (bei Pro zum Beispiel 365 Tage). Änderungen erscheinen etwa 30 Sekunden, nachdem sie passiert sind, im Feed.
Eine vollständige Sync-Schleife
import os, time, requests
KEY = os.environ["PRICANA_KEY"]
BASE = "https://pricana.io/api/v1"
def load_cursor() -> int:
try:
return int(open("pricana.cursor").read())
except FileNotFoundError:
return 0
def save_cursor(cursor: int) -> None:
with open("pricana.cursor", "w") as f:
f.write(str(cursor))
cursor = load_cursor()
while True:
r = requests.get(f"{BASE}/changes", params={"after": cursor, "limit": 1000},
headers={"Authorization": f"Bearer {KEY}"}, timeout=60)
if r.status_code == 429:
time.sleep(int(r.headers.get("Retry-After", "5")))
continue
r.raise_for_status()
page = r.json()
for change in page["changes"]:
apply(change) # your code: insert, update or mark removed
cursor = page["nextCursor"]
save_cursor(cursor) # after applying: a crash repeats changes, never skips them
if not page["hasMore"]:
time.sleep(300)Speichern Sie den Cursor erst, nachdem Sie die Änderungen angewendet haben, wenn möglich in derselben Transaktion.
Dann wiederholt ein Absturz schlimmstenfalls einige Änderungen, und eine Änderung zweimal anzuwenden schadet nicht, wenn
die id des Items der Schlüssel Ihrer Zeilen ist.
Änderungen anwenden
| kind | in Ihrer Kopie |
|---|---|
NEW | das Item einfügen (mit /items/{id} lesen, wenn Sie alle Felder brauchen) |
CHANGED | es aktualisieren: changes enthält [alt, neu] für jedes geänderte Feld; price ist der neue Preis |
REMOVED | als nicht mehr gelistet markieren |
RELISTED | als wieder gelistet markieren |
Änderungen enthalten id, Titel, Link und Site des Items. Das vollständige Item lesen Sie mit /items/{id}, viele auf
einmal mit /items?changedSince= (unten).
Die erste Kopie
Der Feed beginnt am Anfang der Historie Ihres Plans, und die kann lang sein. So beginnen Sie stattdessen frisch:
- Lesen Sie
/changes/recent?limit=1und merken Sie sich dieidder Änderung darin (gibt es keine, nehmen Sie0). Eine Änderung erscheint dort, wenn sie im Feed erscheint; nichts danach geht verloren. - Lesen Sie dann alle Items mit einem Export (oder mit
/itemsSeite für Seite). - Lesen Sie den Feed ab
after=<diese id>. Änderungen während des Exports kommen noch einmal; eine Änderung zweimal anzuwenden schadet nicht.
Einfacher und oft ausreichend: /items?changedSince=<Zeitpunkt der letzten Synchronisation>&status=all liefert jedes
Item, das sich seitdem geändert hat, in seinem aktuellen Stand. Entfernte Items sind mit gesetztem removedAt dabei:
Löschen Sie sie aus Ihrer Kopie. (Ohne status=all kommen nur gelistete Items, und eine so gepflegte Kopie erfährt nie
von Entfernungen.)
Nur ein Dataset
dataset=<id> grenzt den Feed auf eines Ihrer Datasets ein. Führen Sie für jeden Feed, den Sie lesen, einen eigenen
Cursor.
Geschickt statt abgefragt
Webhooks schicken dieselben Änderungen an Ihren Server, sobald sie passieren, in derselben Reihenfolge und mit denselben Objekten. Nutzen Sie sie, wenn Ihnen Änderungen binnen einer Minute geschickt werden sollen, und den Feed, wenn Sie lieber selbst abfragen.