Analytics
  • Overview
  • Realtime
  • Visitors
  • Bot traffic
  • Errors
Manage
  • Settings
  • Sign inTrack your own sites
Datasense

Settings

acme.store

GeneralInstallationGoalsAd blockersRevenueBot trafficExport
GeneralInstallationGoalsAd blockersRevenueBot trafficExport

General

How this site appears across your dashboard, and the timezone its reports use.

Installation

Add the script to every page on acme.store. The status updates live.

Receiving data from acme.storeLast pageview
  1. 1Paste this inside the <head> of every page. Works for plain HTML, Vite, Astro, Hugo, and Webflow or Framer custom code.
index.html
<script
  defer
  data-website-id="demo"
  src="https://usedatasense.com/js/script.js"
></script>
Install it with AIPaste a ready-made prompt into Cursor, Claude Code or Copilot.

Goals

Count signups, checkouts or any action. Goals show up on the overview with their conversion rate.

goals.js
// From JavaScript, after a key action
window.datasense?.("signup", { plan: "pro" })

// Or declaratively, on click
<button data-ds-goal="checkout" data-ds-goal-plan="pro">Buy</button>

Ad blockers

Recommended. Filter lists block analytics domains, not acme.store. Proxy a path through your own server: your framework or edge forwards two URLs to us, and the script detects the proxy on its own.

Pick a short, unique path. Filter lists match common analytics paths, not /_ds on your own domain.

next.config.ts
// next.config.ts
import type { NextConfig } from "next"

const nextConfig: NextConfig = {
  async rewrites() {
    return [
      { source: "/_ds/js/script.js", destination: "https://usedatasense.com/js/script.js" },
      { source: "/_ds/api/e", destination: "https://usedatasense.com/api/e" },
    ]
  },
}

export default nextConfig
Then load the script from your own domain
<script
  defer
  data-website-id="demo"
  src="/_ds/js/script.js"
></script>

Revenue

Tie every payment back to the source, campaign and page that brought the buyer. Works with any provider.

  1. 1
    Create a secret API keyYour server uses it to report payments. Never put it in browser code.
    Secret API keyNo key yet
  2. 2
    Send the visitor's session to checkoutThe script exposes the current session as window.datasense.sessionId().
    checkout.js
    // When the visitor starts checkout, send their session along
    await fetch("/api/checkout", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        plan: "pro",
        datasense_session_id: window.datasense?.sessionId?.(),
      }),
    })
  3. 3
    Keep it on the Stripe checkout
    api/checkout.js
    const session = await stripe.checkout.sessions.create({
      line_items: [/* … */],
      metadata: { datasense_session_id: body.datasense_session_id },
    })
  4. 4
    Report payments from your webhookSend each successful payment. Repeated webhooks are de-duplicated by transaction_id.
    api/webhook.js
    await fetch("https://usedatasense.com/api/v1/payments", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.DATASENSE_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        amount: 29.99,                 // major units, not cents
        currency: "USD",
        transaction_id: payment.id,    // makes retries safe
        datasense_session_id: payment.metadata.datasense_session_id,
        customer_id: payment.customer, // optional
        renewal: false,                // true for subscription renewals
        // refunded: true,             // send again with this for a refund
      }),
    })
Receiving payments

Bot traffic

See when ChatGPT, Claude, Perplexity, Google and training crawlers read acme.store. Crawlers skip JavaScript, so your server reports them from its middleware. Human requests are never sent.

proxy.ts
// proxy.ts (middleware.ts before Next.js 16)
import { NextResponse, type NextFetchEvent, type NextRequest } from "next/server"

const CRAWLER = /bot|crawl|spider|slurp|-user\b|claude|anthropic|cohere|meta-external|facebookexternalhit|google|bingpreview/i

export function proxy(request: NextRequest, event: NextFetchEvent) {
  const userAgent = request.headers.get("user-agent") ?? ""
  if (request.method === "GET" && CRAWLER.test(userAgent)) {
    const host = request.headers.get("x-forwarded-host") ?? request.headers.get("host")
    // Report in the background; never delay or fail the page.
    event.waitUntil(
      fetch("https://usedatasense.com/api/crawls", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          websiteId: "demo",
          href: `https://${host}${request.nextUrl.pathname}`,
          ai: {
            userAgent,
            // Only trust these headers behind a proxy that sets them (Vercel, Cloudflare, nginx).
            ip: request.headers.get("cf-connecting-ip") ?? request.headers.get("x-real-ip") ??
              request.headers.get("x-forwarded-for")?.split(",")[0]?.trim(),
            source: "nextjs",
          },
        }),
        signal: AbortSignal.timeout(2000),
      }).catch(() => {}),
    )
  }
  return NextResponse.next()
}

export const config = {
  // Skip assets, but keep robots.txt, llms.txt and sitemaps: crawlers read those first.
  matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
}
Receiving bot traffic · last crawl 2d ago

Export

Raw rows for spreadsheets, notebooks or your warehouse. Your data is always yours.

Pageviews, goals and time on page, one row per event.

Download events as CSV

Reports count days in this timezone: Today starts at its midnight.