WebmeticDocs
Get startedDetectEnrichPersonalizeIntegrateManagePartnersDevelopers
Guides

Export visits daily

Export the previous day's visits to a CSV file every day, with no duplicate or missing visits.

In this guide you fetch the visits of identified companies through the API every night. At the end you'll have:

  • one CSV file per day with one row per visit: company, time, number of pages, duration, source, and first page
  • a script that runs every night and fetches the last few days again, so late visits are added and no visit appears twice
  • optionally the same rows in your own database, with session_id as the key

Before you start

  • Time: Setting it up takes about 15 minutes. Your first full file appears after the first day with identified visits.
  • Account: You need a Webmetic account with at least one website where your tracking code runs.
  • Access: You need a server or computer that runs scheduled jobs, for example with cron. It needs Python 3 with the requests library.
  • Rights: The script uses the Visitor Intelligence key, which only admins see under Menu > Integrations > API. If you're not an admin, ask an admin of your account for the key. The Quickstart describes the steps.
  • Plan: The API is part of every plan, including the Free plan and the trial. The requests on this page cost no credits.

Fetch one day

GET /company-sessions returns every company with its visits and their page views. Put the same date in from_date and to_date to get exactly that day:

curl "https://hub.webmetic.de/company-sessions?domain=your-website.com&from_date=2026-10-14&to_date=2026-10-14" \
  -H "Authorization: wmtc_YOUR_API_KEY"

Write the domain exactly as it appears in your account. Otherwise the API answers with 403 and API_KEY_NOT_AUTHORIZED.

If you send a date without a time, it stands for a whole day in UTC. In Germany that day starts at 1 a.m. in winter and 2 a.m. in summer.

A visit belongs to the day it started. To query by German time instead, see Date ranges and time zones.

Use fixed dates, not yesterday or -1 days. These values depend on when you send the request, so a second run gets a different range.

For every field, see Company, Session, and Page View.

Read every page

One page of the response holds up to 10,000 companies. If more companies visited your website, the API spreads them over several pages.

So keep fetching the next page until current is at least page_total. For details, see Pagination.

The visit endpoints of Visitor Intelligence answer one request per second per endpoint, and that includes /company-sessions. Wait a little over a second between two pages.

On status 429 or 5xx, wait and send your request again. After a 429, the Retry-After header gives the seconds until the next attempt.

On any other status from 400 up, a retry doesn't help. The script then stops and prints the error code. For details, see Errors.

Store each visit once

Every visit has a session_id that stays the same for that visit, even when page views are added later. So store visits with session_id as the key and overwrite existing entries.

  • Visits: The key is session_id. In your database you write with an upsert, in PostgreSQL for example with ON CONFLICT (session_id) DO UPDATE.
  • Companies: The key is company_id, and company_url serves as a second key. The same company appears on several days, and its scores reflect the time of the fetch.
  • Not a key: client_id changes daily and doesn't link visits across days.

Because no visit runs past midnight UTC, two adjacent days never return the same visit. Duplicates only appear when you fetch days again, and the session_id key catches them.

On the Free plan the script skips companies with blurred: true, because they are hidden and their visits only contain session_id and timestamp. For details, see Trial and Free plan.

Fetch the last few days again

Webmetic matches visits continuously, not in one run per night. If the company is already known, you see a visit in the dashboard after a few minutes.

If a company visits a website with Webmetic for the first time, it takes longer. The visit then appears with its original start time, which puts it on a past day.

Values such as the time on the last page can also change after the visit. But the API has no parameter for "changed since the last fetch".

So fetch the last few days again on every run and overwrite your entries. You choose how many days with DAYS_BACK in the script.

Set up the script

The script puts the steps above into practice. It fetches the last few days in UTC, stores each visit once, and rewrites each of these days into its own file.

  1. Save the script as webmetic_visits.py, for example in the folder /opt/webmetic.
  2. Replace your-website.com with your domain.
  3. Run it once by hand with WEBMETIC_API_KEY=wmtc_YOUR_API_KEY python3 webmetic_visits.py. The script creates the folder webmetic-visits with one file per day, for example visits-2026-10-14.csv.
"""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 = "your-website.com"
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()

Because each run rewrites the files of the fetched days, a second run does no harm. The date column is the day in UTC, and the landing_page column is the first page of the visit.

For a database, replace the file part with an upsert that uses session_id as the key.

Run it daily

Start the script every night after about 3 a.m. German time, because by then the previous UTC day is over, in summer too.

This cron line starts the script at 4:15 a.m.:

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

Because cron starts in the home folder, the line uses absolute paths. The time is in your server's time zone.

Call the Python interpreter that has requests installed. Instead of putting your key in the line, you can also keep it in a protected file.

Combine webhooks and the export

Recommended: webhooks if you want to react fast, for example with a task in your CRM. Webmetic sends an event a few minutes after the visit appears in the dashboard.

A webhook sends the visit as it looked when the event was created. It doesn't resend later page views, and a new webhook reports no earlier visits.

The daily export gives you complete visits and also fills in past days. You can combine both: webhooks for the fast reaction, the export for the complete record.

Without a script of your own, Webmetic emails you a list of the companies through notifications on fixed weekdays, optionally with a CSV file.

Next steps

On this page