Skip to main content

What we collect

The Traceten snippet collects the following signals on each pageview. Every field listed here is necessary for AI traffic detection, session management, or the per-session figures we report back to you. We do not collect anything else. A few of the fields we store are derived on our servers from the signals below, rather than read from the visitor. The table says so where that applies.

Signals collected

We store most behavioral signals, such as scroll depth and mouse movement, only on the individual event record. Four things are also summarized per session: the number of pageviews, the total time your pages spent in the foreground of the visitor’s browser, the first page of the visit (the entry page), and its last page (the exit page). The entry and exit pages are page URLs we already store on the individual event records; summarizing them per session lets us show a visit as a journey with two ends rather than an undifferentiated list. Session records are keyed by visitor, so the sessions one visitor had on your site can be read together for as long as those records are retained (see Data retention). We rely on that to connect a conversion back to the AI referral that led to it, which is what this product exists to measure. A deletion request removes every session record for that visitor.

What we receive on the server

When the snippet sends an event to ingest.traceten.com, the Cloudflare Worker receives:
  • The payload above
  • The visitor’s IP address (used only for hashing; see below)

What we do NOT collect

  • Email addresses, names, or phone numbers, in our analytics stores. They are never written to the analytics database, and never written to a log. Two paths do handle an address before that point, and both are described below rather than glossed over. traceten.identify(). If you call it, the snippet reads the address you pass and sends it to our edge. The edge hashes it there, clears the plaintext before the request returns, and stores only the hash. The snippet does not read an address on any other code path, and never reads form fields to find one. The revenue paths. The Stripe, Shopify, Lemon Squeezy and Polar integrations take the customer email from the order, and the Payment API accepts an optional email field. Paddle is the exception: no email reaches us at all. Paddle does not put the buyer’s address on a transaction, and Traceten does not ask for one: it creates the notification destination with include_sensitive_fields set to false and never requests the customer record, so a Paddle payment is matched by the visitor id you pass through checkout or not at all. On the other paths the address is placed on our internal processing queue and hashed when a separate process consumes it, usually within seconds. It is not hashed in the same operation that receives it, so for that short window the plaintext exists on the queue. It is never indexed, never retrievable through any customer-facing surface, and ages off the queue on its own. See Data subject rights for what that means for an erasure request. One moment is not a trickle. Connecting a store or a payment account imports your existing orders so your revenue history is not blank on day one, and that import puts every one of those orders on the same queue in a single burst: up to 90 days of them, 60 for Shopify. Each carries the buyer’s email, each is hashed by the same consumer within seconds, and each ages off on the same schedule. Nothing different happens to them. What changes is the number of people briefly represented on that queue at once, which is why it is stated here rather than left to be inferred from the sentence above.
  • Form field contents. The snippet does not read input values.
  • Passwords or payment information. The snippet does not interact with sensitive form fields.
  • URL parameter values that carry personal identifiers. The value of any parameter whose name is a known personal-data parameter (email, token, access_token, id_token, code, state, and others) or contains token, secret or key is replaced with the literal REDACTED. This covers parameters in the query string and in the fragment after # (where sign-in callbacks often put tokens), in the page URL, the referrer and the clicked link. The snippet does it before the URL leaves the browser, and our ingestion edge applies the same rule to every event it receives, including events from server SDKs and direct API calls. To be precise about what that does and does not do: the parameter name itself remains in the URL, every other parameter is kept as it was, and a hash route such as #/pricing is left unchanged. We do not remove the query string from the page URL, because utm_source, utm_campaign and ref are read from it, as described in the table above.
  • Raw visitor IP addresses. A visitor’s IP address is hashed with HMAC-SHA-256 under a per-site key at the edge (Cloudflare Worker) before any storage. The raw value is never written to ClickHouse, logs, or disk. See the AI crawler exception below, which is not visitor data.
  • Location finer than the city. We derive the country and city from the IP address at the edge. We never derive from a visitor, or store, the region, postcode, street address, or GPS coordinates, and we never ask the browser for its location. Dashboard maps do not change this: when a map needs a point to draw, we look the city name up in a public list of city centre coordinates as the page is rendered. That point belongs to the city, not to the visitor: everyone in a city gets the identical point, it is never written to your data, and no request about a visitor is sent to any location service. See Third-party data and licences.
  • Network operator (ASN). Not recorded, for visitors or for AI crawlers.
  • Cross-site tracking data. The session and visitor cookies are first-party, scoped to your domain. They cannot be read by Traceten on any other site. Nothing we store links a visitor on your site to the same person on another site. (One value used purely to block abuse is shared across sites for one hour, and is never written to any record. See How IP hashing works.)

AI crawler IP addresses

This section is about AI crawlers fetching your pages, not about people visiting your site. If you have not installed AI crawler tracking, none of it applies to you. An AI crawler’s connection terminates at your server, not at ours, so its IP address is observable only there and your server reports it to us. We use it to check the crawler’s claimed identity against evidence the vendor publishes: first against the IP ranges the vendor publishes, and, if that does not match and the claimed vendor supports it, by a reverse DNS lookup. That lookup sends the address to Cloudflare’s public DNS resolver to see whether it resolves back to the vendor’s own hostname. The address is sent before we know whether it belongs to a crawler at all, on failed attempts as well as successful ones, unless a recent identical lookup is still cached. That cache holds the address itself, in memory on the machine that made the lookup, for up to one hour; it is never written to disk and never logged. We retain that raw IP address in exactly two cases:
  • It fell inside an IP range the vendor publishes for that specific crawler.
  • Its reverse DNS forward-confirmed to the vendor’s own domain.
In every other case we store an HMAC-SHA-256 hash of the address under a per-site key, the same construction we use for visitor IPs, and discard the address itself at the edge. That includes the case where the only evidence is that the address sits somewhere in the vendor’s wider network, which we treat as insufficient for retention even though we record it as a weaker form of verification. We do not derive or store the network operator (ASN), the country, or any other location data from a crawler’s IP address. Retention: individual crawl records, including any retained IP, are deleted after 90 days. What remains after that is aggregate counts, which contain no IP data at all. The 90-day limit is enforced by the database itself, and the hash-only rule is enforced by a storage constraint rather than by application code alone. Crawl records carry no visitor or session identifier, so the per-visitor access and deletion API cannot reach them: it has no key to look one up by. That is a limit of the API rather than of what is possible. The hash on a crawl record is deterministic per site, so a person who supplies the address itself can be matched, and we do that by hand: email privacy@traceten.com. See AI crawler records for how to raise one, and Crawl data and retention for the full field list.

How IP hashing works

This describes the visitor path. AI crawler IP addresses follow different rules, including two cases where we keep the raw address. See AI crawler IP addresses. When a visitor event arrives at our Cloudflare Worker:
  1. We extract the IP address from the connection.
  2. We derive a key unique to your site: HMAC-SHA-256(key = server_secret, message = site_id).
  3. We compute the hash: HMAC-SHA-256(key = your_site_key, message = IP).
  4. We store only the hash.
  5. The raw IP is discarded before the event is written anywhere.
Because the key is derived per site, the same IP address on two different sites produces two unrelated hashes. No hash we store can be correlated across sites, and the hash cannot be computed by anyone who does not hold the server secret. The hash is not rotated over time, so within one site the same IP produces the same hash for as long as the record is retained (730 days for raw events). We also derive the country and city from a visitor’s IP at this point. Those are stored; the IP is not. We derive neither from an AI crawler’s IP address.

A second derivation, used only to block abuse

Rate limiting has to recognise one client sending traffic to many sites at once, which a per-site hash cannot do. For that we compute a separate value: HMAC-SHA-256(key = shield_secret, message = IP + current hour). This value is the same for one IP across every site, for one hour. It exists to stop floods and denial-of-service traffic, and we handle it accordingly:
  • It is never written to an event, a log, or any database. It exists in memory for the life of the request and in a rate-limit counter that deletes itself.
  • It rotates every hour, so it cannot be used to follow anyone over time.
  • It is never combined with a site identifier, a visitor ID, or anything else we store.
No record we keep contains this value, so it does not appear in an access request or a deletion request. There is nothing to return or erase.

Cookies set

All cookies are first-party (scoped to your domain, not ours) and Secure on HTTPS connections. SameSite varies by cookie: _traceten_sid and _traceten_vid use SameSite=Lax so a visitor’s identity survives their first hit from an AI-answer link; _traceten_optout uses SameSite=Strict. _traceten_consent carries whatever your CMP sets, since your CMP writes it, not Traceten. See Cookies for the full breakdown. For more on cookies, see Cookies.

Opt-out and Do Not Track

The _traceten_optout cookie and the DNT: 1 header both put the snippet into opt-out mode. We treat them identically. To set the opt-out cookie programmatically:
What opt-out mode stops:
  • The persistent _traceten_vid visitor cookie. A transient in-memory ID is used instead, so visits cannot be joined across sessions.
  • The higher-entropy device signals: WebGL support, canvas entropy, approximate JavaScript heap size, timezone offset, and paste detection.
  • The _traceten_cart Shopify cart token.
  • Exit events, click tracking, and in-app navigation tracking.
  • Goals fired from the data-traceten-goal and data-traceten-scroll HTML attributes: neither the click listener nor the scroll observer is installed at all.
  • traceten.identify() and custom traceten.track() calls, which become no-ops.
What opt-out mode does not stop: the initial pageview is still sent. It carries the page URL, referrer, timestamp, the transient visitor ID, the session ID, the user agent, and the basic device signals in the table above (screen width and height, browser language, IANA timezone). We derive country and city from the request IP and hash the IP as we would for any pageview. The _traceten_sid session cookie is also still written. Screen size, language, and timezone are fingerprinting inputs in their own right. Opt-out mode reduces how identifiable a visitor is; it does not make them anonymous. If you need collection to stop outright, use a consent tool and set the consent state to denied. That is enforced on our side: no event is stored. To remove data we already hold for a visitor, use the deletion API.

Data retention

These are retention limits: how long we keep a record before deleting it. They are not a statement that a record of that age exists. A limit that has recently been raised takes the full period to be reached, because raising it does not restore anything already deleted. Retention depends on the kind of record:
  • Raw event data is stored at full granularity for 730 days (two years), then permanently deleted.
  • Session-level records, which are keyed by pseudonymous visitor and session identifiers, are kept for at least 730 days and at most 760 days. Deletion runs on monthly storage partitions, so the exact expiry date for a given session falls between those two bounds. Quote the 730 figure: it is the one we guarantee for any individual session.
  • Custom events you send with traceten.track(), with a goal attribute such as data-traceten-goal, or from a server SDK, are kept for 365 days (each one also writes a visitor step record, retained for 730 days, described below). This is shorter than the raw event data above, so a session from more than a year ago can show pageviews on its timeline with no custom events around them. Each one holds the event name you chose, any properties you attached, and the pseudonymous visitor and session identifiers. Every property value is scanned on arrival for email address, phone number, payment card and national ID patterns, and any match is replaced with a redaction marker. That scan is the same one on every route data can arrive by.
Key names are treated differently, and one route differs from the others. A property whose key is exactly email, phone, name, password, token, ssn, credit_card or card_number is dropped before storage when it arrives from a website snippet or a server SDK. It is not dropped on /v1/ingest/conversions, the authenticated ingestion API you can call with a server API key: there the value is scanned and redacted as everywhere else, but a property named email is stored under that name. Beyond that fixed list of eight, keys are not inspected for meaning on any route, so a key you invent is stored as you sent it, except that a key longer than 40 characters is dropped by the browser snippet before sending. Events arriving at the ingestion endpoints are capped at 20 properties with string values truncated to 256 characters; the account API rejects an over-limit payload outright instead of truncating it. Events sent from a browser are clamped further before they are sent at all, to 10 properties with values truncated to 200 characters. What those properties contain is decided by you, in your own code or in a data-traceten-goal-* attribute on your page, so treat anything you put in them as data you are choosing to retain for a year.
  • Visitor step records are kept for 730 days. Every pageview and every goal completion also writes one narrow row here, holding the pseudonymous visitor identifier, the session identifier, a timestamp, the URL path (query string and fragment removed) and, for a goal, its name. No property values are stored in it. It is what funnel questions are answered from, and it is retained for the same period as the raw event data above, so a funnel is not limited to a shorter window than the pageviews it counts. A deletion request removes it along with everything else.
  • Aggregated dashboard metrics (daily rollups such as pageview counts, revenue by AI source, top pages, and visits by country, device type and referring site) contain no visitor or session identifier column; distinct-visit counts are stored as approximate aggregate sketches derived from hashed session identifiers. One rollup, the daily visitor count, derives its sketch from the hashed visitor identifier instead, and is retained for 730 days rather than for the life of your account for that reason. Most are retained for the life of your account. They are what powers year-over-year trend reporting. Top-page rollups include the page URL as it was received. For a pageview sent by the tracking snippet, the values of personal-data query parameters have already been replaced in the browser as described above. That replacement happens only in the browser. A pageview sent from one of our server SDKs, or posted directly to our server-side events endpoint, is stored with the URL exactly as you sent it, including its query string; nothing on that path rewrites it. If you send pageviews from your own server, remove personal data from the URL before you send it.
  • Top pages by country is one of two aggregated rollups we do not keep for the life of your account (the daily visitor count above is the other). It records, per day, which page URLs were read from which country, and it is deleted after 730 days. It is kept for a shorter time than the rollups above precisely because pairing a page with a country is more revealing than either on its own.
  • Payment Link signals pair a provider’s transaction id with the visitor whose browser reported it, as described in the table above. Each one holds the site key, the provider, that transaction id, and the pseudonymous visitor and session identifiers. No email, no name, no amount. A signal is kept in two places:
    • On our internal processing queue, for up to about 25 hours. This applies to every signal we accept, whether or not it ever pairs with a payment and whether or not your site uses that provider.
    • In our database, for up to 60 minutes, and only when your site has connected that payment provider (for Dodo, when the site’s account holds a live server API key). A signal for any other provider is dropped when it is read off the queue and is never written to the database. A job running every two minutes deletes stored signals past the 60 minutes. The visitor and session identifiers are cleared as soon as the pairing is used or refused, which is normally within seconds. A pairing more than 30 minutes apart is refused. One case runs longer: if we have started moving an order onto the visitor and cannot finish, for example because our analytics store is unavailable, the stored signal is kept and retried until the move completes, so the order’s revenue is not lost. It keeps its visitor identifier while it waits, unless a deletion request removes it; the order is then completed without a visitor, as unattributed revenue.
    A deletion request clears the visitor and session identifiers from a stored signal immediately. It does not reach a copy still on the queue. That copy ages off within about 25 hours, and if it is read off the queue after the request it can be stored again, then removed by the 60-minute limit above. One residue outlives the window: when a signal corrected an order’s attribution, we keep the row that records how much was moved, because a refund months later needs it to subtract the right amount. That residue holds two provider identifiers, a date and a figure, and no visitor or session identifier.
  • Individual AI crawler records are stored for 90 days, then permanently deleted. This applies only if you have installed AI crawler tracking. For how we handle a crawler’s IP address inside that window, see AI crawler IP addresses.
  • AI crawler rollups (daily counts by crawler, by page, and by page and crawler) hold no IP address in any form, and no visitor or session identifier. We retain them for the life of your account, which is what keeps your crawl trends available after the 90-day records above expire.
  • AI visibility answers are stored for 90 days, then permanently deleted. This applies only if AI visibility is running for your account. Each record holds the verbatim answer an engine returned (up to 100,000 characters) and its citation list (up to 100 citations, each with a title up to 512 characters and a URL up to 2,048), alongside the prompt identifier, the engine, the exact model identifier, the tool configuration, the locale and the timestamp. Citation URLs have their query string and fragment removed before storage.
  • AI visibility mentions and citations are kept for 365 days. A mention holds a verbatim span of up to 500 characters around where your brand was matched, with its confidence score and how it was matched. A resolved citation holds the URL, its path and its domain. They outlive the answers above so that a year of trend data is not limited to the shorter window of the evidence behind it.
  • AI visibility brand rollups (daily counts per prompt, engine and brand) hold counts only, and are retained for the life of your account.
  • AI visibility citation rollups (daily counts per cited link) are retained for the life of your account and are not counts-only: each row keeps the cited URL, its path and its domain, because that is what a year-over-year citation trend is made of. Those links are third-party pages an engine chose to cite, not pages anyone on your site visited, but a URL can identify a person on its own. This is the only visibility record that has no expiry and holds more than counts.
None of the AI visibility record types carries a visitor identifier, a session identifier or an IP hash. They describe what an answer engine said when we asked it, not what anybody did on your site. That also means a per-visitor deletion request cannot reach them: there is no key to look one up by. Deleting the site or closing the account clears all of them, and for the two rollups that is the only thing that clears them.
An AI visibility answer is unredacted third-party prose. We store what the engine wrote, including its citation titles, without filtering it, and it can name people. Your own prompt text is stored too, exactly as you typed it, and is sent to the answer engines on every scan. Do not put personal data in a prompt. See AI answer engines for what leaves our infrastructure and who receives it.

Chart notes your team writes

Chart notes are not visitor data. They are text your own team types onto a site’s traffic chart, and we store them only because you write them. For each note we keep:
  • The text, with leading and trailing whitespace removed, up to 500 characters. A note containing control characters or text-direction overrides is refused.
  • The day it is pinned to, and when it was created and when it was last changed.
  • Who wrote it and who last changed it, as the identifiers our authentication provider gives us. Never a name or an email address. A note written with an API key is attributed to the team member who created that key.
  • How it was written and last changed: in the dashboard, with an API key (and which key), or through an AI assistant connected to your account.
Who can read a note. Everyone in your organization who can see the site, any of your API keys that can read the site, and any AI assistant you connect. When an assistant reads a note, the note text is sent to that assistant’s vendor under the vendor’s own terms. API keys and assistants are never given the identifiers of who wrote a note, only how it was written. How long we keep it. A note can only be pinned to a day inside the 730-day analytics retention window described above, today included. Once its day falls outside that window, our hourly deletion job deletes it within a day. A note is also deleted when someone on your team deletes it, and when its site or your account is erased, 30 days after you ask for that. When someone leaves. Removing a member from your organization leaves their notes in place with their identifier, and the dashboard labels them “Former team member”. If their user is deleted from our authentication provider, their identifier is replaced with the placeholder deleted-user on every note, on every account. Do not put personal data in a note. AI crawler records and AI visibility answers both stay at 90 days deliberately. The crawler screens read individual records only for the last few days, and every longer-range chart on them comes from the rollups, so a longer window there would store more without showing you more. The same reasoning applies to stored answers: the rates and trends come from the rollups, and the answers exist so you can read the evidence behind a recent number. You can delete all stored data for an individual visitor, a site, or your whole account at any time, ahead of any of these schedules. See Data deletion. One limit is worth stating precisely, because it is a property of the storage rather than of the API. The aggregate sketches described above have no visitor or session identifier column to match on, so a per-visitor deletion cannot single them out, and the structure itself has no way to remove one input. Deleting the site or the account clears them completely. For the rollups we keep for the life of the account, that is the only thing that clears them. There is no retention limit to fall back on. For the two time-limited rollups, the daily visitor count and top pages by country, they also expire on their own after 730 days. What a sketch is, stated accurately rather than reassuringly: an approximate distinct-count structure. It cannot be read back to produce a list of who was counted, and nothing in the product joins it to an individual. But below a certain size it retains a hash of each distinct identifier it counted, which means that if someone already holds a particular visitor identifier, they can test whether it is present. We do not do that, and no feature is built on it, but the capability is what makes us describe these as bounded rather than as anonymous.