Skip to main content
Glama

Pulse

Open-source, revenue-first analytics that runs on your own Cloudflare.

Deploy to Cloudflare

Pulse tells you which post, search, referrer or AI answer brought the people who paid you, and how AI assistants use your MCP tools. It's a self-hosted, drop-in alternative to DataFast: one Worker, one D1 database, one script tag. Most sites fit in Cloudflare's free plan.

  • Revenue attribution: Stripe, Polar and Dodo webhooks. Every sale, renewal and refund is credited to the visit that brought the buyer, even weeks later.

  • Projects home: every site you run on one screen, each with its own pulse line, live visitors, revenue and MCP calls.

  • MCP usage: see which tools your MCP servers get called with, by which AI client, how fast, and whether they fail.

  • AI search visibility: checks every page the way Googlebot sees it, where you rank in Brave for real buying queries, and whether Claude (which searches with Brave) cites you, with a verdict on what to fix.

  • AI crawlers: see when GPTBot, ClaudeBot, PerplexityBot and friends read your pages, which most analytics can't, because crawlers don't run JavaScript.

  • Ask your AI: Pulse is itself an MCP server, so Claude, ChatGPT or Cursor can answer "where did last week's revenue come from?", and with a write token, add projects and funnels.

  • Yours: your Cloudflare account, your data, MIT licensed. No cookies needed if you don't want them.

Features

Traffic

Visitors, sessions, pageviews, bounce, engaged time, scroll depth; channels, referrers, UTM campaigns, pages (entry, exit), countries, cities, devices, browsers, OS. Click any row to filter everything. ChatGPT, Claude, Perplexity and Gemini get their own AI channel.

Revenue

Revenue, sales, average order, share attributed, refunds, revenue per visitor, per source, per page and per country. One-click Stripe history import.

Readable Direct

No-referrer traffic split into typed/bookmarked, shared links (WhatsApp, iMessage, Slack…), checkout returns and named in-app browsers (Instagram, Facebook, LinkedIn, X, TikTok).

Lifetime value

Customers grouped by the source that first brought them, with renewals and repeat buyers.

Funnels

Pages (with * wildcards), goals and payment as steps; drop-off, median time between steps, revenue from finishers. Saved per project.

Page flow

For any page or section (/docs/*): where visits came from right before it and went right after. Click through to follow a path.

Conversions by source

Visits per referrer, channel, utm_medium or utm_campaign, and how many completed any goal, a chosen goal, or paid.

Bots & AI crawlers

Hits by category (AI crawlers, search, link previews, SEO tools, monitoring) and by bot and page, from the script and from your server. Real Chrome browsers running inside cloud networks (scanners, agents) are counted as automated browsers, not visitors.

AI search

Weekly: every sitemap page audited as Googlebot (status, robots.txt, noindex, canonical, links between your sites), Brave index and rank for your buying queries, and Claude's answer with web search (searched? found you? cited you? who instead?). One-click checklist for Brave's submit form.

Segments

Save any set of filters ("AI traffic from the US on mobile") and switch to it in one click.

Goals

pulse("signup"), data-pulse-goal, scroll goals (data-pulse-scroll), conversion rate and revenue per goal. Outbound link clicks are automatic.

Realtime

Live map and list of who's on the site, pushed over a WebSocket the moment someone arrives; visitors per minute, sales in the last hour, and the full journey of any visitor.

Alerts

Telegram: every sale with its source, traffic spikes (a source at 4× its usual hour), daily digest.

Team

Invite people by link as admins or viewers (email + password, no third-party auth). Viewers see everything but secrets and change nothing.

API tokens

Named read or write tokens for each AI assistant or script, with last-used dates and one-click revoke.

Sharing

A read-only public dashboard link per project.

Workflow

⌘K command palette, custom date ranges, per-project timezone, CSV export everywhere, light and dark themes, works on phones, and an in-app notice when a new version is out.

Coming from DataFast?

Swap the script tag and keep your code: window.datafast("signup"), data-fast-goal, data-fast-scroll, and checkouts that send datafast_visitor_id in metadata all keep working. Then point your payment webhooks at Pulse.

Related MCP server: flin-shopify-analytics-mcp

Install: one click

Deploy to Cloudflare

  1. Click the button and sign in to Cloudflare (a free account is fine).

  2. Pick two passwords when asked: ADMIN_TOKEN (your dashboard login) and PULSE_MCP_KEY (for AI assistants).

  3. Click Deploy. Cloudflare copies Pulse to your GitHub, creates the database and deploys it.

  4. Open your new pulse.<you>.workers.dev URL, sign in with ADMIN_TOKEN, add a project and paste the tag.

To use your own domain, add it under the Worker's Settings → Domains & Routes in Cloudflare. Updates: pull changes from this repo into your copy; Cloudflare redeploys and runs new migrations.

git clone https://github.com/everyai-com/pulse && cd pulse
npm install
npx wrangler login
npm run setup                                     # or: npm run setup -- --domain analytics.example.com

setup creates the database, applies migrations, deploys, and generates ADMIN_TOKEN and PULSE_MCP_KEY for you (saved in pulse-credentials.txt, which is gitignored). Upgrade later with git pull && npm run deploy.

Track a site

<script defer data-website-id="my-app" data-domain="example.com" src="https://YOUR-PULSE-URL/js/script.js"></script>

Single-page apps need nothing extra. Options:

Attribute

data-cookieless

No cookie and no storage. The server derives a visitor hash from a daily-rotating secret + IP + user agent, so visitors can't be linked across days (checkout attribution is off).

data-allowed-hostnames="app.example.com"

Carry one visitor across your own domains.

data-exclude="/admin*,/preview/*"

Don't track these paths.

data-hash

Track #/routes.

data-disable-outbound, data-disable-payments

Turn off outbound-click tracking or payment-link tagging.

data-allow-localhost

Track localhost while developing.

In the browser: pulse("goal", props), pulse.identify({ user_id, email, name }), pulse("payment", { email | payment_id }), pulse.visitorId(), pulse.ignoreMe().

Connect payments

In Settings → Payments, copy the webhook URL for Stripe, Polar or Dodo, add it in the provider, and paste the signing secret back. Then pass the visitor id into checkout:

// Stripe Checkout (server), with visitorId from window.pulse.visitorId() in the browser
await stripe.checkout.sessions.create({
  ...,
  client_reference_id: visitorId,
  metadata: { pulse_visitor_id: visitorId },
  subscription_data: { metadata: { pulse_visitor_id: visitorId } },
});
// Polar or Dodo: metadata: { pulse_visitor_id: visitorId }

Stripe Payment Links, Polar links and Dodo /buy/ links need nothing: the script tags them on click. Checkouts that can't carry metadata: call pulse("payment", { email }) on the thank-you page.

MCP tool-call analytics

For an MCP server on the same Cloudflare account, add a service binding and log after each response. The PulseIngest entrypoint is only reachable through bindings, so there's no secret to manage and nobody can post fake events:

// wrangler.jsonc of your MCP server
"services": [{ "binding": "PULSE", "service": "pulse", "entrypoint": "PulseIngest" }]
ctx.waitUntil(env.PULSE.track("my-mcp", {
  name: "mcp_call", path: "/mcp",
  props: { tool: params.name, client: "claude", status: res.status, ms: Date.now() - t0 },
}));

Anywhere else: POST /api/track with Authorization: Bearer <project API key> and { "name": "mcp_call", "props": { "tool": "...", "client": "...", "ok": true, "ms": 120 } }. Never send tool arguments. MCP servers report failures inside HTTP 200 (a JSON-RPC error or isError: true), so send ok: false for those; the MCP view shows failure rates and average latency per tool.

AI search visibility

Claude's web search retrieves from Brave, and Brave won't crawl a page Googlebot can't, so Pulse checks that chain for every project once a week:

  1. Crawl audit (always on): every sitemap page fetched as Googlebot.

  2. Brave index and rank: npx wrangler secret put BRAVE_SEARCH_API_KEY (free key at api-dashboard.search.brave.com).

  3. Claude citations: npx wrangler secret put ANTHROPIC_API_KEY. Each check is one Claude Opus 5.5 answer with web search, so it costs money; SEO_AI_WEEKLY_LIMIT caps checks per week (default 60).

Add buying queries per project ("best X 2026", "X vs Y"), or let Pulse suggest them. Brave's submit form has a CAPTCHA, so submission stays a one-click copy-and-open checklist.

If Pulse runs on the same Cloudflare zone as a site it audits, Cloudflare can route its requests differently from public ones; failures seen only from inside the zone are shown as "unverified" rather than as problems.

AI crawler analytics

AI crawlers fetch your pages without running JavaScript, so log them where the page is served. From a Worker with the PULSE binding:

const ua = request.headers.get("user-agent") || "";
if (/bot|crawl|spider|GPTBot|ClaudeBot|Perplexity|Google-Extended/i.test(ua))
  ctx.waitUntil(env.PULSE.crawl("my-app", { ua, path: new URL(request.url).pathname, status: res.status }));

Anywhere else: POST /api/crawl with the project API key and { "ua": "...", "path": "/", "status": 200 }. Pulse classifies the bot (GPTBot, ClaudeBot, PerplexityBot, Google-Extended, Googlebot, Bingbot, link previews, SEO tools, uptime monitors…).

Let your AI read your analytics

Pulse serves an MCP server at /mcp. Create a token for each assistant under Team & API tokens (read, or read + write), or use PULSE_MCP_KEY.

Read tools: list_sites, site_report, breakdown, timeseries, revenue_report, pages_report, get_paths, get_attribution, get_bot_stats, get_search_visibility, list_goals, realtime, lifetime_value, mcp_usage, run_funnel, list_funnels, visitor_journey, get_tracking_snippet. Write tools (write tokens only): create_site, save_funnel, delete_funnel.

claude mcp add --transport http pulse https://YOUR-PULSE-URL/mcp --header "Authorization: Bearer <token>"

Cursor, VS Code, Windsurf and Claude Desktop take the same URL and header. Clients that can't send headers (Claude.ai and ChatGPT custom connectors) can use https://YOUR-PULSE-URL/mcp/<PULSE_MCP_KEY>; treat that URL like a password.

Alerts

npx wrangler secret put TELEGRAM_BOT_TOKEN   # from @BotFather
npx wrangler secret put TELEGRAM_CHAT_ID     # default chat; each project can override it

How it works

browser ── /js/script.js ──▶ POST /api/e ─┐
Stripe · Polar · Dodo ── /webhooks/* ─────┤
your Workers ── PULSE binding (track, crawl) ┤
                                          ▼
                                 Worker ──┬──▶ D1: projects, visitors, sessions, events, payments (system of record)
                                          └──▶ LiveSite Durable Object per project: last 30 min in SQLite
                                                 └── WebSocket push to open dashboards (hibernates when idle)
dashboard (/), share links, /mcp ◀── Worker      cron every 15 min: spikes, daily digest → Telegram
  • Visits are classified at ingest (referrer, UTM, in-app user agent, landing page) and stored per session, so filters are cheap.

  • The live view comes from a per-project Durable Object, so it's instant and doesn't touch D1; reports read D1.

  • A payment is credited to the visitor's last non-direct session in the 30 days before it (else their first). Refunds and renewals inherit the original sale's credit; webhooks that arrive out of order are back-filled.

  • Bots never count as visitors; they're stored separately and classified. IPs are never stored.

  • Money is stored in minor units with its currency and summed as-is (no currency conversion).

Why D1 and not a data lake? Pulse fits in Cloudflare's free plan: one Worker, one D1 database and one Durable Object class. D1 comfortably handles small and mid-size sites (up to tens of millions of events per database). If you outgrow it, the events table is the only thing to move.

Develop

cp .dev.vars.example .dev.vars    # local secrets
npm run dev                       # local D1 + dashboard on http://localhost:8787
npm run check                     # typecheck + script syntax

Schema changes: add the next migrations/NNNN_name.sql. npm run deploy applies pending migrations before deploying.

Security

Report vulnerabilities privately to support@magicteams.ai. The dashboard escapes every visitor-supplied string; admin APIs need ADMIN_TOKEN; webhook signatures are verified; public share links expose only overview, pages and realtime for one project.

License

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    MCP server for Adobe Customer Journey Analytics, enabling AI-powered analytics queries including reports, breakdowns, trends, and dimension searches through Claude and other MCP clients.
    16
    28 PyPI
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    MCP server for Umami Analytics that provides read-only tools to query website stats, events, sessions, reports, and more, enabling natural language analytics queries.
    8 npm
    3
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    A read-only MCP server for Umami analytics, enabling natural language queries of website stats, traffic trends, events, sessions, and analytics reports.
    13
    16 npm
    1
    Elastic 2.0