# Besuche täglich exportieren (https://webmetic.de/docs/api/anleitungen/besuche-taeglich-exportieren)

Mit dieser Anleitung rufen Sie jede Nacht die Besuche der erkannten Unternehmen über die API ab. Am Ende haben Sie:

- eine CSV-Datei pro Tag mit einer Zeile pro Besuch: Unternehmen, Uhrzeit, Zahl der Seiten, Dauer, Quelle und erste Seite
- ein Skript, das jede Nacht läuft und die letzten Tage erneut abruft, damit späte Besuche dazukommen und kein Besuch doppelt erscheint
- auf Wunsch dieselben Zeilen in Ihrer eigenen Datenbank, mit der `session_id` als Schlüssel

## Bevor Sie beginnen [#bevor-sie-beginnen]

- **Dauer**: Die Einrichtung dauert etwa 15 Minuten. Ihre erste volle Datei entsteht nach dem ersten Tag mit erkannten Besuchen.
- **Konto**: Sie brauchen ein Webmetic-Konto mit mindestens einer Website, auf der Ihr Tracking-Code läuft.
- **Zugang**: Sie brauchen einen Server oder Rechner, der geplante Aufgaben ausführt, zum Beispiel mit cron. Dort brauchen Sie Python 3 mit der Bibliothek `requests`.
- **Rechte**: Das Skript nutzt den Schlüssel **Visitor Intelligence**, den nur Admins unter [**Menü** > **Integrationen** > **API**](https://app.webmetic.de/?menu=api_details) sehen. Wenn Sie keine Admin-Rechte haben, bitten Sie einen Admin Ihres Kontos um den Schlüssel. Der [Schnellstart](https://webmetic.de/docs/api/schnellstart) beschreibt die Schritte.
- **Plan**: Die API ist in jedem Plan verfügbar, auch im Free-Plan und in der Testphase. Die Abfragen auf dieser Seite kosten keine Credits.

## Einen Tag abrufen [#einen-tag-abrufen]

`GET /company-sessions` liefert jedes Unternehmen mit seinen Besuchen und deren Seitenaufrufen. Wenn Sie in `from_date` und `to_date` dasselbe Datum eintragen, erhalten Sie genau diesen Tag:

```bash tab="cURL" tab-group="lang"
curl "https://hub.webmetic.de/company-sessions?domain=ihre-website.de&from_date=2026-10-14&to_date=2026-10-14" \
  -H "Authorization: wmtc_IHR_API_SCHLUESSEL"
```

```js tab="JavaScript" tab-group="lang"
const params = new URLSearchParams({
  domain: 'ihre-website.de',
  from_date: '2026-10-14',
  to_date: '2026-10-14',
});
const res = await fetch(`https://hub.webmetic.de/company-sessions?${params}`, {
  headers: { Authorization: 'wmtc_IHR_API_SCHLUESSEL' },
});
const data = await res.json();
```

```python tab="Python" tab-group="lang"
import requests

res = requests.get(
    "https://hub.webmetic.de/company-sessions",
    params={"domain": "ihre-website.de", "from_date": "2026-10-14", "to_date": "2026-10-14"},
    headers={"Authorization": "wmtc_IHR_API_SCHLUESSEL"},
)
data = res.json()
```

Schreiben Sie die Domain genau so wie in Ihrem Konto. Sonst antwortet die API mit `403` und `API_KEY_NOT_AUTHORIZED`.

Ein Datum ohne Uhrzeit steht für einen ganzen Tag in UTC. In Deutschland beginnt dieser Tag um 1 Uhr im Winter und um 2 Uhr im Sommer.

Ein Besuch gehört zu dem Tag, an dem er begonnen hat. Wie Sie stattdessen nach deutscher Zeit abfragen, steht unter [Zeiträume und Zeitzonen](https://webmetic.de/docs/api/zeitraeume-und-zeitzonen).

Nutzen Sie feste Daten, nicht `gestern` oder `-1 days`. Diese Werte hängen vom Zeitpunkt Ihrer Anfrage ab, und ein zweiter Lauf erhält dann einen anderen Zeitraum.

<Accordions>
<Accordion title="Beispielantwort (gekürzt)">

```json
{
  "pagination": [
    { "total": 1, "page_total": 1, "isFirst": true, "current": 1, "isLast": true, "page_size": 10000 }
  ],
  "result": [
    {
      "company_id": "41ea486a9c95",
      "company_name": "Bergquell Mineralbrunnen GmbH",
      "company_url": "www.bergquell.example",
      "relevance_score": 80.96,
      "sessions": [
        {
          "session_id": "01M4WQFX6YM2X5V8T4N6R1C0DZ",
          "timestamp": "2026-10-14T08:12:05.214000",
          "utm_source": "linkedin",
          "utm_medium": "social",
          "utm_campaign": "messe-2026",
          "session_duration": 309.0,
          "user_data": [
            { "timestamp": "2026-10-14T08:12:05.214000", "document_location": "https://ihre-website.de/preise?utm_source=linkedin&utm_medium=social&utm_campaign=messe-2026", "time_spent": 95.0 },
            { "timestamp": "2026-10-14T08:13:40.214000", "document_location": "https://ihre-website.de/abfuellanlagen", "time_spent": 150.0 },
            { "timestamp": "2026-10-14T08:16:10.214000", "document_location": "https://ihre-website.de/kontakt", "time_spent": 64.0 }
          ]
        }
      ]
    }
  ]
}
```

</Accordion>
</Accordions>

Alle Felder finden Sie unter [Company](https://webmetic.de/docs/api/datenmodell/company), [Session](https://webmetic.de/docs/api/datenmodell/session) und [Page View](https://webmetic.de/docs/api/datenmodell/page-view).

## Alle Seiten lesen [#alle-seiten-lesen]

Eine Seite der Antwort fasst bis zu 10.000 Unternehmen. Wenn mehr Unternehmen Ihre Website besucht haben, verteilt die API sie auf mehrere Seiten.

Fragen Sie deshalb die nächste Seite ab, bis `current` mindestens `page_total` ist. Mehr dazu unter [Paginierung](https://webmetic.de/docs/api/paginierung).

Die Besuchs-Endpunkte von Visitor Intelligence beantworten eine Anfrage pro Sekunde je Endpunkt, und das gilt auch für `/company-sessions`. Warten Sie zwischen zwei Seiten etwas länger als eine Sekunde.

Bei Status `429` oder `5xx` warten Sie und senden Ihre Anfrage erneut. Nach `429` nennt der Header `Retry-After` die Sekunden bis zum nächsten Versuch.

Bei jedem anderen Status ab `400` hilft eine Wiederholung nicht. Das Skript bricht dann ab und nennt den Fehlercode. Mehr dazu unter [Fehler](https://webmetic.de/docs/api/fehler).

## Besuche eindeutig speichern [#besuche-eindeutig-speichern]

Jeder Besuch hat eine `session_id`, die für diesen Besuch gleich bleibt, auch wenn später Seitenaufrufe dazukommen. Speichern Sie Besuche deshalb mit der `session_id` als Schlüssel und überschreiben Sie bestehende Einträge.

- **Besuche**: Der Schlüssel ist die `session_id`. In Ihrer Datenbank schreiben Sie mit einem Upsert, in PostgreSQL zum Beispiel mit `ON CONFLICT (session_id) DO UPDATE`.
- **Unternehmen**: Der Schlüssel ist die `company_id`, und die `company_url` dient als zweiter Schlüssel. Dasselbe Unternehmen erscheint an mehreren Tagen, und seine Scores gelten für den Zeitpunkt des Abrufs.
- **Nicht als Schlüssel**: Die `client_id` ändert sich täglich und verbindet keine Besuche über mehrere Tage.

Weil kein Besuch über Mitternacht UTC hinausreicht, liefern zwei benachbarte Tage nie denselben Besuch. Doppelte Besuche entstehen nur, wenn Sie Tage erneut abrufen, und der Schlüssel `session_id` fängt sie ab.

Im Free-Plan überspringt das Skript Unternehmen mit `blurred: true`, denn sie sind verborgen und ihre Besuche enthalten nur `session_id` und `timestamp`. Mehr dazu unter [Testphase und Free-Plan](https://webmetic.de/docs/konto/testphase-und-free-plan).

## Die letzten Tage erneut abrufen [#die-letzten-tage-erneut-abrufen]

Webmetic ordnet Besuche laufend zu, nicht in einem Durchlauf pro Nacht. Wenn das Unternehmen schon bekannt ist, sehen Sie einen Besuch nach wenigen Minuten im Dashboard.

Wenn ein Unternehmen zum ersten Mal eine Website mit Webmetic besucht, dauert es länger. Der Besuch erscheint dann mit seiner ursprünglichen Startzeit und damit an einem vergangenen Tag.

Auch Werte wie die Zeit auf der letzten Seite können sich nach dem Besuch noch ändern. Die API kennt aber keinen Parameter für „seit dem letzten Abruf geändert".

Rufen Sie deshalb bei jedem Lauf die letzten Tage erneut ab und überschreiben Sie Ihre Einträge. Sie legen die Zahl der Tage im Skript mit `DAYS_BACK` selbst fest.

## Skript einrichten [#skript-einrichten]

Das Skript setzt die Schritte oben um. Es ruft die letzten Tage in UTC ab, speichert jeden Besuch einmal und schreibt jeden dieser Tage neu in eine eigene Datei.

1. Speichern Sie das Skript als `webmetic_visits.py`, zum Beispiel im Ordner `/opt/webmetic`.
2. Ersetzen Sie `ihre-website.de` durch Ihre Domain.
3. Starten Sie es einmal von Hand mit `WEBMETIC_API_KEY=wmtc_IHR_API_SCHLUESSEL python3 webmetic_visits.py`. Das Skript legt den Ordner `webmetic-visits` mit einer Datei pro Tag an, zum Beispiel `visits-2026-10-14.csv`.

```python
"""Export the visits of the last days from Webmetic, one CSV file per UTC day."""
import csv
import os
import time
from datetime import datetime, timedelta, timezone
from pathlib import Path

import requests

API_URL = "https://hub.webmetic.de/company-sessions"
API_KEY = os.environ["WEBMETIC_API_KEY"]
DOMAIN = "ihre-website.de"
DAYS_BACK = 3  # your choice: how many past days each run fetches again
OUT_DIR = Path("webmetic-visits")
COLUMNS = ["date", "session_id", "timestamp", "company_id", "company_name", "company_url",
           "relevance_score", "pages", "session_duration", "utm_source", "utm_medium",
           "utm_campaign", "landing_page"]


def get(params, attempts=5):
    """GET with retries on 429, 5xx and network errors."""
    delay = 1
    for attempt in range(1, attempts + 1):
        try:
            res = requests.get(API_URL, params=params, headers={"Authorization": API_KEY}, timeout=120)
        except requests.RequestException:
            if attempt == attempts:
                raise
            time.sleep(delay)
            delay *= 2
            continue
        if res.status_code == 429 or res.status_code >= 500:
            if attempt == attempts:
                res.raise_for_status()
            retry_after = res.headers.get("Retry-After", "")
            time.sleep(int(retry_after) if retry_after.isdigit() else delay)
            delay *= 2
            continue
        if not res.ok:
            # other 4xx: a retry does not help, stop with the error code
            raise SystemExit(f"{res.status_code} {res.text}")
        return res.json()


def fetch_companies(first_day, last_day):
    """All companies with visits from first_day to last_day (both included)."""
    companies, page = [], 1
    while True:
        body = get({"domain": DOMAIN, "from_date": first_day.isoformat(),
                    "to_date": last_day.isoformat(), "page": page})
        companies += body["result"]
        info = body["pagination"][0]  # pagination is a list with one object
        if info["current"] >= info["page_total"]:
            return companies
        page += 1
        time.sleep(1.1)  # 1 request per second


def main():
    today = datetime.now(timezone.utc).date()
    first_day, last_day = today - timedelta(days=DAYS_BACK), today - timedelta(days=1)
    rows = {}  # session_id -> row, so every visit is written once
    for company in fetch_companies(first_day, last_day):
        if company.get("blurred"):
            continue  # hidden on the Free plan
        for session in company.get("sessions", []):
            pages = session.get("user_data", [])
            rows[session["session_id"]] = {
                "date": session["timestamp"][:10],
                "session_id": session["session_id"],
                "timestamp": session["timestamp"],
                "company_id": company["company_id"],
                "company_name": company.get("company_name"),
                "company_url": company.get("company_url"),
                "relevance_score": company.get("relevance_score"),
                "pages": len(pages),
                "session_duration": session.get("session_duration"),
                "utm_source": session.get("utm_source"),
                "utm_medium": session.get("utm_medium"),
                "utm_campaign": session.get("utm_campaign"),
                "landing_page": pages[0].get("document_location") if pages else None,
            }
    OUT_DIR.mkdir(exist_ok=True)
    for offset in range(DAYS_BACK):
        day = (first_day + timedelta(days=offset)).isoformat()
        day_rows = sorted((r for r in rows.values() if r["date"] == day), key=lambda r: r["timestamp"])
        tmp = OUT_DIR / f"visits-{day}.csv.tmp"
        with tmp.open("w", newline="", encoding="utf-8") as f:
            writer = csv.DictWriter(f, fieldnames=COLUMNS)
            writer.writeheader()
            writer.writerows(day_rows)
        tmp.replace(OUT_DIR / f"visits-{day}.csv")  # replaces the file of an earlier run
        print(day, len(day_rows), "visits")


if __name__ == "__main__":
    main()
```

Weil jeder Lauf die Dateien der abgerufenen Tage neu schreibt, schadet ein zweiter Lauf nicht. Die Spalte `date` enthält den Tag in UTC und die Spalte `landing_page` die erste Seite des Besuchs.

Für eine Datenbank ersetzen Sie den Teil mit den Dateien durch einen Upsert mit der `session_id` als Schlüssel.

## Täglich ausführen [#täglich-ausführen]

Starten Sie das Skript jede Nacht nach etwa 3 Uhr deutscher Zeit, denn dann ist der Vortag in UTC auch im Sommer abgeschlossen.

Diese Zeile für cron startet das Skript um 4:15 Uhr:

```bash
15 4 * * * cd /opt/webmetic && WEBMETIC_API_KEY=wmtc_IHR_API_SCHLUESSEL /usr/bin/python3 webmetic_visits.py >> export.log 2>&1
```

Weil cron im Home-Verzeichnis startet, nutzt die Zeile absolute Pfade. Die Uhrzeit gilt in der Zeitzone Ihres Servers.

Rufen Sie den Python-Interpreter auf, in dem `requests` installiert ist. Sie können Ihren Schlüssel statt in der Zeile auch in einer geschützten Datei ablegen.

## Webhooks und Export kombinieren [#webhooks-und-export-kombinieren]

Empfohlen: [Webhooks](https://webmetic.de/docs/api/webhooks), wenn Sie schnell reagieren wollen, zum Beispiel mit einer Aufgabe in Ihrem CRM. Webmetic sendet ein Ereignis wenige Minuten, nachdem der Besuch im Dashboard erscheint.

Ein Webhook sendet den Besuch so, wie er beim Anlegen des Ereignisses aussah. Er sendet spätere Seitenaufrufe nicht nach, und ein neuer Webhook meldet keine früheren Besuche.

Der tägliche Export liefert vollständige Besuche und füllt auch vergangene Tage nach. Sie können beides kombinieren: Webhooks für die schnelle Reaktion, den Export für den vollständigen Bestand.

Ohne eigenes Skript sendet Ihnen Webmetic über die [Benachrichtigungen](https://webmetic.de/docs/visitor-intelligence/benachrichtigungen) an festen Wochentagen eine Liste der Unternehmen per E-Mail, auf Wunsch mit einer CSV-Datei.

## Nächste Schritte [#nächste-schritte]

- [Session](https://webmetic.de/docs/api/datenmodell/session): Alle Felder eines Besuchs für weitere Spalten Ihrer Datei.
- [Webhooks](https://webmetic.de/docs/api/webhooks): Neue Besuche als Ereignis an Ihr System senden lassen.
- [Unternehmen anreichern](https://webmetic.de/docs/api/anleitungen/unternehmen-anreichern): Daten zu den Unternehmen hinter Domains aus Ihrem CRM abrufen.
