Zum Inhalt springen

Scan-Tracking & Analytics

Übersicht

Jeder Scan eines dynamischen QR-Codes erzeugt einen Scan-Record in deinem Workspace. Die Erfassung passiert vollständig am Cloudflare-Edge — ohne externe Tracking-Dienste, ohne Cookies, ohne dass die Original-IP jemals deine Datenbank erreicht.

Drei Wege, Scan-Daten zu konsumieren:

Aggregations-API

GET /v1/codes/:id/scans — Zeitreihen + Top-Länder/-Geräte/-OS für ein einzelnes Code-Objekt.

Webhooks (Echtzeit)

qr.scanned-Event pro Scan, HMAC-SHA256-signiert. Siehe Webhooks.

Dashboard

Visualisierung im qr3.app-Dashboard — kein API-Call nötig.


Welche Daten werden erfasst

Pro Scan wird ein Record in der Tabelle scans angelegt:

FeldQuelleBeispiel
idServer-generierte UUIDscn_d1f8…
code_idCode-ID des gescannten QR-Codesqr_a1b2c3d4
workspace_idWorkspace des Codesws_xxx
countryCloudflare cf.countryAT
regionCloudflare cf.regionVienna
cityCloudflare cf.cityWien
device_typeUser-Agent-Parsingmobile, tablet, desktop
osUser-Agent-ParsingiOS, Android, macOS, Windows
browserUser-Agent-ParsingSafari, Chrome, Firefox
refererHTTP Referer-Headerhttps://example.com/landing
languageAccept-Language-Header (erste Sprache)de
redirected_toTatsächliches Redirect-Zielhttps://example.com
ip_hashSHA-256(IP + täglicher Salt) — nicht reversibel8f3a…
scanned_atISO-8601-Timestamp2026-05-12T14:32:11.000Z

Geo-Lokalisierung am Edge

Alle Geo-Daten (country, region, city) stammen aus dem cf-Objekt von Cloudflare und basieren auf der Cloudflare-Geo-IP-Datenbank. Es werden keine externen Geo-IP-Services angefragt — die Auflösung passiert im selben Worker, der auch den Redirect ausführt.

Das hat drei Konsequenzen:

  1. Niedrige Latenz — kein zusätzlicher Hop, kein DNS-Lookup auf einen externen Provider.
  2. Datenschutz — die IP-Adresse verlässt Cloudflare nicht und wird nie an Dritte weitergegeben.
  3. Begrenzte Granularitätcity ist nicht immer verfügbar (z.B. bei VPNs, Mobilfunk-Carriern oder kleinen Regionen). Erwarte null-Werte und kalkuliere sie in deinen Dashboards ein.

Scan-Analytics abfragen

GET /v1/codes/:id/scans

Liefert aggregierte Analytics für einen einzelnen QR-Code über einen wählbaren Zeitraum.

Terminal-Fenster
curl "https://qr3.app/v1/codes/qr_a1b2c3d4/scans?days=30" \
-H "Authorization: Bearer qr3_sk_..."

Query-Parameter:

ParameterTypStandardBeschreibung
daysinteger30Zeitraum in Tagen (1–365)

Response (HTTP 200):

{
"data": {
"code_id": "qr_a1b2c3d4",
"short_code": "r7f3Kx",
"total_scans": 1842,
"period_days": 30,
"period_scans": 367,
"scans_by_day": [
{ "date": "2026-04-13", "count": 12 },
{ "date": "2026-04-14", "count": 18 }
],
"top_countries": [
{ "value": "AT", "count": 142 },
{ "value": "DE", "count": 98 },
{ "value": "CH", "count": 41 }
],
"top_devices": [
{ "value": "mobile", "count": 281 },
{ "value": "desktop", "count": 72 },
{ "value": "tablet", "count": 14 }
],
"top_os": [
{ "value": "iOS", "count": 158 },
{ "value": "Android", "count": 123 },
{ "value": "macOS", "count": 48 }
]
},
"meta": { "request_id": "req_xyz123" }
}

Antwort-Felder:

FeldBeschreibung
total_scansLebenslange Scan-Summe des Codes (unabhängig vom days-Fenster)
period_scansSumme der Scans im gewählten Zeitfenster
scans_by_dayTages-Buckets, aufsteigend nach Datum, leere Tage werden weggelassen
top_countriesTop 8 Länder im Zeitfenster, absteigend
top_devicesTop 5 Gerätetypen (mobile, tablet, desktop)
top_osTop 5 Betriebssysteme

Aggregation über mehrere Codes

Workspace-weite Summen (z.B. Scans pro Monat über alle Codes) stehen über GET /v1/workspaces/:id als scans_this_month-Feld zur Verfügung. Für Cross-Code-Analysen empfehlen wir das Dashboard oder einen periodischen Export.


Echtzeit-Konsum via Webhooks

Wenn du Scan-Events sobald sie passieren verarbeiten willst — etwa für Live-Dashboards, Lead-Tracking oder CRM-Integrationen — abonniere das qr.scanned-Event:

Terminal-Fenster
curl -X POST https://qr3.app/v1/webhooks \
-H "Authorization: Bearer qr3_sk_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/qr3",
"events": ["qr.scanned"],
"secret": "my-secret-key-min-16-chars"
}'

Payload:

{
"id": "evt_abc123xyz",
"type": "qr.scanned",
"created": "2026-05-12T14:32:11.000Z",
"data": {
"code_id": "qr_a1b2c3d4",
"short_code": "r7f3Kx",
"scan_id": "scn_d1f8...",
"country": "AT",
"device_type": "mobile",
"os": "iOS"
}
}

Vollständige Doku zu Signaturverifikation, Retry-Logik und Delivery-Logs in API → Webhooks.


DSGVO & Datenschutz

Scan-Records enthalten keine personenbezogenen IP-Adressen. Die ursprüngliche IP wird im Edge-Worker durch eine kryptografische Hash-Funktion (SHA-256) mit einem täglich rotierenden Salt ersetzt. Das bedeutet:

  • Keine Re-Identifikation auch bei Kenntnis des Algorithmus
  • Keine tagesübergreifende Korrelation desselben Besuchers
  • Original-IP erreicht nie die D1-Datenbank

Retention ist plan-abhängig:

PlanAufbewahrung
Free7 Tage
Pro90 Tage
Business / Agency1 Jahr
EnterpriseCustom (SLA)

Ein täglich laufender Cron-Job (purgeOldScans) entfernt ältere Records automatisch — total_scans am Code-Objekt bleibt davon unberührt.


Best Practices

  • Polling-Frequenz: Die Aggregations-API ist günstig, aber nicht für Sekundentakt gedacht. Für Live-Updates immer Webhooks bevorzugen und die API nur für Backfills / Dashboards verwenden.
  • Caching: Die Antwort enthält Zeitreihen — bei großen Fenstern (days=365) kann der Payload mehrere KB groß werden. Cache clientseitig mit kurzer TTL (z.B. 60 s).
  • Top-N-Limits: top_countries, top_devices, top_os sind serverseitig auf 8 bzw. 5 Einträge limitiert. Für tiefergehende Analysen exportiere die Rohdaten.
  • null-Werte tolerieren: country, region, city, referer, language können null sein. Behandle das in deiner Auswertung als „Unbekannt”.