# BetterMeter — Full Documentation
> BetterMeter is a privacy-first analytics platform for the AI era. It tracks websites, CLI tools, MCP servers, and APIs through a unified, cookie-free pipeline. This is the extended documentation file with complete SDK reference, CLI flags, MCP tool schemas, and integration examples.
For a shorter overview, see [llms.txt](https://bettermeter.com/llms.txt).
---
## Table of Contents
1. Overview
2. Web Tracking — Full Reference
3. Node SDK — Complete API Reference
4. CLI — All Commands with Full Flags
5. MCP Server — Complete Tool Schemas
6. REST API — Full Endpoint Reference
7. Proxy Setup — All Platform Guides
8. Privacy Model — Technical Details
9. Data Model
10. Brand Monitoring — Full Reference
11. Integration Examples
12. FAQ
---
## 1. Overview
BetterMeter is an analytics platform for developers building AI-powered products. It tracks four source types through one privacy-preserving pipeline:
| Source | Integration Method | Event Type | What Gets Tracked |
|--------|-------------------|------------|-------------------|
| Website | `
```
### Script Attributes
| Attribute | Required | Description |
|-----------|----------|-------------|
| `data-site` | Yes | Your domain as registered in BetterMeter |
| `data-api` | No | Custom API endpoint for proxy setups (e.g., `/bm/e`) |
### JavaScript API
The tracker exposes a global `window.bettermeter` object:
```javascript
// Track a custom event
window.bettermeter.track("signup", { plan: "pro", source: "landing" });
// Identify a user (attaches user ID to subsequent events)
window.bettermeter.identify("user_123");
```
### What the Tracker Captures Automatically
- Page URL and pathname
- Referrer (with AI platform detection)
- Screen width (for device classification)
- Timezone
- SPA navigation via History API
- Tab return events (visibility change)
### What the Tracker Does NOT Capture
- No cookies are set
- No local storage or session storage is used
- No canvas, WebGL, or font fingerprinting
- No scroll depth or mouse movement
- No form input values
---
## 3. Node SDK — Complete API Reference
**Package:** `@bettermeter/node`
**Install:** `npm install @bettermeter/node`
**Requirements:** Node.js 18+ (uses built-in `fetch` and `crypto`)
**Dependencies:** Zero
### Constructor
```typescript
import { BetterMeter } from "@bettermeter/node";
const bm = new BetterMeter({
siteId: "my-tool", // Required: domain or identifier
apiKey: "bm_...", // Required: API key from dashboard
apiUrl: "https://bettermeter.com", // Optional: default shown
disabled: false, // Optional: disable all tracking
batch: false, // Optional: queue events for batch send
batchInterval: 5000, // Optional: batch flush interval in ms
});
```
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| siteId | string | Yes | — | Domain or identifier registered in BetterMeter |
| apiKey | string | Yes | — | API key (Bearer token) from dashboard settings. Starts with `bm_`. |
| apiUrl | string | No | `https://bettermeter.com` | BetterMeter API URL |
| disabled | boolean | No | `false` | Disable all tracking. Also respects `BETTERMETER_DISABLED=1` env var. |
| batch | boolean | No | `false` | Queue events and flush on interval instead of sending immediately |
| batchInterval | number | No | `5000` | Batch flush interval in milliseconds |
### trackCommand(options)
Track a CLI command invocation.
```typescript
bm.trackCommand({
command: "deploy", // Required: command name
subcommand: "preview", // Optional: subcommand
flags: ["--prod", "--verbose"], // Optional: flag names (values stripped)
version: "2.1.0", // Optional: CLI version
durationMs: 4500, // Optional: execution time in ms
exitCode: 0, // Optional: process exit code
isCi: !!process.env.CI, // Optional: running in CI?
userId: "user_123", // Optional: custom user ID
properties: { region: "us-east" }, // Optional: additional properties
});
```
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| command | string | Yes | CLI command name (e.g., "deploy", "build") |
| subcommand | string | No | Subcommand (e.g., "deploy preview") |
| flags | string[] | No | Flag names used. Values are stripped for privacy. e.g., ["--prod", "--verbose"] |
| version | string | No | CLI version string |
| durationMs | number | No | Duration of command execution in milliseconds |
| exitCode | number | No | Exit code (0 = success) |
| isCi | boolean | No | Whether running in a CI environment |
| userId | string | No | Custom user ID for the CLI user |
| properties | Record | No | Additional custom properties |
### trackTool(options)
Track an MCP tool invocation.
```typescript
bm.trackTool({
tool: "search_docs", // Required: tool name
client: "claude-code", // Optional: AI client name
protocolVersion: "2024-11-05", // Optional: MCP protocol version
durationMs: 250, // Optional: execution time in ms
success: true, // Optional: success/failure
errorType: "validation_error", // Optional: error classification
inputTokens: 150, // Optional: input token count
outputTokens: 800, // Optional: output token count
userId: "user_123", // Optional: custom user ID
properties: {}, // Optional: additional properties
});
```
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| tool | string | Yes | MCP tool name (e.g., "search_docs", "create_issue") |
| client | string | No | MCP client name (e.g., "claude-code", "cursor", "windsurf") |
| protocolVersion | string | No | MCP protocol version |
| durationMs | number | No | Duration of tool execution in milliseconds |
| success | boolean | No | Whether the tool call succeeded |
| errorType | string | No | Error type if failed (e.g., "validation_error", "timeout") |
| inputTokens | number | No | Input token count |
| outputTokens | number | No | Output token count |
| userId | string | No | Custom user ID |
| properties | Record | No | Additional custom properties |
### trackApi(options)
Track an API request.
```typescript
bm.trackApi({
method: "POST", // Required: HTTP method
endpoint: "/api/users", // Required: endpoint pattern
statusCode: 201, // Optional: HTTP status code
durationMs: 45, // Optional: response time in ms
userId: "user_123", // Optional: custom user ID
properties: {}, // Optional: additional properties
});
```
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| method | string | Yes | HTTP method (GET, POST, PUT, DELETE, etc.) |
| endpoint | string | Yes | Endpoint pattern. Use `:param` for dynamic segments (e.g., "/api/users/:id"), NOT actual paths with IDs. |
| statusCode | number | No | HTTP response status code |
| durationMs | number | No | Duration of request in milliseconds |
| userId | string | No | Custom user ID |
| properties | Record | No | Additional custom properties |
### wrapCommander(program, options?)
Auto-track all Commander.js commands via `postAction` hook.
```typescript
import { Command } from "commander";
const program = new Command();
bm.wrapCommander(program, { version: "1.0.0" });
// All commands registered after this are automatically tracked
program.command("deploy").action(() => { /* ... */ });
program.command("build").action(() => { /* ... */ });
program.parse();
```
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| program | Commander instance | Yes | The Commander.js program to wrap |
| options.version | string | No | CLI version to include in events |
### wrapMcpServer(server)
Auto-track all MCP tool invocations on a McpServer instance. Works by monkey-patching `server.tool()` to wrap handlers with timing and error tracking.
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
const server = new McpServer({ name: "my-server", version: "1.0.0" });
bm.wrapMcpServer(server);
// Tools registered after wrapping are automatically tracked
server.tool("search_docs", { query: z.string() }, async (args) => {
// Your tool logic (unchanged) — BetterMeter tracks automatically
return { content: [{ type: "text", text: "results..." }] };
});
```
### expressMiddleware()
Returns an Express/Connect-compatible middleware that auto-tracks every request.
```typescript
import express from "express";
const app = express();
app.use(bm.expressMiddleware());
// All routes are automatically tracked
app.get("/api/users", (req, res) => { /* ... */ });
app.post("/api/users", (req, res) => { /* ... */ });
```
The middleware hooks into `res.end` to capture the status code and duration after the response completes.
### flush(): Promise
Send all queued events to BetterMeter immediately. Call this before process exit to ensure no events are lost.
```typescript
await bm.flush();
```
### shutdown(): Promise
Stop the batch timer and flush all remaining events. Call this on graceful shutdown.
```typescript
process.on("SIGTERM", async () => {
await bm.shutdown();
process.exit(0);
});
```
### Privacy-Preserving Machine ID
The SDK generates a privacy-preserving machine identifier by hashing `hostname + platform + arch` with SHA-256 and truncating to 16 characters. The raw hostname is never sent.
### Error Handling
All tracking methods are fire-and-forget. If the BetterMeter API is unreachable or returns an error, the SDK silently drops the event. Analytics failures never throw exceptions or break the host application.
---
## 4. CLI — All Commands with Full Flags
**Install:** `npm install -g bettermeter`
### Global Options
| Flag | Description |
|------|-------------|
| `-V, --version` | Show CLI version |
| `-h, --help` | Show help |
### Authentication
```bash
# Authenticate
bettermeter login -t -u
# Show current user
bettermeter whoami
# Remove stored credentials
bettermeter logout
```
### Output Formats
By default, the CLI renders rich visual output with ASCII art charts, colored text, sparklines, and box-drawn stat cards. All output uses Unicode characters compatible with every modern terminal. Colors auto-detect terminal capabilities and respect the `NO_COLOR` environment variable.
| Mode | Description |
|------|-------------|
| Default (visual) | Line charts for timeseries, horizontal bar charts for ranked data, sparklines in overview stats, styled tables with box-drawing borders, colored change indicators |
| `--json` | Raw JSON data — ideal for scripting, piping to `jq`, or programmatic use |
### Analytics Commands — Common Options
All analytics commands accept these options:
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| `-s, --site ` | string | (required) | Site ID (domain) to query |
| `-r, --range ` | string | `30d` | Date range: `today`, `7d`, `30d`, `90d`, `12m` |
| `-l, --limit ` | number | `10` | Maximum results to return |
| `--json` | flag | off | Output raw JSON instead of visual charts and tables |
### Web Analytics Commands
```bash
bettermeter stats -s example.com
# Output: visitors, pageviews, sessions, bounce rate, avg duration + % change vs prior period
bettermeter pages -s example.com -r 7d -l 20
# Output: top pages ranked by visitor count
bettermeter sources -s example.com --filter ai
# Output: traffic sources. --filter: all (default), ai (AI referrals only), traditional
bettermeter ai-traffic -s example.com
# Output: breakdown by AI platform (ChatGPT, Claude, Perplexity, Gemini, Copilot)
bettermeter bots -s example.com --category ai-crawler
# Output: bot traffic. --category: all (default), ai-crawler, search, monitoring, scraper
bettermeter timeseries -s example.com -r 90d
# Output: daily visitor and pageview counts
bettermeter countries -s example.com
# Output: visitors by country
bettermeter devices -s example.com
# Output: device type breakdown (mobile, tablet, desktop)
bettermeter browsers -s example.com
# Output: browser breakdown
bettermeter export -s example.com --format json
# Output: full analytics report. --format: md (default), json, csv
```
### CLI Analytics Commands
```bash
bettermeter cli-overview -s my-cli-tool
# Output: total invocations, unique callers, success rate, avg duration
bettermeter cli-commands -s my-cli-tool -l 20
# Output: top commands ranked by invocation count
bettermeter cli-timeseries -s my-cli-tool -r 90d
# Output: daily invocation and unique caller counts
```
### MCP Analytics Commands
```bash
bettermeter mcp-overview -s my-mcp-server
# Output: total invocations, unique callers, success rate, avg duration
bettermeter mcp-tools -s my-mcp-server
# Output: top tools ranked by invocation count
bettermeter mcp-clients -s my-mcp-server
# Output: client breakdown (Claude Code, Cursor, Windsurf, etc.)
bettermeter mcp-timeseries -s my-mcp-server -r 90d
# Output: daily invocation and unique caller counts
```
### API Analytics Commands
```bash
bettermeter api-overview -s my-api
# Output: total requests, unique callers, error rate, avg duration
bettermeter api-endpoints -s my-api -l 20
# Output: top endpoints ranked by request count
bettermeter api-timeseries -s my-api -r 90d
# Output: daily request and unique caller counts
```
### Brand Monitoring Commands
```bash
bettermeter brand-report example.com -q "best analytics tool,website analytics"
# Output: brand visibility report with rankings for each query
bettermeter brand-config example.com
# Output: current brand monitoring configuration
# Options: --brand-name , --add-keyword , --remove-keyword ,
# --add-competitor , --remove-competitor ,
# --schedule NONE|DAILY|WEEKLY
bettermeter brand-compare example.com -q "best analytics" -c "plausible.io,fathom.com"
# Output: side-by-side ranking comparison
bettermeter brand-alerts example.com
# Output: list alert rules
# Options: --action list|create|delete
# --type POSITION_DROP|RANKING_LOST|RANKING_GAINED|AI_OVERVIEW_CHANGE
# --threshold (for POSITION_DROP)
# --id (for delete)
```
### Site Management Commands
```bash
bettermeter sites list
bettermeter sites add example.com --name "My Website"
bettermeter sites remove
bettermeter sites info
bettermeter install # Get tracker snippet
```
### Team Management Commands
```bash
bettermeter members list -s
bettermeter members add user@example.com -s -r editor
bettermeter members remove -s
bettermeter members update-role -s -r admin
# Roles: viewer (read-only), editor (modify site), admin (full control)
```
---
## 5. MCP Server — Complete Tool Schemas
BetterMeter runs as a Model Context Protocol server. AI assistants can query analytics data via natural language.
### Configuration
```json
{
"mcpServers": {
"bettermeter": {
"command": "npx",
"args": ["bettermeter"]
}
}
}
```
### Common Tool Parameters
Most analytics tools accept:
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| site_id | string | (required) | The site ID to query |
| range | enum | "30d" | Date range: "today", "7d", "30d", "90d", "12m" |
| limit | integer | 10 | Max results (1-100) |
### Tool Schemas
#### bettermeter_stats
Returns: visitors, pageviews, sessions with period-over-period percentage change.
Parameters: site_id, range, limit
#### bettermeter_pages
Returns: top pages ranked by visitor count.
Parameters: site_id, range, limit
#### bettermeter_sources
Returns: traffic sources with AI referral detection.
Parameters: site_id, range, limit, filter (enum: "all", "ai", "traditional")
#### bettermeter_ai_traffic
Returns: AI traffic breakdown by platform (ChatGPT, Claude, Perplexity, Gemini, Copilot).
Parameters: site_id, range, limit
#### bettermeter_bots
Returns: bot and crawler traffic with category filtering.
Parameters: site_id, range, limit, category (enum: "all", "ai-crawler", "search", "monitoring", "scraper")
#### bettermeter_timeseries
Returns: daily visitor and pageview counts over time.
Parameters: site_id, range, limit
#### bettermeter_countries
Returns: visitors grouped by country.
Parameters: site_id, range, limit
#### bettermeter_devices
Returns: device type breakdown (mobile, tablet, desktop).
Parameters: site_id, range, limit
#### bettermeter_browsers
Returns: browser breakdown.
Parameters: site_id, range, limit
#### bettermeter_visitors
Returns: list of recent visitors with activity summary.
Parameters: site_id, range, limit
#### bettermeter_visitor
Returns: detailed visitor profile with full event timeline.
Parameters: site_id (string), visitor_id (string)
#### bettermeter_events
Returns: custom (non-pageview) events with counts.
Parameters: site_id, range, limit
#### bettermeter_export
Returns: full analytics report.
Parameters: site_id, range, limit, format (enum: "json", "md")
#### bettermeter_cli_overview
Returns: CLI usage overview — total invocations, unique callers, success rate, average duration.
Parameters: site_id, range, limit
#### bettermeter_cli_commands
Returns: top CLI commands ranked by invocation count.
Parameters: site_id, range, limit
#### bettermeter_cli_timeseries
Returns: daily CLI invocation and unique caller counts.
Parameters: site_id, range, limit
#### bettermeter_mcp_overview
Returns: MCP usage overview — total invocations, unique callers, success rate, average duration.
Parameters: site_id, range, limit
#### bettermeter_mcp_tools
Returns: top MCP tools ranked by invocation count.
Parameters: site_id, range, limit
#### bettermeter_mcp_clients
Returns: MCP client breakdown (Claude Code, Cursor, Windsurf, etc.).
Parameters: site_id, range, limit
#### bettermeter_mcp_timeseries
Returns: daily MCP invocation and unique caller counts.
Parameters: site_id, range, limit
#### bettermeter_api_overview
Returns: API usage overview — total requests, unique callers, error rate, average duration.
Parameters: site_id, range, limit
#### bettermeter_api_endpoints
Returns: top API endpoints ranked by request count.
Parameters: site_id, range, limit
#### bettermeter_api_timeseries
Returns: daily API request and unique caller counts.
Parameters: site_id, range, limit
#### bettermeter_sites_list
Returns: all configured sites with their domains and names.
Parameters: (none)
#### bettermeter_sites_add
Creates a new site to track.
Parameters: domain (string, required), name (string, optional)
#### bettermeter_sites_remove
Removes a site. This is destructive and deletes all associated data.
Parameters: site_id (string, required)
#### bettermeter_sites_info
Returns site details including domain, name, creation date, and the tracking snippet.
Parameters: site_id (string, required)
#### bettermeter_install
Returns the HTML tracking snippet for a site.
Parameters: site_id (string, required), tracker_url (string, optional — custom tracker URL)
#### bettermeter_members_list
Returns all members and pending invitations for a site.
Parameters: site_id (string, required)
#### bettermeter_members_add
Adds a member to a site or sends an invitation email.
Parameters: site_id (string), email (string), role (enum: "viewer", "editor", "admin"), all_sites (boolean, optional — grant access to all owner's sites)
#### bettermeter_members_remove
Removes a member from a site.
Parameters: site_id (string), member_id (string)
#### bettermeter_members_update_role
Updates a member's role on a site.
Parameters: site_id (string), member_id (string), role (enum: "viewer", "editor", "admin")
#### bettermeter_brand_report
Generates a brand visibility report by searching queries on Google via SerpAPI.
Parameters: domain (string), queries (string array, max 10)
#### bettermeter_brand_config
Views or updates brand monitoring configuration.
Parameters: domain (string), brandName (string, optional), addKeyword (string, optional), removeKeyword (string, optional), addCompetitor (string, optional), removeCompetitor (string, optional), schedule (enum: "NONE", "DAILY", "WEEKLY", optional)
#### bettermeter_brand_compare
Compares brand rankings against competitors across search queries.
Parameters: domain (string), queries (string array), competitors (string array, optional — uses configured competitors if omitted)
#### bettermeter_brand_alerts
Manages brand alert rules — list, create, or delete alerts.
Parameters: domain (string), action (enum: "list", "create", "delete"), type (enum: "POSITION_DROP", "RANKING_LOST", "RANKING_GAINED", "AI_OVERVIEW_CHANGE", optional), threshold (number, optional), id (string, optional — for delete)
---
## 6. REST API — Full Endpoint Reference
Base URL: `https://bettermeter.com`
Authentication: `Authorization: Bearer `
### Event Ingestion
**POST /api/event** — Ingest an analytics event. Returns 202 Accepted.
```json
{
"site_id": "example.com",
"event_name": "pageview",
"event_source": "web",
"url": "https://example.com/page",
"pathname": "/page",
"hostname": "example.com",
"referrer": "https://google.com",
"screen_width": 1920,
"timezone": "America/New_York",
"user_id": "optional_user_id",
"properties": { "key": "value" }
}
```
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| site_id | string | Yes | Site ID (domain) |
| event_name | string | Yes | Event name: "pageview", "cli.command", "mcp.tool", "api.request", or custom |
| event_source | string | Yes | Source: "web", "cli", "mcp", "api" |
| url | string | Yes | Full URL or synthetic URL (e.g., "cli://my-tool/deploy") |
| pathname | string | Yes | Path portion of URL |
| hostname | string | Yes | Hostname portion |
| referrer | string | Yes | Referrer URL (empty string if none) |
| screen_width | number | Yes | Screen width in pixels (0 for non-web) |
| timezone | string | Yes | IANA timezone string |
| user_id | string | No | Optional user identifier |
| properties | object | No | Key-value custom properties |
### Query Endpoints
All accept query parameters: `siteId` (required), `from` (YYYY-MM-DD), `to` (YYYY-MM-DD).
**Web Analytics:**
- `GET /api/analytics/overview` — Visitors, pageviews, sessions + period-over-period change
- `GET /api/analytics/pages` — Top pages by visitor count
- `GET /api/analytics/sources` — Traffic sources with AI referral detection
- `GET /api/analytics/ai-traffic` — AI referral breakdown by platform
- `GET /api/analytics/bots` — Bot/crawler traffic by category
- `GET /api/analytics/timeseries` — Daily visitor and pageview counts
- `GET /api/analytics/countries` — Visitors grouped by country
- `GET /api/analytics/devices` — Device type breakdown
- `GET /api/analytics/browsers` — Browser breakdown
- `GET /api/analytics/visitors` — Visitor list with activity summary
- `GET /api/analytics/visitors/[visitorId]` — Single visitor profile with event timeline
- `GET /api/analytics/events` — Custom events with counts
- `GET /api/analytics/events/[eventName]` — Events filtered by name
- `GET /api/analytics/all-sites` — Overview across all sites
**CLI Analytics:**
- `GET /api/analytics/cli-overview` — Invocations, callers, success rate, avg duration
- `GET /api/analytics/cli-commands` — Top commands by invocation count
- `GET /api/analytics/cli-timeseries` — Daily CLI invocation counts
**MCP Analytics:**
- `GET /api/analytics/mcp-overview` — Invocations, callers, success rate, avg duration
- `GET /api/analytics/mcp-tools` — Top tools by invocation count
- `GET /api/analytics/mcp-clients` — Client breakdown
- `GET /api/analytics/mcp-timeseries` — Daily MCP invocation counts
**API Analytics:**
- `GET /api/analytics/api-overview` — Requests, callers, error rate, avg duration
- `GET /api/analytics/api-endpoints` — Top endpoints by request count
- `GET /api/analytics/api-timeseries` — Daily API request counts
**Brand Monitoring:**
- `GET /api/analytics/brand` — Brand visibility report
- `GET /api/analytics/brand/history` — Historical brand visibility data
- `GET /api/analytics/brand/compare` — Competitor comparison
- `GET /api/analytics/brand/alerts` — Alert rules
- `GET /api/analytics/brand/alerts/triggered` — Triggered alert history
- `GET /api/analytics/brand/suggest` — Suggested keywords
- `GET /api/analytics/brand/export` — Export brand data
**Other:**
- `GET /api/script` — Serves the browser tracking script (~1KB IIFE)
- `GET /api/pixel` — Noscript tracking pixel
- `GET /api/s` — Proxy-friendly script endpoint
- `GET /api/e` — Proxy-friendly event endpoint
- `GET /api/p` — Proxy-friendly pixel endpoint
---
## 7. Proxy Setup — All Platform Guides
To bypass ad blockers, proxy BetterMeter through your own domain. The browser sees first-party requests instead of third-party `bettermeter.com` requests.
**Required rewrites:**
| Your Path | Proxies To | Purpose |
|-----------|-----------|---------|
| /bm/s | https://bettermeter.com/api/s | Tracker script |
| /bm/e | https://bettermeter.com/api/e | Event endpoint |
| /bm/p | https://bettermeter.com/api/p | Noscript pixel |
**Proxied snippet:**
```html
```
### Next.js
```javascript
// next.config.js
module.exports = {
async rewrites() {
return [
{ source: "/bm/s", destination: "https://bettermeter.com/api/s" },
{ source: "/bm/e", destination: "https://bettermeter.com/api/e" },
{ source: "/bm/p", destination: "https://bettermeter.com/api/p" },
];
},
};
```
### Nuxt
```typescript
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
"/bm/s": { proxy: "https://bettermeter.com/api/s" },
"/bm/e": { proxy: "https://bettermeter.com/api/e" },
"/bm/p": { proxy: "https://bettermeter.com/api/p" },
},
});
```
### Nginx
```nginx
location /bm/s { proxy_pass https://bettermeter.com/api/s; proxy_ssl_server_name on; }
location /bm/e { proxy_pass https://bettermeter.com/api/e; proxy_ssl_server_name on; }
location /bm/p { proxy_pass https://bettermeter.com/api/p; proxy_ssl_server_name on; }
```
Guides also available for SvelteKit, Astro, Remix, Caddy, Apache, and Rails at https://bettermeter.com/docs#proxy-setup.
---
## 8. Privacy Model — Technical Details
### IP Hashing
Raw IP addresses are never stored. BetterMeter creates two hashes:
1. **Daily visitor hash**: `SHA-256(IP + site_id + daily_salt)` — Used for unique visitor counting. Rotates daily, so the same IP produces a different hash each day.
2. **Stable visitor hash**: `SHA-256(IP + site_id + stable_salt)` — Used for visitor profiles. Stable across days but cannot be reversed to an IP address.
### What Is NOT Collected
- IP addresses (raw)
- Cookies / local storage / session storage
- User agent fingerprints (canvas, WebGL, fonts)
- Form input values
- CLI flag values or file paths
- MCP tool input/output content
- API request/response bodies or headers
### Opt-Out Mechanisms
- SDK: `disabled: true` in config or `BETTERMETER_DISABLED=1` environment variable
- Web: Users can block the script with standard ad blockers or browser settings
- Enterprise: Data Processing Agreement (DPA) available for GDPR compliance
---
## 9. Data Model
### Event Table
| Column | Type | Description |
|--------|------|-------------|
| id | UUID | Unique event ID |
| siteId | string | Site identifier |
| eventName | string | "pageview", "cli.command", "mcp.tool", "api.request", or custom |
| eventSource | enum | "web", "cli", "mcp", "api" |
| url | string | Full URL or synthetic URL |
| pathname | string | Path portion |
| hostname | string | Hostname |
| referrer | string | Referrer URL |
| visitorHash | string | SHA-256 daily visitor hash |
| stableVisitorHash | string | SHA-256 stable visitor hash |
| country | string | Country code from geo lookup |
| deviceType | string | "mobile", "tablet", "desktop" |
| browser | string | Browser name |
| os | string | Operating system |
| isBot | boolean | Whether request is from a known bot |
| botName | string | Bot name if detected |
| botCategory | string | "ai-crawler", "search", "monitoring", "scraper" |
| aiPlatform | string | AI platform name if AI referral detected |
| properties | JSON | Custom event properties |
| createdAt | timestamp | Event timestamp |
---
## 10. Brand Monitoring — Full Reference
Brand monitoring tracks how LLMs (ChatGPT, Claude, Gemini) describe your brand across search queries.
### Visibility Score
Each brand gets a visibility score from 0 to 100 based on:
- Search result position (higher = better)
- Presence in AI overview / featured snippets
- Number of queries where the brand appears
### Alert Types
| Type | Description |
|------|-------------|
| POSITION_DROP | Your ranking dropped by N or more positions |
| RANKING_LOST | You disappeared from results for a tracked query |
| RANKING_GAINED | You appeared in results for a new query |
| AI_OVERVIEW_CHANGE | Your presence in AI overview changed |
### Monitoring Schedules
- NONE — Manual reports only
- DAILY — Automated daily visibility check
- WEEKLY — Automated weekly visibility check
---
## 11. Integration Examples
### Complete CLI Tool with Analytics
```typescript
import { Command } from "commander";
import { BetterMeter } from "@bettermeter/node";
const bm = new BetterMeter({
siteId: "my-cli-tool",
apiKey: "bm_...",
batch: true, // Queue events for efficiency
});
const program = new Command();
program.name("mycli").version("1.0.0");
bm.wrapCommander(program, { version: "1.0.0" });
program.command("deploy")
.option("--prod", "Deploy to production")
.action(async (opts) => {
console.log("Deploying...");
// ... your deploy logic
});
program.parse();
// Graceful shutdown
process.on("SIGTERM", () => bm.shutdown());
process.on("SIGINT", () => bm.shutdown());
```
### Complete MCP Server with Analytics
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { BetterMeter } from "@bettermeter/node";
import { z } from "zod";
const bm = new BetterMeter({
siteId: "my-mcp-server",
apiKey: "bm_...",
});
const server = new McpServer({
name: "my-server",
version: "1.0.0",
});
// Wrap BEFORE registering tools
bm.wrapMcpServer(server);
server.tool("search_docs", { query: z.string() }, async ({ query }) => {
const results = await searchDocs(query);
return { content: [{ type: "text", text: JSON.stringify(results) }] };
});
const transport = new StdioServerTransport();
await server.connect(transport);
```
### Complete Express API with Analytics
```typescript
import express from "express";
import { BetterMeter } from "@bettermeter/node";
const bm = new BetterMeter({
siteId: "my-api",
apiKey: "bm_...",
});
const app = express();
app.use(express.json());
app.use(bm.expressMiddleware()); // Add before routes
app.get("/api/users", async (req, res) => {
const users = await getUsers();
res.json(users);
});
app.post("/api/users", async (req, res) => {
const user = await createUser(req.body);
res.status(201).json(user);
});
app.listen(3000);
process.on("SIGTERM", () => bm.shutdown());
```
---
## 12. FAQ
**What is BetterMeter?**
BetterMeter is a privacy-first analytics platform that tracks websites, CLI tools, MCP servers, and APIs through a unified pipeline. It is designed for developers building AI-powered products.
**How does BetterMeter detect AI traffic?**
BetterMeter parses referrer headers and user agent strings for patterns associated with AI platforms (ChatGPT, Claude, Perplexity, Gemini, Copilot). When a visitor arrives from an AI platform, BetterMeter attributes the visit accordingly.
**Does BetterMeter use cookies?**
No. BetterMeter uses no cookies, no local storage, no session storage, and no fingerprinting techniques. It is GDPR, CCPA, and PECR compliant by default without requiring a consent banner.
**How is BetterMeter different from Google Analytics?**
Google Analytics uses cookies, requires consent banners, and only tracks websites. BetterMeter is cookie-free, privacy-first, and tracks four source types: websites, CLI tools, MCP servers, and APIs. BetterMeter also provides AI traffic attribution and bot/crawler analytics.
**How is BetterMeter different from Plausible?**
Plausible is privacy-first like BetterMeter, but only tracks websites. BetterMeter extends privacy-first analytics to CLI tools, MCP servers, and APIs. It also provides AI traffic attribution, bot analytics, and brand monitoring.
**Can I use BetterMeter as an MCP server?**
Yes. BetterMeter runs natively as an MCP server. Add it to your AI assistant's configuration and query analytics with natural language (e.g., "How's my traffic this week?" or "Which AI clients use my MCP server?").
**What data does the CLI/MCP SDK collect?**
Command names, tool names, flag names (not values), duration, exit code, OS, and architecture. File paths, arguments, flag values, and tool input/output content are never collected.
**How do I opt out of tracking?**
Set `disabled: true` in the SDK config or set the `BETTERMETER_DISABLED=1` environment variable. For web tracking, standard ad blockers or browser settings can block the script.
**Is the BetterMeter SDK open source?**
The browser tracking script and Node.js SDK are open source. The dashboard and analytics backend are proprietary.
**What AI bots does BetterMeter detect?**
BetterMeter detects 30+ bots including GPTBot, ChatGPT-User, ClaudeBot, Google-Extended, Googlebot, Bingbot, PerplexityBot, and many more. Bots are categorized as AI crawlers, search engines, monitoring bots, or scrapers.
---
Links:
- Website: https://bettermeter.com
- Documentation: https://bettermeter.com/docs
- Features: https://bettermeter.com/features
- Privacy Policy: https://bettermeter.com/privacy
- Terms of Service: https://bettermeter.com/terms
- DPA: https://bettermeter.com/dpa