Visitors and traffic

The area Besucher answers three questions: how many people come and how that changes, where they come from and which source is any good, and who comes back. It has five tabs – Überblick, Herkunft, Qualität, Suche, Wiederkehr – and every number in it follows the rules on this page, in the dashboard, the app, the weekly report, the API and MCP alike. Interest over time (the tab Qualität) has its own guide: Traffic quality.

X3 counts without cookies before consent (Cookieless sessions). That makes some numbers impossible – nobody can know without an identifier whether a visit tomorrow is the same person as today – and X3 never pretends otherwise.

Counting words

MetricDefinitionUI label
Visitssessions, count(*)„Besuche“
Visitors per dayper local day: distinct visitor hashes plus sessions without a hash„Besucher pro Tag“
Average visitors per daysum of daily visitors ÷ complete days with quality ok; today never counts„Ø Besucher pro Tag“
Visitor-dayssum of daily visitors over the range (someone who comes on three days counts three times)„Besuchertage“ (folded away)
Share without adsvisits whose channel group is not paid ÷ visits„Anteil ohne Werbung“
Seriously interestedvisits with relevance ≥ the engaged+ threshold (30 by default)„Ernsthaft interessiert“

The rules every text follows (the dashboard copy lint enforces them, see Copy rules):

  • „Besuche“ for visits, „Besucher“ only for the daily de-duplicated count (Ø Besucher pro Tag, Besuchertage) and in prose without a number. A change is always counted in visits: „478 Besuche mehr über Werbung“, never „478 Besucher mehr“.
  • A range total of visitors is only ever called „Besuchertage“.
  • Interest is scored per visit: „73 Besuche waren ernsthaft interessiert“, never „Besucher waren interessiert“.
  • Google clicks are „Klicks“, never people.
  • Never: „Unique“, „eindeutige Besucher“, „Nutzer“, „neue Besucher“ (unknowable without consent), „Stammbesucher“, „treue Besucher“, „verfolgt“ or „getrackt“ (say „gezählt“), and „wiedererkannt“ unless the text names the consent it relies on.

Channel groups

„Organisch“ hides very different traffic. X3 splits every visit into one channel group, computed at read time from the stored channel, the entry attribution and the referrer host. Nothing stored is rewritten, so the groups work for both channel rule versions and for the whole history, and a changed host list applies to past visits immediately. Rules are evaluated in this order:

OrderKeyUIRule
1paidWerbungan ad click id or a paid utm_medium (cpc, paid, ppc, …), any channel; also the channel google_ads
2aiKI Assistentenchannel ai_assistant, a referrer host of the AI list, or an AI utm_source without utm_medium and utm_campaign
3searchSuchegoogle_organic, bing, search_other, and referrals from a host of the search list (DuckDuckGo, Yandex, Ecosia, Startpage, mail.ru, …)
4socialSoziale Netzwerkemeta, linkedin, tiktok, social_other without paid evidence, and app referrers such as com.linkedin.android
5emailE-Mailchannel email
6referralVerweisethe remaining referrals (also utm_medium=referral)
7directDirekttyped address, bookmark or an app that sends no origin
8otherSonstigethe rest
  • **„Kostenlos“ = every group except paid.** Cost cells of those groups read „keine Werbekosten“, never „kostenlos“ – free visits are not worthless, they are unpaid.
  • A Meta visit without click id or paid medium (a plain Instagram link) is social, not paid. The Meta channel and the Werbung group therefore differ by those visits.
  • Bot and agent traffic never counts as KI Assistenten.
  • The AI and search host lists are project settings (settings.traffic.aiHosts, settings.traffic.searchHosts, set with MCP project_update_settings; the dashboard has no page for them). A list you set replaces the default; a host matches itself and its subdomains, invalid entries are dropped.

Filters and breakdowns (Filters):

KeyMeaning
channel_groupone of the eight keys above
traffic=paidchannel group paid
traffic=freeevery group except paid („Ohne Werbung“); traffic=organic is the same, kept for compatibility
traffic=searchchannel group search
dimension=channel_groupbreakdown by channel group

Measuring start and data quality

A project often has older visits from before the tracking went live – imported from an earlier tool, or a go-live day mixed with test traffic. Comparing against them would show growth that never happened, so X3 finds the measuring start and labels every day.

  • Import sessions are visits without device, attribution, active time and visitor hash (channel other, device unknown, attr_from=none, 0 ms active). They are excluded from every count – totals, series, breakdowns and the daily rollups – unless the filter sets include_imported=true („Importierte Besuche einblenden“).
  • **tracking_live_since** is the first local day with at least 80 % hashed visits and at most 5 % import sessions. Charts start their x-axis there and draw a thin edge marker „Messbeginn“ with the tooltip „Vor dem … sind die Zahlen unvollständig (importierte Daten).“
  • Day quality (series[].quality in timeseries, days[] in the data-quality route):
QualityMeaning
before_trackingbefore the measuring start
importedbefore the measuring start and the day holds import sessions
unfinishedtoday – drawn dotted, left out of averages and rolling sums
okeverything else
  • A comparison period that reaches before the measuring start is „Vergleich unsicher“: the change engine answers with confidence: "low" and the sentence names the reason; Pulse waits („Ab … siehst Du hier den Vergleich zur Vorwoche.“).
GET /v1/projects/{project}/traffic/data-quality?preset=30d

Why a number changed

One change engine writes the „Warum?“ sentence for Pulse, Überblick, alerts and MCP (GET /v1/projects/{project}/analytics/changes, MCP analytics_changes):

  1. Δ = visits now − visits in the comparison period (the same number of days immediately before, weekday aligned; compare=previous_year for the year before). A change counts only if |Δ| ≥ max(kMin, 10 % of the previous value); otherwise the stable sentence („Ähnlich viele Besucher wie letzte Woche“).
  2. Level 1: Δ per channel group, ranked by absolute change – never by percent, so 3 → 6 visits never outranks 507 → 985. Groups below kMin in both periods are dropped; at most 5 movers.
  3. Level 2 for the top groups: the dimension whose strongest child explains most of the group's change (channel or campaign for Werbung – „vor allem Meta“ –, entry page for Suche and Direkt, channel for Soziale Netzwerke, referrer host for Verweise and KI Assistenten, then device and country). A child needs |Δ| ≥ max(5, 5 % of the total).
  4. Annotations within one day of the change are named („seit Kampagnenstart am 15.10.“).
  5. metric can also be engaged_plus (ernsthaft interessiert), hot_plus or leads (from the Leads list, never from the score).

The answer carries total, groups[], drivers[], annotations[], confidence, template, sentence_de / sentence_en and one line per mover. Claude and other MCP clients may rephrase the sentence, but only with the numbers in the answer.

Today vs. usual (GET …/analytics/baseline, MCP analytics_baseline): visits today up to now against the median of the same weekday up to the same time over the last 4 to 8 measured weeks. Before 4 weeks of data usual is null and availableAt says from when.

Return visits

Without cookies X3 recognises a visitor only on the same day. Over several days it can see returns only for visitors with consent and for people you already know. Each number therefore names its basis, and there is no overall return rate, no extrapolation and no „neue Besucher“:

LayerWhat is countedBasis and badge
R1 same dayvisitor-days with a visit that starts ≥ 30 minutes after the previous visit of the same hash endedall visitors, cookieless; „Mal kam jemand am selben Tag wieder“, share of visitor-days as subline
R2 with consentconsented visitor ids with a visit in the range and one on an earlier day (≤ 180 days)only visitors with consent („nur mit Einwilligung“); a rate only from 30 eligible visitors and 4 weeks of id data
R3 known peopleleads whose journey has a visit on an earlier day before the enquiryonly known people („nur bekannte Personen“); needs leads:read or leads:summary, off under the health preset
R4 visit chainsjourneys with visits on at least 2 local days, by link reason (ad click, consent, enquiry)a lower bound („mindestens“)
  • The 30-minute gap is why R1 is small: repeat taps on the same ad or a reopened in-app browser within minutes are not people coming back.
  • The consent coverage shows how many visits could be recognised at all, per channel with at least 30 visits – consent is uneven (search visitors agree far more often than visitors from social ads), so R2 never stands for all visitors.
  • The weekly chart appears from 4 complete weeks after the measuring start; return metrics stop at 180 days.
  • Sessions filter visitor_type: same_day_repeat (the R1 rule), returning_consented, unknown („nicht erkennbar (ohne Einwilligung)“). There is deliberately no new.
GET /v1/projects/{project}/traffic/returning?from=2026-10-08&to=2026-10-21

Small cells

Every new aggregate of the area obeys the project's privacy settings (Einstellungen › Datenschutz & Erfassung): kMin (minimum visits for a rate, default 5, health preset 10) and cellSuppress (smallest count that is shown, default 3). Hiding one cell is not enough when it can be worked out from the others, so X3 protects whole tables (small-cell protection v2):

  • A count between 1 and cellSuppress − 1 is hidden and shown as „< 3“; 0 is shown.
  • Rows under 30 visits get no split by interest (shown as „–“).
  • X3 knows every relation an attacker could use: the bands of a row add up to its visits, children add up to their parent, „davon heiß“ lies within „ernsthaft interessiert“, Werbung + Ohne Werbung = Alle, and shares, rates and costs reveal their numerator. Until every hidden cell keeps at least 3 possible values, the smallest revealing neighbour is rounded to 5 („≈ 55“, tooltip „auf 5 gerundet, damit sich kein ausgeblendeter Wert ausrechnen lässt“), or the parent is hidden.
  • Shares, rates and costs of a rounded row come from the published value („≈ 5,6 %“).
  • The published cells are computed once per range and filter for the whole hierarchy, so Pulse, the dashboard, compare mode, CSV, the app and MCP show the same numbers. A footer names how many values are hidden or rounded.
  • Hidden values never drive a bar length: charts draw a fixed hatched placeholder.

In API answers a published count is { value, suppressed, rounded, unsplit }: value: null with suppressed: true reads „< 3“, rounded: true reads „≈“. Never compute a hidden value from totals.

The protection covers one table (one range and filter). Differences between two different filters (for example Gerät = Handy against all) are out of scope; views without access to single sessions (share links, reports, client views) use the health rules. See Privacy and GDPR.

Alerts

The tasks traffic_drop and traffic_spike open when yesterday's visits leave the band of the last 6 same weekdays (median ± 2.5 MAD, at least 20 expected visits; a drop also needs less than 70 % of the median). They stay quiet during the first days after the measuring start and while the tracking itself is down, and name the strongest mover from the change engine. Switch them off per project with settings.traffic.alerts=false (Tasks).

Copy rules

The rules of Counting words are data (COPY_LINT_RULES in @3x/core/insights) and run in the test suite over every German and English string of the dashboard, the shared view models and the server sentences. The same check guards the tier vocabulary of Traffic quality: the lowest tier is „Nicht interessiert“, never „Kalt“, and no tier is called „Lead“.

API and MCP

QuestionRESTMCP
Home card: today, Ø per day, change, search, same-day returnsGET …/traffic/summarytraffic_summary
Visitors per day and returns in the KPIsGET …/analytics/overview (visitors)analytics_overview
Series with comparison and splitGET …/analytics/timeseries?compare=previous&split=channel_groupanalytics_timeseries
Why did it changeGET …/analytics/changesanalytics_changes
Today vs. usualGET …/analytics/baselineanalytics_baseline
Weekday × time of dayGET …/traffic/heatmaptraffic_heatmap
Interest over time, compare sourcesGET …/traffic/qualitytraffic_quality
Who comes backGET …/traffic/returningtraffic_returning
Measuring start and day qualityGET …/traffic/data-qualitytraffic_data_quality

All routes take the analytics filter and need analytics:read. The tool list with scopes is in MCP tools.