Web Analytics
Temps records first-party web analytics — visitors, pageviews, custom events, funnels, Core Web Vitals and AI crawler traffic — in the same binary that deploys your app. Wrap a React app in <TempsAnalyticsProvider> (or add one <script> tag to any other site) and pageviews start arriving through the Temps proxy, which stores them on your own server. Session replay is a separate, opt-in feature documented in Session Replay.
Overview
Vercel and most hosting platforms leave analytics to a third-party integration. Temps ships it in the platform, so there is no PostHog or Plausible account to connect. For the product overview, see Temps Analytics.
What's Included
- Real-time visitor analytics
- Custom event tracking
- Core Web Vitals monitoring
- Funnel analysis
- Geographic tracking
- Session stitching
- AI crawler and bot classification
- Opt-in session replay (details)
Why It Matters
- No third-party analytics service
- Data stays on your server (unless you link the instance to Temps Cloud)
- Page view capacity scales with your server
- No per-event or per-seat fees
- Designed to support GDPR/CCPA compliance — see Privacy & Compliance
Architecture
Temps analytics is powered by:
- TimescaleDB: the default backend, a PostgreSQL extension whose hypertables partition events by time
- Optional ClickHouse backend: for high-volume instances (see Observability storage)
- Batching: the proxy writes request logs in batches, and the replay recorder sends events in batches
Setup
For React and Next.js apps, wrap your application with the React provider:
import { TempsAnalyticsProvider } from '@temps-sdk/react-analytics';
export function App() {
return (
<TempsAnalyticsProvider>
<YourApp />
</TempsAnalyticsProvider>
);
}
For plain HTML pages, Astro, Alpine.js, htmx, or another non-React frontend, add the browser analytics script before </head>:
<script
defer
src="https://unpkg.com/@temps-sdk/analytics-browser@0.0.6/dist/temps.min.js"
></script>
Both options send events to the current site's relative /api/_temps/* endpoints by default. For apps deployed on Temps, the proxy handles those endpoints automatically. The full list of provider props and hooks is in the React Analytics SDK reference; for a step-by-step walkthrough that also adds error tracking and replay, follow Add analytics, errors and session replay.
How does Temps analytics work
Temps identifies visitors with two first-party, HttpOnly cookies set by the Pingora reverse proxy: _temps_visitor_id (kept for 365 days) and _temps_sid (a 30-minute session). The browser SDK sends pageviews and events to the proxy, which attaches those IDs, enriches the event with geography and device data, and writes it to your database. There are no third-party cookies. When Temps is used only as an analytics backend for a site it doesn't serve, the SDK falls back to a visitor ID it keeps in localStorage. In self-hosted mode, analytics data stays on your server; if you link the instance to Temps Cloud (coming soon), configured analytics and OpenTelemetry data is uploaded to Temps Cloud for managed storage and queries. For how events move from the browser to storage, see Data flow; to see which AI crawlers hit your site, see how to track AI crawler traffic.
How this compares with the two analytics tools teams most often replace (competitor details as of October 2026):
| Temps | PostHog Cloud | Plausible Cloud | |
|---|---|---|---|
| Visitor tracking | Two first-party cookies set by the proxy | JS snippet with first-party cookie/localStorage | No cookies (daily rotating hash) |
| Session replay | Opt-in (rrweb, inputs masked) | Built-in (5,000 free recordings/mo, then usage-based) | Not included |
| Core Web Vitals | Built-in | Built-in (opt-in toggle) | Not included |
| Funnel analysis | Built-in | Built-in | Business plan and up |
| Data location | Your server (self-hosted) or Temps Cloud (coming soon) | PostHog cloud | Plausible cloud |
| Pricing | Free (self-hosted); Temps Cloud from $29/mo (coming soon) | Free up to 1M events/mo, then usage-based | From $9/mo |
| Open-source license | MIT / Apache 2.0 | MIT (core) / proprietary (EE) | AGPL |
For a full side-by-side, see Temps vs PostHog and Temps vs Plausible.
Visitor analytics
Track and analyze visitor behavior with detailed metrics and geographic data.
Metrics Tracked
- Name
Page Views- Description
Total page views with time-series charts, breakdown by path, referrer tracking
- Name
Unique Visitors- Description
First-party cookie visitor ID (
_temps_visitor_id), session stitching, returning vs new visitor ratio
- Name
Session Duration- Description
Average session length, time on page, engagement metrics
- Name
Bounce Rate- Description
Single-page session tracking, exit page analysis
- Name
Geographic Data- Description
Country, region, city detection using MaxMind GeoIP, timezone tracking
- Name
Device & Browser- Description
User agent parsing, browser version, OS, device type (mobile/desktop/tablet)
- Name
Referrer Sources- Description
Traffic source tracking, UTM parameter parsing, campaign attribution
- Name
Real-Time Visitors- Description
Live visitor count, currently active pages
Dashboard Views
Access analytics data via the dashboard. View real-time metrics, historical trends, and detailed visitor insights all in one place.
Time-Series Charts
View trends over preset ranges (such as the last 24 hours, 7 days or 30 days) or a custom date range. The chart bucket size adjusts to the range you pick.
Bot filtering & engagement
Temps filters out automated traffic so your metrics reflect real humans. Bot detection and engagement scoring are built-in and run automatically — there are no flags, environment variables, or endpoints to configure. The thresholds and pattern list below are fixed in source.
How a session counts as "engaged"
A session is marked engaged only if at least one of these is true:
- The visitor spent ≥ 10 seconds of measured wall-clock time on the site (
MAX(timestamp) − MIN(timestamp)across the session, orlast_accessed_at − started_atfor the per-session detail view), or - The visitor fired a genuine interaction event.
Auto-fired view events do not mark a session engaged on their own, because intersection observers fire them for bots too. The following are excluded from the interaction count:
| Event | Why it's excluded |
|---|---|
page_view | Auto-fired on every page load |
page_leave | Auto-fired when the page is left |
Any event name ending in _viewed (e.g. product_viewed) | Auto-fired by intersection observers when an element scrolls into view |
A session that only fires these view events still becomes engaged if it crosses the 10-second wall-clock threshold — that's the only way such a session counts.
Previously, any event other than page_view / page_leave flipped a session to engaged, which inflated engagement with bot-driven view events. The 10-second-or-interaction rule replaced that as of v0.1.0.
Session replays exclude zero-duration sessions
Replay listings (both the count and the list) only include sessions whose recorded duration is greater than 0 ms. Sessions with a duration of 0 ms or NULL — never-finalized single-burst sessions, which are typically bots — are excluded, because they have nothing to play back.
User-agent bot detection
The events pipeline matches each visitor's user-agent string (case-insensitively, by substring) against a built-in list of 30+ known bot patterns. A match sets the event/visitor is_crawler flag and records the matched crawler_name. A missing or empty user-agent is conservatively treated as a bot.
The pattern list covers:
| Category | Examples |
|---|---|
| AI crawlers | GPTBot, ClaudeBot, anthropic-ai, PerplexityBot, CCBot, Bytespider, Google-Extended, Applebot, Amazonbot |
| Search / SEO crawlers | Googlebot, Bingbot, YandexBot, DuckDuckBot, Baiduspider, AhrefsBot, SemrushBot |
| Link-preview unfurlers | facebookexternalhit, Slackbot, Discordbot, Twitterbot, LinkedInBot, WhatsApp, Telegrambot |
| Headless browsers / automation | HeadlessChrome, PhantomJS, Puppeteer, Playwright, Selenium |
| Monitoring tools | Pingdom, UptimeRobot, StatusCake |
| HTTP clients | python-requests, curl, wget, go-http-client, node-fetch, axios |
| Generic catch-alls | bot, crawler, spider |
Google-Extended is a robots.txt control token, not a standalone HTTP User-Agent used by Google. Its presence in this general substring filter only means that a client sending that text will be treated as automated traffic; it does not identify genuine Google traffic. The separate AI Agents view uses the proxy taxonomy described below.
This list is matched in addition to the underlying parser's own crawler classification; the substring match is the more specific signal and takes precedence.
Event tracking
Track custom events to understand user interactions and behavior.
Built-in Events
With default settings, the SDK captures these without extra code:
- Page views (
page_view), including client-side route changes - Page exits (
page_leave) - Web Vitals (see Performance monitoring)
- Engagement heartbeats, used for the engaged-session rule above
- Clicks on any element with a
temps-event-nameattribute (declarative tracking, withtemps-data-*attributes as properties)
Other interactions — outbound link clicks, form submissions, downloads — are custom events you send yourself. JavaScript errors are captured by error tracking, not analytics: point a Sentry SDK at your Temps DSN as described in Error Tracking. 404s show up as response status codes in your request logs.
Custom Events
Track any custom event in your application:
Track Custom Events
import { useTrackEvent } from '@temps-sdk/react-analytics';
function CheckoutButton() {
const trackEvent = useTrackEvent();
const handleCheckout = () => {
trackEvent('checkout_completed', {
orderId: '12345',
amount: 99.99,
currency: 'USD',
items: 3
});
};
return <button onClick={handleCheckout}>Complete Purchase</button>;
}
Event Properties
- Name
Event Name- Description
String identifier for the event (e.g., 'signup_completed', 'video_played')
- Name
Properties- Description
Custom JSON object with event metadata (any structure)
- Name
Visitor and Session ID- Description
Taken from the
_temps_visitor_idand_temps_sidcookies, so events join the visitor's other activity automatically
- Name
Timestamp- Description
Set by the server when the event is recorded
- Name
Context- Description
Automatic context (page path, referrer, user agent, screen and viewport size, language, geography)
Querying Events
The project's analytics dashboard lists event names with their counts (the events dimension below); click an event to see its totals, breakdown charts and the visitors who fired it. The same data is available from the API — for example GET /api/projects/{project_id}/events/properties/breakdown for a property breakdown — and through the Node SDK.
Session replay
Session replay is off by default. When you turn it on (enableSessionRecording on the React provider, or data-session-recording="true" on the script tag), the SDK records the page with rrweb: DOM snapshots and mutations, and user input such as clicks, scrolling and typing. All input values are masked by default. The script tag also skips login, signup, checkout, payment and billing paths by default; with the React provider, list those routes in excludedPaths. It does not capture console logs, network requests, or performance data such as FPS or memory, and there are no heatmaps. Recordings are kept for 15 days.
<TempsAnalyticsProvider
enableSessionRecording
sessionRecordingConfig={{ sessionSampleRate: 0.1 }} // record 10% of sessions
>
<YourApp />
</TempsAnalyticsProvider>
Configuration options, privacy controls and the replay player are documented on the Session Replay page. Comparing tools? See Temps vs FullStory and Temps vs LogRocket.
Performance monitoring
Track Core Web Vitals and performance metrics to optimize user experience.
Core Web Vitals
The three Core Web Vitals are LCP, INP and CLS. The SDK also reports FCP and TTFB, and still sends FID, which INP replaced as a Core Web Vital in March 2024.
- Name
Largest Contentful Paint (LCP)- Description
Core Web Vital. Measures loading performance. Target: < 2.5 seconds
- Name
Interaction to Next Paint (INP)- Description
Core Web Vital. Measures responsiveness. Target: < 200 milliseconds
- Name
Cumulative Layout Shift (CLS)- Description
Core Web Vital. Measures visual stability. Target: < 0.1
- Name
First Contentful Paint (FCP)- Description
Additional metric. Time to first content render. Target: < 1.8 seconds
- Name
Time to First Byte (TTFB)- Description
Additional metric. Server response time. Target: < 800 milliseconds
Automatic Collection
Web Vitals are collected by default (autoTrackSpeedAnalytics defaults to true), so no extra code is needed. To turn them off:
Disable Web Vitals collection
import { TempsAnalyticsProvider } from '@temps-sdk/react-analytics';
function App() {
return (
<TempsAnalyticsProvider autoTrackSpeedAnalytics={false}>
<YourApp />
</TempsAnalyticsProvider>
);
}
Performance Insights
The project's performance view shows:
- Each metric over time
- Percentiles (P75, P90, P95, P99)
- Breakdowns and filters by page path, device type, browser, operating system and location
Temps does not have performance budgets or Web Vitals alerts. For alerting on server-side signals such as CPU, memory or error rates, use Monitoring.
Funnel analysis
A funnel is an ordered list of events. Temps counts how many visitors reach each step in order, and where they drop off.
Create a Funnel
Create funnels in the project's analytics Funnels view, or through the API. Each step has an event_name and optional event_filter rules (page path, hostname, UTM values, referrer, channel, device type, browser, operating system, language, or a custom event property):
Define Funnel
import { TempsClient } from '@temps-sdk/node-sdk';
const client = new TempsClient({
baseUrl: 'https://your-temps-instance.com/api', // include the /api prefix
apiKey: 'your-api-key'
});
await client.funnels.create({
path: { project_id: 1 },
body: {
name: 'Signup Funnel',
steps: [
{ event_name: 'page_view', event_filter: [{ type: 'page_path', value: '/' }] },
{ event_name: 'page_view', event_filter: [{ type: 'page_path', value: '/signup' }] },
{ event_name: 'signup_form_submit', event_filter: [] },
{ event_name: 'email_verified', event_filter: [] }
]
}
});
Funnel Metrics
- Name
Step Conversion- Description
Share of visitors from the previous step who reached this step
- Name
Drop-off Rate- Description
Share of visitors who left at each step
- Name
Time Between Steps- Description
Average time from one step to the next
- Name
Overall Conversion- Description
Share of visitors who completed every step
Funnel metrics can be filtered by environment, country and date range.
AI agent tracking
Temps classifies requests whose User-Agent matches its built-in AI-agent taxonomy. For traffic passing through the Temps proxy, no browser SDK or app-side instrumentation is required. User-Agent strings can be spoofed, so this view is for observability—not authentication or proof of downstream model use.
Tracked signals
- Canonical agent name, provider, and broad purpose
- Requested paths and request counts
- Unique source IPs per recognized agent name
- Request share, response status, and activity over time
Use cases
- Observe requests carrying recognized identifiers
- See which paths those requests target most
- Find crawler requests receiving errors
- Inspect request activity after publishing llms.txt or sitemap changes
What this data proves:
A matching row proves that Temps recorded a request carrying a recognized User-Agent string. It does not prove that the named provider made the request, indexed the response, used it for training, included it in an answer, or cited it to a user.
Via CLI
# Which recognized AI-agent identifiers appear in requests?
bunx @temps-sdk/cli analytics ai-agents -p my-app --period 7d
# Grouped by AI provider (OpenAI, Anthropic, Google…)
bunx @temps-sdk/cli analytics ai-agents -p my-app --group-by provider
# Which paths receive the most recognized AI-agent requests?
bunx @temps-sdk/cli analytics ai-pages -p my-app --period 30d
# Show the recognized agent split for each path
bunx @temps-sdk/cli analytics ai-pages -p my-app --with-agents
Via REST API
# AI agent breakdown
curl https://your-temps.example.com/api/proxy-logs/stats/ai-agents \
-H "Authorization: Bearer <token>" \
-G --data-urlencode "project_id={project_id}" \
--data-urlencode "start_time=2026-08-01T00:00:00Z" \
--data-urlencode "end_time=2026-08-08T00:00:00Z"
# Pages crawled by AI
curl https://your-temps.example.com/api/proxy-logs/stats/ai-pages \
-H "Authorization: Bearer <token>" \
-G --data-urlencode "project_id={project_id}"
View all dimension data
The overview dashboard shows the top 5 or 10 rows for each chart. To see the full dataset, click the View all button in any chart header.
Dimension pages
Each chart's "View all" button opens a dedicated dimension page at /projects/:slug/analytics/dimensions/:dimension. These pages:
- Show up to 100 rows for the selected dimension and date range
- Include an inline text filter for client-side narrowing — useful when a dimension has many distinct values
- Preserve the currently selected date filter, so navigating to a dimension page and back does not reset your time range
The following dimensions have dedicated pages:
| Dimension | What it shows |
|---|---|
pages | Top pages by view count |
referrers | Traffic sources and referring domains |
browsers | Browser distribution |
operating_systems | OS distribution |
devices | Device type (desktop, mobile, tablet) |
countries | Geographic distribution by country |
channels | Acquisition channels (organic, direct, paid, etc.) |
events | Custom event names and occurrence counts |
languages | Browser/OS language preference |
utm_source | UTM source parameter values |
utm_medium | UTM medium parameter values |
utm_campaign | UTM campaign parameter values |
utm_term | UTM term parameter values |
utm_content | UTM content parameter values |
Event detail
Rows in the events dimension link through to the event detail view. The event detail page now shows:
- A first → last timestamp range for that event across all visitors in the selected period
- Per-visitor columns: visitor UUID, numeric visitor ID, device, browser, location, and referrer
Visitor segments
Clicking any row in a non-page dimension chart — for example "Chrome" in Browsers, "United States" in Countries, an event name, a UTM campaign value, or a referrer — opens a visitor segment page.
Segment pages
Segment pages are located at /projects/:slug/analytics/segments/:dimension/:value and show a paginated list of visitors who match that dimension value within the selected date range:
- 25 visitors per page, sorted by last action descending
- Each row links to the individual visitor detail page, where you can see the visitor's full session history, device information, and event timeline
- The active date filter is carried through the entire navigation chain: overview → dimension page → segment → visitor detail → back
Supported segment filters
The segment visitor list is powered by the /analytics/visitors endpoint, which accepts the following filter parameters:
- Name
filter_country- Description
Filter visitors by country code (e.g.
US,DE)
- Name
filter_region- Description
Filter visitors by region or state
- Name
filter_city- Description
Filter visitors by city name
- Name
filter_channel- Description
Filter visitors by acquisition channel (e.g.
organic,direct,paid)
- Name
filter_referrer- Description
Filter visitors by referring domain or URL
- Name
filter_event- Description
Filter visitors who fired a specific event name
- Name
filter_browser- Description
Filter visitors by browser name (e.g.
Chrome,Firefox)
- Name
filter_os- Description
Filter visitors by operating system (e.g.
macOS,Windows)
- Name
filter_device- Description
Filter visitors by device type (
desktop,mobile,tablet)
- Name
filter_language- Description
Filter visitors by browser language preference
- Name
filter_utm_source- Description
Filter visitors by UTM source value
- Name
filter_utm_medium- Description
Filter visitors by UTM medium value
- Name
filter_utm_campaign- Description
Filter visitors by UTM campaign value
- Name
filter_utm_term- Description
Filter visitors by UTM term value
- Name
filter_utm_content- Description
Filter visitors by UTM content value
Date filters are preserved across the full navigation chain — overview, dimension pages, segment pages, and visitor detail — so the cohort you are inspecting stays consistent as you drill down and navigate back.
SDKs
Track events from your frontend and enrich visitor records from your backend using the official SDKs.
React Analytics SDK — provider, hooks, declarative tracking
Node SDK — visitor enrichment, API client reference
Privacy & Compliance
Temps analytics is designed to support GDPR and CCPA compliance: the data stays in a database you control and no third party receives it. Whether you are compliant still depends on how you deploy and configure it — your cookie notice, consent flow and retention settings. Temps stores a first-party visitor ID in a cookie and in localStorage, which generally requires consent for EU visitors.
Data Ownership
- Name
Self-Hosted- Description
Analytics data is stored on your own server and is not sent to Temps or any third party, unless you link the instance to Temps Cloud. Your hosting provider remains a data processor — see Data ownership and privacy.
- Name
First-party cookies only- Description
The proxy sets two first-party, HttpOnly cookies on HTML responses:
_temps_visitor_id(365 days) and_temps_sid(30 minutes). No third-party tracking cookies. When Temps only receives events for a site it doesn't serve, the SDK keeps a visitor ID inlocalStorageinstead.
- Name
IP addresses- Description
The proxy records the client IP address and user agent with each visitor, for geolocation and bot detection. There is no built-in IP anonymisation setting.
- Name
Data Retention- Description
On the default TimescaleDB backend, retention is configurable per data type (proxy logs, OTel spans/logs/metrics) via Settings → Monitoring → Data Retention or the settings API. On the optional ClickHouse backend, retention is fixed to table-level TTL. Session replay (15 days) and log aggregator retention (7/30 days) are fixed regardless of backend.
- Name
Access and erasure requests- Description
Because the data lives in your own database, you can answer access and deletion requests directly. Temps has no dedicated erasure or export endpoint.
Consent
To wait for consent before sending analytics events, keep the provider disabled until the visitor accepts:
Consent Mode
import { useState } from 'react';
import { TempsAnalyticsProvider } from '@temps-sdk/react-analytics';
function App() {
const [consent, setConsent] = useState(false);
return (
<TempsAnalyticsProvider
disabled={!consent} // Only send events once the user consents
>
<CookieBanner onAccept={() => setConsent(true)} />
<YourApp />
</TempsAnalyticsProvider>
);
}
disabled stops the SDK from sending events and replays. The two cookies above are set by the proxy, not the SDK, so list them in your cookie notice.
On the default TimescaleDB backend, data retention windows for proxy logs, OTel spans, OTel logs, and OTel metrics are configurable per data type — see Settings → Monitoring → Data Retention, or PUT /settings. On the optional ClickHouse backend, retention is fixed to table-level TTL and the settings page hides the editable fields accordingly.
Additional Resources
Analytics is one part of Temps' built-in observability: session replay, error tracking and the Observe timeline run on the same server. For the overview, see Observability.