API Tracking
Track API endpoint usage, latency, error rates, and caller patterns with Express middleware or manual calls.
import { BetterMeter } from "@bettermeter/node";
import express from "express";
const bm = new BetterMeter({
siteId: "my-api",
apiKey: "bm_...",
});
const app = express();
// Auto-track all requests
app.use(bm.expressMiddleware());bm.trackApi({
method: "POST",
endpoint: "/api/users", // Use patterns, not actual paths with IDs
statusCode: 201,
durationMs: 45,
});What gets tracked
HTTP method, endpoint pattern, status code, and duration. Request/response bodies, headers, query parameters, and path parameters are never sent. Use endpoint patterns (/api/users/:id) not actual paths (/api/users/abc123).
API Reference
Most analytics endpoints are GET with query parameters; each row below shows the method it actually accepts. Authenticate with Authorization: Bearer <api_key>.
Event Ingestion
POST /api/eventIngest an event (web, CLI, MCP, or API). Returns 202.Web events must reference a registered site and the event URL must match that site domain. CLI, MCP, and API events require Authorization: Bearer [api_key]; forwarded user-agent and client-IP headers are trusted only after that key is validated for the site.
{
"site_id": "example.com",
"event_name": "pageview", // or "cli.command", "mcp.tool", "api.request"
"event_source": "web", // "web" | "cli" | "mcp" | "api"
"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" }
}Signup Interest
POST /api/waitlistCapture public waitlist submissions and email farbour@paraito.ca for each new lead. The alert includes clearly labeled name and organization clues derived from the address, account status, same-domain interest, and a link to the admin record. Duplicate submissions do not send another alert.Real-Time & Heartbeat
GET /api/analytics/live?activity=1&cursor=…Live visitor count. Add activity=1 for recent human events, active visitor IDs, goal definitions, and a resumable cursor. Accepts ?siteId=...POST /api/heartbeatReceive browser heartbeats for live visitor trackingPOST /api/hStealth alias for /api/heartbeat (ad-blocker resistant)Query Endpoints
Most GET endpoints accept ?siteId=...&from=YYYY-MM-DD&to=YYYY-MM-DD; any exception is noted on the row. List endpoints also accept limit=all when you need the complete set for pagination or search.
Human-traffic endpoints exclude known and high-confidence automated traffic by default. Pass includeBots=true for unfiltered totals. Overview responses include trafficFilter counts so the exclusion is auditable.
Sites and Access
GET /api/sitesEvery site the caller can access, newest firstPOST /api/sitesCreate a site. Dashboard session only — an API key gets 403 with dashboardOnly: trueGET /api/sites/[siteId]One site's settings and metadataPATCH /api/sites/[siteId]Update site settings — brand name, keywords, competitors, IP filters, currency (Editor or Admin)DELETE /api/sites/[siteId]Delete a site and every event it holds. Admin, and dashboard session onlyGET /api/sites/[siteId]/membersMembers, owner and pending invitations (any role)POST /api/sites/[siteId]/membersAdd a member or send an invitation (Admin)PATCH /api/sites/[siteId]/membersChange a member's role (Admin)DELETE /api/sites/[siteId]/membersRemove a member or cancel a pending invitation (Admin)POST /api/auth/verify-keyVerify an API key and return the account it belongs to, with the sites that account owns. Limited to 10 attempts per minute per IPGET /api/billing/subscriptionCurrent plan, subscription status, plan limits and usage to dateWeb Analytics
GET /api/analytics/overviewVisitors, pageviews, sessions + % change and hasPreviousPeriodData for empty comparison windowsGET /api/analytics/all-sitesRollup across every site the caller can access; takes from and to, without a siteIdGET /api/analytics/pages?limit=allPages by visitor count. Add limit=all to return the full searchable list.GET /api/analytics/sources?limit=allTraffic sources with AI detectionGET /api/analytics/timeseriesDaily visitor/pageview trendGET /api/analytics/ai-trafficAI referral breakdown by platformGET /api/analytics/bots?limit=allBot/crawler trafficGET /api/analytics/countries?limit=allVisitors by countryGET /api/analytics/devicesDevice type breakdownGET /api/analytics/browsers?limit=allBrowser breakdownGET /api/analytics/visitors?limit=allVisitor list with activityGET /api/analytics/visitors/findFind a visitor by email, CRM contact id, or any property key/value (Editor or Admin, because an email resolves to a named person)GET /api/analytics/visitors/[visitorId]Person profile with first-touch attribution, sessions, lifecycle, and event timelinePATCH /api/analytics/visitors/[visitorId]Update the person's identity and lifecycle (Editor or Admin)GET /api/analytics/visitors/[visitorId]/propertiesRead every key/value property on a visitor, with the source that wrote eachPATCH /api/analytics/visitors/[visitorId]/propertiesWrite or delete visitor properties; a null value deletes the key (Editor or Admin)POST /api/visitor-propertiesPublic endpoint used by the tracker's setProperties(); the visitor is derived from the request, never supplied by the callerGET /api/analytics/events?limit=allCustom eventsGET /api/analytics/events/[eventName]One custom event in detail — total fires, unique visitors, and property facets counted over the whole rangeGET /api/analytics/campaigns?limit=allAutomatically captured campaign URL attribution (UTM + click IDs)GET /api/analytics/campaigns/[campaign]Single campaign detail — visit quality (bounce rate, avg duration, quality score vs. site average), variant/keyword performance, device/browser/country breakdowns, hourly pattern, custom eventsGET /api/analytics/campaigns/[campaign]/visitorsThe people one campaign brought in, with each visitor's landing page, variant, device, country and post-click activityGET /api/analytics/keywords?limit=allKeywords (utm_term) aggregated across campaigns with visit quality (bounce rate, avg duration) and campaign/source attributionGET /api/analytics/marketingMarketing performance: campaigns, landing pages, channel mix, campaign vs non-campaign trafficGET /api/analytics/channelsChannel breakdownGET /api/analytics/session-statsSession statisticsGET /api/analytics/sessions?limit=allSession listCLI Analytics
GET /api/analytics/cli-overviewInvocations, callers, success rateGET /api/analytics/cli-commands?limit=allTop commandsGET /api/analytics/cli-timeseriesDaily CLI activityMCP Analytics
GET /api/analytics/mcp-overviewInvocations, callers, success rateGET /api/analytics/mcp-tools?limit=allTop toolsGET /api/analytics/mcp-clients?limit=allClient breakdownGET /api/analytics/mcp-timeseriesDaily MCP activityAPI Analytics
GET /api/analytics/api-overviewInvocations, callers, error rateGET /api/analytics/api-endpoints?limit=allTop endpointsGET /api/analytics/api-timeseriesDaily API activityConversion Goals
GET /api/analytics/goalsList all conversion goals with conversions, unique converters, and conversion ratesPOST /api/analytics/goalsCreate a new conversion goal (pageview URL, custom event, outbound link, or download)GET /api/analytics/goals/[goalId]Get human-only current/prior evolution, stable converters, first-touch acquisition, exposed-visitor efficiency, and momentum; includeBots=true opts into automationPATCH /api/analytics/goals/[goalId]Update a conversion goal definitionDELETE /api/analytics/goals/[goalId]Delete a conversion goalGET /api/analytics/goals/suggestAnalyze traffic patterns and suggest high-value conversion targetsBuilder
GET /api/builders/dashboardsList saved custom dashboards for a site and its left-nav dashboard menuPOST /api/builders/dashboardsCreate a custom dashboardGET /api/builders/dashboards/[dashboardId]Get one custom dashboardPATCH /api/builders/dashboards/[dashboardId]Update a custom dashboardDELETE /api/builders/dashboards/[dashboardId]Delete a custom dashboardGET /api/builders/reportsList saved report templatesPOST /api/builders/reportsCreate a report templateGET /api/builders/reports/[reportId]Get one report templatePATCH /api/builders/reports/[reportId]Update a report templateDELETE /api/builders/reports/[reportId]Delete a report templatePOST /api/builders/reports/previewPreview a report outlineSearch Rankings and AI Visibility
GET /api/analytics/brandSearch rankings visibility reportPOST /api/analytics/brandRun a full brand report and store it for historical tracking; falls back to the site's saved keywords (Editor or Admin, brand monitoring plan)GET /api/analytics/brand/historyHistorical ranking data. Dashboard session only — this route does not accept an API keyPOST /api/analytics/brand/compareCompetitor comparison — POST siteId, with competitors and queries optional (Editor or Admin, brand monitoring plan)GET /api/analytics/brand/alertsRanking alert rulesPOST /api/analytics/brand/alertsCreate a ranking alert rule (Editor or Admin)PATCH /api/analytics/brand/alertsEnable or disable an alert rule (Editor or Admin)DELETE /api/analytics/brand/alerts?id=…Delete an alert rule; the rule id goes in the query string (Editor or Admin)GET /api/analytics/brand/exportExport ranking data. Dashboard session only — this route does not accept an API keyGET /api/analytics/backlinksBacklink profileGET /api/analytics/ai-mentionsAI chatbot brand mentionsPOST /api/analytics/ai-mentionsStart a background AI-mention scan and return a job id; poll GET /api/analytics/ai-mentions/job?id=… for progress (Editor or Admin)GET /api/analytics/ai-mentions/historyAI mention historyPulse AI
GET /api/pulse/insightsAnomalies, trends, opportunities, and domain shiftsGET /api/pulse/healthProduct health score (0-100)GET /api/pulse/briefingDaily or weekly briefingGET /api/pulse/forecastTraffic/usage forecastGET /api/pulse/comparePeriod comparisonGET /api/pulse/alertsList monitoring alertsPOST /api/pulse/alertsCreate alert ruleDELETE /api/pulse/alertsDelete alert ruleGET /api/pulse/notificationsGet notificationsPATCH /api/pulse/notificationsMark notifications as readPOST /api/pulse/chatStreaming Pulse chat with adaptive, linked visitor-level answersGET /api/pulse/conversationsList your Pulse conversation threads for a sitePOST /api/pulse/conversationsStart a new Pulse conversation threadGET /api/pulse/conversations/[id]One thread with its full message historyPATCH /api/pulse/conversations/[id]Rename a conversation threadDELETE /api/pulse/conversations/[id]Delete a conversation and its messagesGET /api/pulse/screenshotCapture a landing page on your own site as an authenticated, same-origin imageAd Spend & Revenue
GET /api/analytics/ad-spendCampaign spend joined to tracked trafficGET /api/analytics/conversionsCRM records that carried one of our ad click IDs; wonOnly=true for closed-won only, withAds=true to resolve the ad behind each oneGET /api/analytics/ad-creativeThe ad behind one visit — exact when the URL carried the ad's id or its name, campaign-level when only the campaign is known, and no match at all when neither is — or every synced ad in a campaign with list=true. Resolves a single visit rather than a period, so it takes no date rangeGET /api/analytics/ad-creative/[creativeId]/imageThe creative image, proxied from our own origin so provider CDN URLs never reach the browser. Takes siteId and no date rangeGET /api/integrations/adsList connected ad accounts for a sitePOST /api/integrations/adsConnect a Meta ad account with a system user tokenDELETE /api/integrations/ads?id=…Disconnect an ad account by id; the spend already recorded is kept (Admin)POST /api/integrations/ads/syncPull ad spend now instead of waiting for the nightly sync