⚡ ⚡ Learn how Investing.com uses Jitsu to move data faster

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 events table 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, page and identify calls (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.

EventEmitted fromWhy it matters
pageBrowser, Jitsu scriptArticle views with article_id, the base of content metrics
screenApps, through Segment's librariesApp reads in the same table as web
article_engagedBrowser, appEngaged seconds and scroll depth, once per page view
video_play_startedBrowser, appVideo starts; never sent to ad platforms
paywall_shownBrowser, appMeter and paywall impressions
newsletter_subscribedBrowserRegistration without a paywall
signed_up + identifyBrowser, appLinks the anonymous reader to an account
subscription_startedBilling backendThe conversion ad spend is judged on
trial_convertedBilling backendFirst paid charge after a trial
subscription_canceledBilling backendChurn reasons, warehouse only
page_renderedServer or edgeServer-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

Publisher event pipeline: sources, Jitsu functions per connection, warehouse and live destinations

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.

  1. Web: the script loads from an events. subdomain, and the consent platform's callback sets Jitsu's privacy settings.
  2. Apps: Segment's Analytics-Swift and Analytics-Kotlin send to the same Jitsu domain.
  3. Billing: the backend posts subscription events to /api/s/track with a server-to-server write key and Strict Mode on.
  4. 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

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.

Subscribe to the Jitsu newsletter
Occasional product updates when we ship something worth knowing about. No spam.