Audience analytics for publishers, on reader events you own
Ask which articles bring in subscribers, and the answer sits in three systems: the analytics tool, the paywall and billing. Audience analytics on Jitsu puts the raw events from all three into a warehouse you own: page views with an article ID, engaged time, paywall impressions, sign-ups and subscriptions.
Jitsu, an MIT-licensed event pipeline, collects those events on your own subdomain (custom domains, Business and Enterprise plans), runs JavaScript functions on them in transit, and loads them into your warehouse and live destinations such as Mixpanel, Google Ads and Meta Ads.
TL;DR
- Raw reader events land in one
eventstable in BigQuery, ClickHouse, Snowflake, Redshift or Postgres. - Functions run per connection, before events reach a destination.
- Investing.com sends 5 billion events a month through Jitsu.
- Jitsu bills active events: Free covers 200k a month, Business is $99 a month for 2M.
- Identity stitching is on Jitsu Cloud Enterprise and self-hosted Jitsu, warehouse rows only, 30 days back.
Jitsu fits if:
- Raw reader events belong in your warehouse (BigQuery, ClickHouse, Snowflake, Redshift or Postgres), not only in an analytics tool's export.
- Article metadata, URL clean-up and consent checks should run once, in a function, instead of in every app release.
- Subscriptions are sold through Google Ads or Meta, and conversions happen in your billing backend.
- Your code already makes Segment-style
track,pageandidentifycalls (Segment compatibility).
Look elsewhere if:
- Event data must stay in the EU on a managed service. Jitsu Cloud is hosted in the United States; the EU route is self-hosting.
- Editors need a ready-made newsroom dashboard. Jitsu loads data; dashboards live in your BI tool.
What publishers send through Jitsu
Event names are snake_case track calls unless noted; userId is an internal ID, never an email address.
| Event | Emitted from | Why it matters |
|---|---|---|
page | Browser, Jitsu script | Article views with article_id, the base of content metrics |
screen | Apps, through Segment's libraries | App reads in the same table as web |
article_engaged | Browser, app | Engaged seconds and scroll depth, once per page view |
video_play_started | Browser, app | Video starts; never sent to ad platforms |
paywall_shown | Browser, app | Meter and paywall impressions |
newsletter_subscribed | Browser | Registration without a paywall |
signed_up + identify | Browser, app | Links the anonymous reader to an account |
subscription_started | Billing backend | The conversion ad spend is judged on |
trial_converted | Billing backend | First paid charge after a trial |
subscription_canceled | Billing backend | Churn reasons, warehouse only |
page_rendered | Server or edge | Server-side count for measuring blocker loss |
All 34 events, with property types and PII handling: media tracking plan (CSV). Its pii_handling column takes two values. ok means the listed properties carry no personal data; never_send marks an event that sits next to data no event may carry, with those fields named in the notes.
How the pipeline is laid out
The website loads Jitsu's script from your own subdomain (custom domains, Business and Enterprise plans).
Apps send through Segment's open-source libraries pointed at your Jitsu domain, and the billing backend sends server-to-server events over the HTTP API. Each source-to-destination connection runs its own functions. Functions do not run on device-mode destinations (GA4 Device Mode, Tag, Clarity, Hotjar), which load in the browser.
What media companies run on Jitsu
Investing.com, the public story
Investing.com publishes financial news, quotes and charts across 250 exchanges and sends 5 billion events a month through Jitsu from its website, Android app and iPhone app.
Jitsu loads Google BigQuery and sends to Google Ads and Mixpanel; Google Analytics and Google Tag Manager run as client-side destinations. Before Jitsu, Google Analytics was its collection layer with a BigQuery export, so each new parameter meant changing client code on Android, iOS and web at once.
"Unlike other platforms, Jitsu allows not only to modify the data in transit, but also to enrich it by calling external APIs and caching API responses between function calls in a persistent key-value store. This allowed us to move away from client-side enrichment and eliminate massive code duplication."
From the Investing.com customer story
The reader data warehouse page shows that pattern as code.
What others in the industry do
Public write-ups from other publishers, cited as industry examples. They do not describe Jitsu.
- Ophan, the Guardian's in-house analytics tool on Elasticsearch, replaced a packaged product that ran four hours behind (Elastic case study).
- Spoor, the Financial Times event pipeline, expands a content UUID sent with an event into article metadata during enrichment (o-tracking event spec).
- Schibsted's collector checked each user's opt-out status before sending any events, as described in a 2017 conference talk (JFokus slides).
Example implementation (illustrative)
Illustrative example: not a Jitsu customer. A regional news publisher with a metered paywall, a website, iOS and Android apps, BigQuery and Mixpanel.
- Web: the script loads from an
events.subdomain, and the consent platform's callback sets Jitsu's privacy settings. - Apps: Segment's Analytics-Swift and Analytics-Kotlin send to the same Jitsu domain.
- Billing: the backend posts subscription events to
/api/s/trackwith a server-to-server write key and Strict Mode on. - Connections: web and apps go to BigQuery and Mixpanel. Billing goes to BigQuery, Google Ads and Meta Ads, and both ad connections carry the consent-gated function from the subscriber conversions page.
Every connection from the website carries the function below, first in its chain. It drops staging traffic before any fetch runs, so those events reach no destination and are not billed. Personal query parameters (email addresses in newsletter links, reset tokens) come out of URLs in the same pass.
Set INTERNAL_HOSTS on the connection. Build and deploy it with jitsu-cli: it is TypeScript, and the UI editor takes JavaScript with only @jitsu/functions-lib and crypto imports.
import { JitsuFunction } from "@jitsu/protocols/functions";
import { AnalyticsServerEvent } from "@jitsu/protocols/analytics";
export const config = {
slug: "strip-reader-pii",
name: "Strip reader PII and internal traffic",
description: "Drops events from internal hosts and removes personal query parameters from URLs",
};
type Props = {
// Comma-separated hosts whose events never reach a destination, e.g. "staging.example.com,localhost:3000"
INTERNAL_HOSTS?: string;
};
// Query parameters that carry personal data or credentials in newsletter, login and reset links
const PERSONAL_PARAMS = new Set(["email", "e", "token", "auth_token", "code", "password", "subscriber_id"]);
function scrub(value: string): string {
const hashAt = value.indexOf("#");
const beforeHash = hashAt === -1 ? value : value.slice(0, hashAt);
const hash = hashAt === -1 ? "" : value.slice(hashAt);
const queryAt = beforeHash.indexOf("?");
if (queryAt === -1) return value;
const params = new URLSearchParams(beforeHash.slice(queryAt + 1));
for (const name of [...params.keys()]) {
if (PERSONAL_PARAMS.has(name.toLowerCase())) params.delete(name);
}
const query = params.toString();
return beforeHash.slice(0, queryAt) + (query ? `?${query}` : "") + hash;
}
function scrubFields(obj: Record<string, any> | undefined, keys: string[]) {
if (!obj) return;
for (const key of keys) {
if (typeof obj[key] === "string") obj[key] = scrub(obj[key]);
}
}
const stripReaderPii: JitsuFunction<AnalyticsServerEvent, Props> = async (event, { props }) => {
const host = event.context?.page?.host ?? event.properties?.host;
const internal = (props.INTERNAL_HOSTS ?? "").split(",").map(h => h.trim()).filter(Boolean);
if (typeof host === "string" && internal.includes(host)) {
return "drop"; // no fetch has run, so this event is not billed
}
scrubFields(event.context?.page, ["url", "search", "referrer"]);
scrubFields(event.properties, ["url", "search", "referrer"]);
return event;
};
export default stripReaderPii;
Use cases
- Server-side and first-party tracking: custom domains, server-side events, Safari's cookie cap, and SQL that measures blocker loss.
- Subscription analytics: the reader-to-subscriber funnel and consent-gated conversions from billing to Google Ads and Meta. It also covers identity stitching, which rewrites warehouse rows only.
- Reader events in your own warehouse, a GA4 alternative: CMS metadata added in transit, engaged-reading SQL, and per-event versus per-reader billing.
Where Jitsu does not fit
- Editorial tools. No editorial analytics or audience-measurement product is a live destination.
- Newsletters. The live email and messaging destinations are Resend, SendGrid and Braze; most newsletter platforms are not destinations.
- Hosting. Jitsu Cloud is hosted in the United States. EU publishers self-host on Kubernetes with the Jitsu operator. On Prem and Private Cloud are on the Enterprise plan (pricing, contact). SOC 2 and DPA: security.
- Identity. Stitching is Enterprise on Jitsu Cloud (unrestricted self-hosted), rewrites warehouse rows only and reaches back 30 days. Safari's 7-day cap on script-set cookies shortens that without Jitsu's ID endpoint.
- Apps. Apps use Segment's libraries, as there is no Jitsu mobile SDK; connected-TV apps use the HTTP API.
What it costs
Jitsu bills active events: an event sent successfully to at least one destination counts once, however many destinations it reaches. Captured events are unlimited and free.
- Free: 200k active events a month.
- Business: $99 a month for 2M active events, then $40 per additional 1M. Custom domains start here.
- Enterprise: custom volume and pricing, with identity stitching. On Prem and Private Cloud are on the Enterprise plan (pricing).
From the published rates: 2M active events a month is $99; 5M is $219 ($99 + 3 × $40); 20M is $819 ($99 + 18 × $40).
Events a function drops are not billed, unless the function called fetch before dropping them, and an event split into several is billed per resulting event.
Segment bills per monthly tracked user; the reader data warehouse page has SQL that gives your events-per-reader ratio. See also Jitsu vs Segment, Jitsu vs Snowplow, migrating from Segment and pricing.
Questions people ask
Does Jitsu work with dbt? Jitsu loads events into one events table in your warehouse by default, with nested fields flattened into snake_case columns. dbt models read that table like any other source table.
How are anonymous readers joined to subscribers? On Jitsu Cloud Enterprise and on self-hosted Jitsu, identity stitching rewrites a reader's anonymous events from up to 30 days before they identify, in ClickHouse, BigQuery, Postgres, Redshift, Snowflake or MySQL. Other plans join anonymous_id to user_id through identify events in SQL.
Do ad blockers stop Jitsu? Some do. A few DNS blocklists include jitsu.com, and DNS blockers that follow CNAMEs can trace a custom subdomain back to it. On the Business and Enterprise plans the script and collection endpoint run on your own subdomain, which browser filter rules written for jitsu.com hostnames do not match, and server-side events never pass through the browser. Filter lists can still match scripts on a first-party domain, so measure the gap on your own site.
How does consent work? The Jitsu JavaScript library takes your consent platform's choices through its privacy settings: send nothing until consent, drop user IDs, or strip the last octet of the IP address.
Get started with Jitsu
Get started on the free plan with 200k active events a month, or book a demo to talk through self-hosting, identity stitching and Enterprise terms.