Introduction

Pulse Analytics is a privacy-friendly, self-hosted web analytics platform built in Rust. It provides real-time pageview tracking, custom events, user segmentation, funnel analysis, A/B testing, surveys, error tracking, and more — all through a single, fast API.

Base URL

https://pulse.ayushojha.com

All API endpoints are relative to this base URL. The API accepts and returns JSON unless otherwise noted (e.g., CSV exports).

Quick Start

Get Your API Keys

Register your project with the admin API and create ingest + query scoped API keys.

Add the Tracking Script

Drop a single <script> tag into your site’s HTML before the closing </body> tag.

View Your Dashboard

Open pulse.ayushojha.com/dashboard and log in with your query-scoped API key.

Quick Install
<!-- Add before </body> -->
<script defer
  src="https://pulse.ayushojha.com/api/script.js"
  data-api="https://pulse.ayushojha.com"
  data-key="pa_live_YOUR_INGEST_KEY"></script>

Authentication

Pulse uses API key authentication for all data operations and bearer-token authentication for admin endpoints.

API Key Authentication

Pass your project API key in one of two ways:

MethodExample
Header (preferred)X-Pulse-Key: pa_live_YOUR_KEY
Query parameter?key=pa_live_YOUR_KEY

Admin Token Authentication

Administrative endpoints (creating projects, managing keys) require the admin bearer token:

Admin Auth Header
Authorization: Bearer <PULSE_ADMIN_TOKEN>

API Key Scopes

Each API key has one or more scopes that determine its permissions:

ScopePermissionsTypical Use
ingestWrite tracking data (pageviews, events, errors, etc.)Frontend tracking script
queryRead analytics data (stats, reports, exports)Dashboard access, API queries
adminFull access including key managementBackend administration

Module Restrictions

API keys can optionally be restricted to specific modules via the allowed_modules field. When set, the key can only access the listed modules. When null or empty, the key has access to all enabled modules for the project.

Security tip: Create separate ingest and query keys for each project. Never expose query or admin keys on the frontend.

Module System

Pulse features a configurable module system that lets you enable or disable feature sets per project. Each module controls access to related API endpoints and data collection. Modules have access levels: read, write, or all (both).

Available Modules

Pulse ships with 23 modules across five categories:

Core

Core
pageviews

Page view tracking and top pages reporting

Core
events

Custom event tracking with metadata

Core
sessions

Session detection and duration metrics

Core
referrers

Referrer / traffic source analysis

Core
devices

Browser, OS, and device type breakdown

Core
geo

Country-level geographic distribution

Core
realtime

Active visitors in the last 5 minutes

Engagement

Engagement
utm

UTM campaign parameter tracking

Engagement
funnels

Multi-step conversion funnels

Engagement
goals

Goal and conversion tracking

Engagement
retention

User retention cohort analysis

Engagement
cohorts

Grouped cohort metric comparison

Engagement
paths

User navigation path analysis

Performance

Performance
webvitals

Core Web Vitals (LCP, FCP, CLS, INP, TTFB)

Performance
error_tracking

JavaScript error capture and grouping

Performance
heatmaps

Click heatmap data collection

Experimentation

Experimentation
ab_testing

A/B experiment management and results

Experimentation
surveys

In-app survey builder and response collection

Experimentation
search

Internal site search query tracking

Platform

Platform
exports

CSV data export for all report types

Platform
sharing

Password-protected shared dashboards

Platform
alerts

Threshold-based alert rules and notifications

Platform
scroll_depth

Scroll depth measurement per page

Module Admin Endpoints

GET /api/admin/projects/{id}/modules

List all modules and their current status for a project.

Request
curl -H "Authorization: Bearer <ADMIN_TOKEN>" \
  https://pulse.ayushojha.com/api/admin/projects/4c3654c3-9274-4fea-bc3e-30b8edcd7f7a/modules
Response 200
{
  "modules": [
    { "name": "pageviews", "enabled": true, "access": "all" },
    { "name": "events", "enabled": true, "access": "all" },
    { "name": "funnels", "enabled": false, "access": "read" },
    { "name": "ab_testing", "enabled": false, "access": "read" }
  ]
}
PUT /api/admin/projects/{id}/modules

Bulk update module settings for a project.

Request
curl -X PUT \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "modules": [
      {"name": "funnels", "enabled": true, "access": "all"},
      {"name": "ab_testing", "enabled": true, "access": "all"},
      {"name": "heatmaps", "enabled": false}
    ]
  }' \
  https://pulse.ayushojha.com/api/admin/projects/4c3654c3-9274-4fea-bc3e-30b8edcd7f7a/modules
POST /api/admin/projects/{id}/modules/{name}/enable

Enable a single module.

Request
curl -X POST \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  https://pulse.ayushojha.com/api/admin/projects/4c3654c3-9274-4fea-bc3e-30b8edcd7f7a/modules/funnels/enable
POST /api/admin/projects/{id}/modules/{name}/disable

Disable a single module. Existing data is preserved but API access is revoked.

Request
curl -X POST \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  https://pulse.ayushojha.com/api/admin/projects/4c3654c3-9274-4fea-bc3e-30b8edcd7f7a/modules/heatmaps/disable
PUT /api/admin/projects/{id}/modules/{name}/access

Set the access level for a module: read, write, or all.

Request
curl -X PUT \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"access": "read"}' \
  https://pulse.ayushojha.com/api/admin/projects/4c3654c3-9274-4fea-bc3e-30b8edcd7f7a/modules/surveys/access

Tracking Script

The easiest way to integrate Pulse is with the lightweight JavaScript tracking script. It automatically captures pageviews, handles SPAs, and supports optional modules like Web Vitals, scroll depth, outbound link tracking, and more.

HTML Snippet Installation

HTML
<script defer
  src="https://pulse.ayushojha.com/api/script.js"
  data-api="https://pulse.ayushojha.com"
  data-key="pa_live_YOUR_INGEST_KEY"
  data-utm="true"
  data-vitals="true"
  data-scroll="true"
  data-outlinks="true"
  data-errors="true"
  data-clicks="true"
  data-search="true"
  data-search-param="q"
  data-dnt="false"></script>

Data Attributes

AttributeDefaultDescription
data-keyRequired. Your project ingest API key.
data-apihttps://pulse.ayushojha.comAPI base URL. Override for self-hosted instances.
data-utmfalseCapture UTM parameters from query strings.
data-vitalsfalseCollect Core Web Vitals (LCP, FCP, CLS, INP, TTFB).
data-scrollfalseTrack maximum scroll depth per page.
data-outlinksfalseTrack clicks on outbound links.
data-errorsfalseCapture unhandled JavaScript errors.
data-clicksfalseRecord click coordinates for heatmaps.
data-searchfalseTrack internal site search queries.
data-search-paramqURL query parameter name for search queries.
data-dntfalseRespect browser Do Not Track setting.

JavaScript API

Use the global pulse() function to send custom data programmatically:

Custom Event Tracking
JavaScript
// Track a custom event
pulse("event", {
  name: "button_click",
  data: { button_id: "cta-hero", variant: "blue" }
});

// Track an event with revenue
pulse("event", {
  name: "purchase",
  revenue_amount: 49.99,
  revenue_currency: "USD",
  data: { plan: "pro", billing: "annual" }
});
Search Query Tracking
JavaScript
// Track a search query
pulse("search", {
  query: "deployment guide",
  results_count: 12
});
Survey Response Submission
JavaScript
// Submit survey response
pulse("survey_response", {
  survey_id: "c7f3e8a2-91b4-4d6e-b2f1-8a3c9d4e5f6a",
  answers: [
    { question_id: "q1", value: "Very satisfied" },
    { question_id: "q2", value: 9 }
  ],
  completed: true
});

TypeScript SDK

Client-side (Browser)
Install
npm install @pulse-analytics/sdk
TypeScript
import { PulseClient } from '@pulse-analytics/sdk';

const pulse = new PulseClient({
  apiKey: 'pa_live_YOUR_INGEST_KEY',
  apiUrl: 'https://pulse.ayushojha.com',
  utm: true,
  vitals: true,
  scrollDepth: true
});

pulse.init();

// Track custom events
pulse.event('signup_complete', { method: 'google' });

// Track revenue
pulse.event('purchase', {
  revenue: { amount: 29.99, currency: 'USD' }
});
Server-side (Node.js)
TypeScript
import { PulseServer } from '@pulse-analytics/sdk/server';

const pulse = new PulseServer({
  apiKey: 'pa_live_YOUR_INGEST_KEY',
  apiUrl: 'https://pulse.ayushojha.com'
});

// Server-side event
await pulse.collect({
  type: 'event',
  name: 'subscription_renewed',
  data: { plan: 'enterprise', mrr: 499 }
});

Ingestion API

All tracking data flows through a single universal collection endpoint. The payload type determines which module processes the data.

POST /api/collect

Universal data collection endpoint. Accepts any payload type.

HeaderValue
X-Pulse-KeyYour ingest-scoped API key
Content-Typeapplication/json

Payload Types

POST  pageview

Record a page view. Sent automatically by the tracking script.

Request
curl -X POST https://pulse.ayushojha.com/api/collect \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "pageview",
    "path": "/pricing",
    "title": "Pricing - Acme Corp",
    "referrer": "https://google.com",
    "screen": "1920x1080",
    "language": "en-US",
    "utm_source": "google",
    "utm_medium": "cpc",
    "utm_campaign": "spring_sale",
    "utm_content": "banner_a",
    "utm_term": "analytics tool"
  }'
Response 200
{ "ok": true }
POST  event

Track a custom event with optional metadata and revenue.

Request
curl -X POST https://pulse.ayushojha.com/api/collect \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "event",
    "name": "add_to_cart",
    "path": "/products/widget-pro",
    "data": {
      "product_id": "prod_8x92k",
      "product_name": "Widget Pro",
      "quantity": 2
    },
    "revenue_amount": 59.98,
    "revenue_currency": "USD"
  }'
POST  identify

Associate traits with the current visitor for segmentation.

Request
curl -X POST https://pulse.ayushojha.com/api/collect \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "identify",
    "traits": {
      "plan": "pro",
      "company": "Acme Corp",
      "role": "admin",
      "signed_up": "2025-08-14"
    }
  }'
POST  web_vital

Submit a Core Web Vital measurement.

Request
curl -X POST https://pulse.ayushojha.com/api/collect \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "web_vital",
    "name": "LCP",
    "value": 1842.5,
    "rating": "good",
    "path": "/home"
  }'
POST  scroll_depth

Report the maximum scroll depth reached on a page (0-100).

Request
curl -X POST https://pulse.ayushojha.com/api/collect \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "scroll_depth",
    "path": "/blog/getting-started",
    "max_depth": 87
  }'
POST  search_query

Track an internal site search query and result count.

Request
curl -X POST https://pulse.ayushojha.com/api/collect \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "search_query",
    "query": "pricing plans",
    "results_count": 7,
    "path": "/search"
  }'
POST  outlink

Track an outbound link click.

Request
curl -X POST https://pulse.ayushojha.com/api/collect \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "outlink",
    "url": "https://github.com/acme/widget",
    "link_type": "external",
    "path": "/docs"
  }'
POST  js_error

Report a JavaScript error with stack trace.

Request
curl -X POST https://pulse.ayushojha.com/api/collect \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "js_error",
    "message": "TypeError: Cannot read properties of null (reading '\''id'\'')",
    "stack": "TypeError: Cannot read properties of null\n    at handleClick (app.js:142:18)\n    at onClick (react-dom.js:3905:14)",
    "filename": "https://acme.com/assets/app.js",
    "lineno": 142,
    "colno": 18,
    "path": "/dashboard"
  }'
POST  click_event

Record a click event with coordinates for heatmap generation.

Request
curl -X POST https://pulse.ayushojha.com/api/collect \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "click_event",
    "path": "/pricing",
    "x": 482,
    "y": 1203,
    "element_selector": "button.cta-primary",
    "viewport_width": 1440,
    "viewport_height": 900
  }'
POST  survey_response

Submit a user’s response to an active survey.

Request
curl -X POST https://pulse.ayushojha.com/api/collect \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "survey_response",
    "survey_id": "c7f3e8a2-91b4-4d6e-b2f1-8a3c9d4e5f6a",
    "answers": [
      {"question_id": "q1", "value": "Very satisfied"},
      {"question_id": "q2", "value": 9},
      {"question_id": "q3", "value": "Love the real-time dashboard!"}
    ],
    "completed": true,
    "path": "/app/settings"
  }'

Core Analytics API

These endpoints are always available regardless of module configuration. They provide the fundamental analytics data for your project. All endpoints require a query-scoped API key.

Common query parameters: All core endpoints accept start_at (ISO 8601), end_at (ISO 8601), limit (default 10), and offset (default 0) unless otherwise noted.
GET /api/v1/stats

Overview metrics for the specified date range, with comparison to the previous period of equal length.

ParameterTypeDescription
start_atstringPeriod start (ISO 8601). Default: 30 days ago.
end_atstringPeriod end (ISO 8601). Default: now.
Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/stats?start_at=2026-03-01T00:00:00Z&end_at=2026-03-22T23:59:59Z"
Response 200
{
  "current": {
    "pageviews": 14832,
    "visitors": 3241,
    "sessions": 4567,
    "bounce_rate": 42.3,
    "avg_duration": 187.5,
    "events": 2891
  },
  "previous": {
    "pageviews": 12104,
    "visitors": 2890,
    "sessions": 3921,
    "bounce_rate": 45.1,
    "avg_duration": 162.8,
    "events": 2340
  }
}
GET /api/v1/stats/timeseries

Daily time series of core metrics within the date range.

Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/stats/timeseries?start_at=2026-03-15&end_at=2026-03-22"
Response 200
{
  "data": [
    { "date": "2026-03-15", "pageviews": 687, "visitors": 198, "sessions": 245 },
    { "date": "2026-03-16", "pageviews": 524, "visitors": 161, "sessions": 203 },
    { "date": "2026-03-17", "pageviews": 731, "visitors": 212, "sessions": 278 },
    { "date": "2026-03-18", "pageviews": 812, "visitors": 234, "sessions": 301 },
    { "date": "2026-03-19", "pageviews": 695, "visitors": 189, "sessions": 256 },
    { "date": "2026-03-20", "pageviews": 903, "visitors": 267, "sessions": 342 },
    { "date": "2026-03-21", "pageviews": 778, "visitors": 221, "sessions": 289 },
    { "date": "2026-03-22", "pageviews": 411, "visitors": 134, "sessions": 167 }
  ]
}
GET /api/v1/pages

Top pages ranked by views.

Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/pages?limit=5"
Response 200
{
  "data": [
    { "path": "/", "views": 4821, "unique_views": 2190, "avg_duration": 45.2 },
    { "path": "/pricing", "views": 2314, "unique_views": 1820, "avg_duration": 92.7 },
    { "path": "/docs/getting-started", "views": 1892, "unique_views": 1456, "avg_duration": 214.3 },
    { "path": "/blog/analytics-guide", "views": 1203, "unique_views": 987, "avg_duration": 312.1 },
    { "path": "/signup", "views": 891, "unique_views": 834, "avg_duration": 67.8 }
  ],
  "total": 47
}
GET /api/v1/referrers

Traffic sources ranked by visitor count.

Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/referrers?limit=5"
Response 200
{
  "data": [
    { "source": "(direct)", "visitors": 1245, "pageviews": 3891 },
    { "source": "google.com", "visitors": 892, "pageviews": 2104 },
    { "source": "twitter.com", "visitors": 341, "pageviews": 567 },
    { "source": "github.com", "visitors": 287, "pageviews": 445 },
    { "source": "reddit.com", "visitors": 198, "pageviews": 312 }
  ]
}
GET /api/v1/events

Custom events ranked by count.

Response 200
{
  "data": [
    { "name": "signup_complete", "count": 423, "unique": 412 },
    { "name": "add_to_cart", "count": 312, "unique": 287 },
    { "name": "purchase", "count": 89, "unique": 84 },
    { "name": "newsletter_subscribe", "count": 67, "unique": 65 }
  ]
}
GET /api/v1/devices

Visitor breakdown by browser, operating system, and device type.

Response 200
{
  "browsers": [
    { "name": "Chrome", "visitors": 1842, "percentage": 56.8 },
    { "name": "Safari", "visitors": 723, "percentage": 22.3 },
    { "name": "Firefox", "visitors": 412, "percentage": 12.7 }
  ],
  "operating_systems": [
    { "name": "Windows", "visitors": 1456, "percentage": 44.9 },
    { "name": "macOS", "visitors": 987, "percentage": 30.5 },
    { "name": "iOS", "visitors": 534, "percentage": 16.5 }
  ],
  "device_types": [
    { "name": "Desktop", "visitors": 2198, "percentage": 67.8 },
    { "name": "Mobile", "visitors": 845, "percentage": 26.1 },
    { "name": "Tablet", "visitors": 198, "percentage": 6.1 }
  ]
}
GET /api/v1/geo

Visitor distribution by country.

Response 200
{
  "data": [
    { "country": "US", "visitors": 1567, "percentage": 48.3 },
    { "country": "GB", "visitors": 412, "percentage": 12.7 },
    { "country": "DE", "visitors": 298, "percentage": 9.2 },
    { "country": "IN", "visitors": 234, "percentage": 7.2 },
    { "country": "CA", "visitors": 189, "percentage": 5.8 }
  ]
}
GET /api/v1/realtime

Count of active visitors on the site right now (within the last 5 minutes).

Response 200
{
  "active_visitors": 23
}

UTM / Campaign Tracking

Requires the utm module to be enabled. Provides campaign-level analytics from UTM parameters captured during pageviews.

GET /api/v1/campaigns

Campaign performance metrics grouped by UTM campaign name.

Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/campaigns?start_at=2026-03-01&end_at=2026-03-22"
Response 200
{
  "data": [
    {
      "campaign": "spring_sale",
      "visitors": 892,
      "pageviews": 2341,
      "bounce_rate": 38.2,
      "avg_duration": 142.5
    },
    {
      "campaign": "product_launch",
      "visitors": 456,
      "pageviews": 1023,
      "bounce_rate": 44.7,
      "avg_duration": 98.3
    }
  ]
}
GET /api/v1/campaigns/sources

Top UTM sources (e.g., google, newsletter, twitter).

Response 200
{
  "data": [
    { "source": "google", "visitors": 1234, "pageviews": 3456 },
    { "source": "newsletter", "visitors": 567, "pageviews": 892 },
    { "source": "twitter", "visitors": 345, "pageviews": 512 }
  ]
}
GET /api/v1/campaigns/mediums

Top UTM mediums (e.g., cpc, email, social).

Response 200
{
  "data": [
    { "medium": "cpc", "visitors": 1102, "pageviews": 2891 },
    { "medium": "email", "visitors": 678, "pageviews": 1234 },
    { "medium": "social", "visitors": 445, "pageviews": 678 }
  ]
}
GET /api/v1/campaigns/timeseries

Daily time series for a specific UTM source.

ParameterTypeDescription
utm_sourcestringRequired. Filter by UTM source.
Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/campaigns/timeseries?utm_source=google&start_at=2026-03-18&end_at=2026-03-22"
Response 200
{
  "data": [
    { "date": "2026-03-18", "visitors": 89, "pageviews": 234 },
    { "date": "2026-03-19", "visitors": 102, "pageviews": 287 },
    { "date": "2026-03-20", "visitors": 78, "pageviews": 198 },
    { "date": "2026-03-21", "visitors": 95, "pageviews": 256 },
    { "date": "2026-03-22", "visitors": 67, "pageviews": 178 }
  ]
}

Funnels

Requires the funnels module. Define multi-step conversion funnels to understand where users drop off in key flows.

GET /api/v1/funnels

List all funnels for the project.

Response 200
{
  "data": [
    {
      "id": "f1a2b3c4-5678-9abc-def0-1234567890ab",
      "name": "Signup Flow",
      "steps": 4,
      "created_at": "2026-02-14T10:30:00Z"
    }
  ]
}
POST /api/v1/funnels

Create a new funnel. Steps are evaluated in order.

Request
curl -X POST \
  -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Signup Flow",
    "steps": [
      {"type": "pageview", "value": "/", "label": "Homepage"},
      {"type": "pageview", "value": "/pricing", "label": "Pricing Page"},
      {"type": "pageview", "value": "/signup", "label": "Signup Page"},
      {"type": "event", "value": "signup_complete", "label": "Signup Complete"}
    ]
  }' \
  https://pulse.ayushojha.com/api/v1/funnels
Response 201
{
  "id": "f1a2b3c4-5678-9abc-def0-1234567890ab",
  "name": "Signup Flow",
  "steps": [
    { "type": "pageview", "value": "/", "label": "Homepage" },
    { "type": "pageview", "value": "/pricing", "label": "Pricing Page" },
    { "type": "pageview", "value": "/signup", "label": "Signup Page" },
    { "type": "event", "value": "signup_complete", "label": "Signup Complete" }
  ],
  "created_at": "2026-03-22T14:30:00Z"
}
GET /api/v1/funnels/{id}

Get a specific funnel definition.

PUT /api/v1/funnels/{id}

Update a funnel’s name or steps.

Request
curl -X PUT \
  -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Signup Flow v2",
    "steps": [
      {"type": "pageview", "value": "/", "label": "Homepage"},
      {"type": "pageview", "value": "/signup", "label": "Signup Page"},
      {"type": "event", "value": "signup_complete", "label": "Signup Complete"}
    ]
  }' \
  https://pulse.ayushojha.com/api/v1/funnels/f1a2b3c4-5678-9abc-def0-1234567890ab
DELETE /api/v1/funnels/{id}

Delete a funnel. Returns 204 No Content on success.

GET /api/v1/funnels/{id}/analyze

Analyze funnel performance. Returns visitor counts and drop-off rates for each step.

Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/funnels/f1a2b3c4-5678-9abc-def0-1234567890ab/analyze?start_at=2026-03-01&end_at=2026-03-22"
Response 200
{
  "funnel_id": "f1a2b3c4-5678-9abc-def0-1234567890ab",
  "name": "Signup Flow",
  "steps": [
    { "label": "Homepage", "visitors": 3241, "drop_off": 0, "conversion_rate": 100.0 },
    { "label": "Pricing Page", "visitors": 1820, "drop_off": 1421, "conversion_rate": 56.2 },
    { "label": "Signup Page", "visitors": 834, "drop_off": 986, "conversion_rate": 45.8 },
    { "label": "Signup Complete", "visitors": 412, "drop_off": 422, "conversion_rate": 49.4 }
  ],
  "overall_conversion": 12.7
}

Goals / Conversions

Requires the goals module. Define conversion goals based on pageviews, events, session duration, or pages per session, and track their performance.

GET /api/v1/goals

List all configured goals.

Response 200
{
  "data": [
    {
      "id": "g9a8b7c6-5432-1fed-cba0-987654321012",
      "name": "Completed Purchase",
      "goal_type": "event",
      "config": { "event_name": "purchase" },
      "created_at": "2026-01-20T08:00:00Z"
    },
    {
      "id": "g1b2c3d4-5678-9abc-def0-abcdef123456",
      "name": "Visited Pricing",
      "goal_type": "pageview",
      "config": { "path": "/pricing" },
      "created_at": "2026-01-20T08:15:00Z"
    }
  ]
}
POST /api/v1/goals

Create a new goal. Supported types: pageview, event, duration, pages_per_session.

Request — Event Goal
curl -X POST \
  -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Completed Purchase",
    "goal_type": "event",
    "config": {
      "event_name": "purchase"
    }
  }' \
  https://pulse.ayushojha.com/api/v1/goals
Request — Duration Goal
curl -X POST \
  -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Engaged Session (>2min)",
    "goal_type": "duration",
    "config": {
      "threshold_seconds": 120
    }
  }' \
  https://pulse.ayushojha.com/api/v1/goals
GET /api/v1/goals/{id}

Get a specific goal definition.

PUT /api/v1/goals/{id}

Update a goal’s name or configuration.

DELETE /api/v1/goals/{id}

Delete a goal. Returns 204 No Content.

GET /api/v1/goals/{id}/stats

Get conversion statistics for a specific goal.

Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/goals/g9a8b7c6-5432-1fed-cba0-987654321012/stats?start_at=2026-03-01&end_at=2026-03-22"
Response 200
{
  "goal_id": "g9a8b7c6-5432-1fed-cba0-987654321012",
  "name": "Completed Purchase",
  "conversions": 89,
  "unique_visitors": 84,
  "total_revenue": 4287.56,
  "conversion_rate": 2.59
}

Retention

Requires the retention module. Analyze how well your product retains users over time with cohort-based retention tables.

GET /api/v1/retention

Get retention cohorts showing return rates at various intervals.

ParameterTypeDescription
periodstringCohort period: daily, weekly (default), or monthly.
start_atstringPeriod start (ISO 8601).
end_atstringPeriod end (ISO 8601).
Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/retention?period=weekly&start_at=2026-02-01&end_at=2026-03-22"
Response 200
{
  "period": "weekly",
  "cohorts": [
    {
      "cohort": "2026-02-03",
      "initial_visitors": 412,
      "D1": 68.2,
      "D7": 42.5,
      "D14": 31.8,
      "D30": 22.1,
      "D60": 14.3,
      "D90": null
    },
    {
      "cohort": "2026-02-10",
      "initial_visitors": 387,
      "D1": 71.0,
      "D7": 45.2,
      "D14": 33.4,
      "D30": 24.8,
      "D60": null,
      "D90": null
    },
    {
      "cohort": "2026-02-17",
      "initial_visitors": 445,
      "D1": 65.8,
      "D7": 39.7,
      "D14": 28.9,
      "D30": 19.6,
      "D60": null,
      "D90": null
    }
  ]
}

Cohorts

Requires the cohorts module. Group visitors into cohorts and compare metrics across time periods.

GET /api/v1/cohorts

Get cohort analysis data grouped by week or month.

ParameterTypeDescription
group_bystringGrouping period: week (default) or month.
metricstringMetric to compare: pageviews, sessions, events, or revenue.
start_atstringPeriod start (ISO 8601).
end_atstringPeriod end (ISO 8601).
Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/cohorts?group_by=month&metric=sessions&start_at=2026-01-01&end_at=2026-03-22"
Response 200
{
  "group_by": "month",
  "metric": "sessions",
  "cohorts": [
    { "period": "2026-01", "visitors": 2890, "total": 3921, "avg_per_visitor": 1.36 },
    { "period": "2026-02", "visitors": 3105, "total": 4234, "avg_per_visitor": 1.36 },
    { "period": "2026-03", "visitors": 3241, "total": 4567, "avg_per_visitor": 1.41 }
  ]
}

Path Analysis

Requires the paths module. Discover the most common navigation flows before or after a given page.

GET /api/v1/paths

Get page flow data showing where visitors go to or come from.

ParameterTypeDescription
pathstringRequired. The pivot page path (e.g., /pricing).
directionstringforward (default) — where visitors go next. backward — where they came from.
limitintegerNumber of paths to return. Default: 10.
Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/paths?path=/pricing&direction=forward&limit=5"
Response 200
{
  "path": "/pricing",
  "direction": "forward",
  "flows": [
    { "next_path": "/signup", "visitors": 456, "percentage": 25.1 },
    { "next_path": "/", "visitors": 312, "percentage": 17.1 },
    { "next_path": "/features", "visitors": 234, "percentage": 12.9 },
    { "next_path": "/docs", "visitors": 178, "percentage": 9.8 },
    { "next_path": "(exit)", "visitors": 640, "percentage": 35.2 }
  ]
}

Web Vitals

Requires the webvitals module. Monitor Core Web Vitals performance across your site with percentile breakdowns.

GET /api/v1/webvitals

Summary of all Web Vitals with p50, p75, and p99 percentiles.

Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/webvitals?start_at=2026-03-01&end_at=2026-03-22"
Response 200
{
  "vitals": {
    "LCP": { "p50": 1240, "p75": 2180, "p99": 5420, "rating": "good" },
    "FCP": { "p50": 820, "p75": 1450, "p99": 3890, "rating": "good" },
    "CLS": { "p50": 0.04, "p75": 0.09, "p99": 0.32, "rating": "good" },
    "INP": { "p50": 120, "p75": 198, "p99": 512, "rating": "good" },
    "TTFB": { "p50": 280, "p75": 520, "p99": 1890, "rating": "good" }
  }
}
GET /api/v1/webvitals/pages

Web Vitals broken down by page path.

Response 200
{
  "data": [
    {
      "path": "/",
      "LCP_p75": 1890,
      "FCP_p75": 1120,
      "CLS_p75": 0.05,
      "INP_p75": 145,
      "TTFB_p75": 380,
      "samples": 1842
    },
    {
      "path": "/dashboard",
      "LCP_p75": 2890,
      "FCP_p75": 1680,
      "CLS_p75": 0.12,
      "INP_p75": 234,
      "TTFB_p75": 620,
      "samples": 567
    }
  ]
}
GET /api/v1/webvitals/timeseries

Daily p75 values for a specific Web Vital metric.

ParameterTypeDescription
metricstringRequired. One of: LCP, FCP, CLS, INP, TTFB.
Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/webvitals/timeseries?metric=LCP&start_at=2026-03-18&end_at=2026-03-22"
Response 200
{
  "metric": "LCP",
  "data": [
    { "date": "2026-03-18", "p75": 2120 },
    { "date": "2026-03-19", "p75": 2045 },
    { "date": "2026-03-20", "p75": 2310 },
    { "date": "2026-03-21", "p75": 1980 },
    { "date": "2026-03-22", "p75": 2180 }
  ]
}

Error Tracking

Requires the error_tracking module. Capture and group JavaScript errors from your frontend to identify and fix issues quickly.

GET /api/v1/errors

Get grouped error messages ranked by occurrence count.

Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/errors?start_at=2026-03-01&end_at=2026-03-22&limit=5"
Response 200
{
  "data": [
    {
      "message": "TypeError: Cannot read properties of null (reading 'id')",
      "count": 142,
      "unique_sessions": 98,
      "last_seen": "2026-03-22T11:42:00Z",
      "first_seen": "2026-03-05T14:23:00Z"
    },
    {
      "message": "ReferenceError: gtag is not defined",
      "count": 67,
      "unique_sessions": 54,
      "last_seen": "2026-03-21T09:15:00Z",
      "first_seen": "2026-03-01T08:02:00Z"
    }
  ]
}
GET /api/v1/errors/detail

Get individual error instances for a specific error message.

ParameterTypeDescription
messagestringRequired. The error message to look up.
limitintegerNumber of instances to return. Default: 20.
Response 200
{
  "message": "TypeError: Cannot read properties of null (reading 'id')",
  "instances": [
    {
      "timestamp": "2026-03-22T11:42:00Z",
      "path": "/dashboard",
      "filename": "https://acme.com/assets/app.js",
      "lineno": 142,
      "colno": 18,
      "stack": "TypeError: Cannot read properties of null\n    at handleClick (app.js:142:18)",
      "browser": "Chrome 122",
      "os": "Windows 11"
    }
  ]
}
GET /api/v1/errors/timeseries

Daily error count over time.

Response 200
{
  "data": [
    { "date": "2026-03-20", "count": 34 },
    { "date": "2026-03-21", "count": 28 },
    { "date": "2026-03-22", "count": 19 }
  ]
}
GET /api/v1/errors/stats

Aggregate error statistics for the period.

Response 200
{
  "total_errors": 342,
  "unique_errors": 18,
  "affected_sessions": 215,
  "error_rate": 4.7
}

Heatmaps

Requires the heatmaps module. Visualize where users click on your pages.

GET /api/v1/heatmaps

Get click coordinate data for a specific page.

ParameterTypeDescription
pathstringRequired. Page path to get heatmap data for.
start_atstringPeriod start (ISO 8601).
end_atstringPeriod end (ISO 8601).
Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/heatmaps?path=/pricing"
Response 200
{
  "path": "/pricing",
  "clicks": [
    { "x": 482, "y": 320, "selector": "button.cta-primary", "count": 234 },
    { "x": 720, "y": 580, "selector": "a.plan-enterprise", "count": 156 },
    { "x": 240, "y": 580, "selector": "a.plan-starter", "count": 98 },
    { "x": 480, "y": 580, "selector": "a.plan-pro", "count": 189 }
  ],
  "total_clicks": 892,
  "viewport_width": 1440
}
GET /api/v1/heatmaps/stats

Pages ranked by total click count.

Response 200
{
  "data": [
    { "path": "/", "total_clicks": 3421 },
    { "path": "/pricing", "total_clicks": 892 },
    { "path": "/docs", "total_clicks": 567 }
  ]
}

A/B Testing

Requires the ab_testing module. Create and manage experiments with multiple variants, assign visitors, and measure results against goals.

GET /api/v1/experiments

List all experiments.

Response 200
{
  "data": [
    {
      "id": "exp-a1b2c3d4-5678-9012-efab-cd3456789012",
      "name": "CTA Button Color",
      "status": "running",
      "variants": 3,
      "created_at": "2026-03-10T09:00:00Z"
    }
  ]
}
POST /api/v1/experiments

Create a new experiment. Variant weights should sum to 100.

Request
curl -X POST \
  -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CTA Button Color",
    "description": "Test whether blue, green, or orange CTA converts best",
    "variants": [
      {"name": "control_blue", "weight": 34},
      {"name": "variant_green", "weight": 33},
      {"name": "variant_orange", "weight": 33}
    ],
    "goal_id": "g9a8b7c6-5432-1fed-cba0-987654321012"
  }' \
  https://pulse.ayushojha.com/api/v1/experiments
Response 201
{
  "id": "exp-a1b2c3d4-5678-9012-efab-cd3456789012",
  "name": "CTA Button Color",
  "status": "draft",
  "variants": [
    { "name": "control_blue", "weight": 34 },
    { "name": "variant_green", "weight": 33 },
    { "name": "variant_orange", "weight": 33 }
  ],
  "goal_id": "g9a8b7c6-5432-1fed-cba0-987654321012",
  "created_at": "2026-03-22T14:30:00Z"
}
GET /api/v1/experiments/{id}

Get a specific experiment and its configuration.

PUT /api/v1/experiments/{id}/status

Update experiment status. Valid transitions: draftrunningpausedrunningcompleted.

Request
curl -X PUT \
  -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -H "Content-Type: application/json" \
  -d '{"status": "running"}' \
  https://pulse.ayushojha.com/api/v1/experiments/exp-a1b2c3d4-5678-9012-efab-cd3456789012/status
DELETE /api/v1/experiments/{id}

Delete an experiment. Only allowed for draft or completed experiments.

GET /api/v1/experiments/{id}/results

Get experiment results with per-variant conversion data.

Response 200
{
  "experiment_id": "exp-a1b2c3d4-5678-9012-efab-cd3456789012",
  "name": "CTA Button Color",
  "status": "running",
  "results": [
    {
      "variant": "control_blue",
      "visitors": 1102,
      "conversions": 34,
      "conversion_rate": 3.09,
      "revenue": 1632.66
    },
    {
      "variant": "variant_green",
      "visitors": 1067,
      "conversions": 41,
      "conversion_rate": 3.84,
      "revenue": 1968.59
    },
    {
      "variant": "variant_orange",
      "visitors": 1072,
      "conversions": 29,
      "conversion_rate": 2.71,
      "revenue": 1392.71
    }
  ]
}
POST /api/v1/experiments/{id}/assign

Assign a visitor to a variant. Uses weighted random assignment. Returns the same variant for repeat calls with the same visitor.

Request
curl -X POST \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  https://pulse.ayushojha.com/api/v1/experiments/exp-a1b2c3d4-5678-9012-efab-cd3456789012/assign
Response 200
{
  "experiment_id": "exp-a1b2c3d4-5678-9012-efab-cd3456789012",
  "variant": "variant_green"
}

Surveys

Requires the surveys module. Build in-app surveys with customizable triggers, appearance, and question types. Collect and analyze responses.

GET /api/v1/surveys

List all surveys for the project.

Response 200
{
  "data": [
    {
      "id": "srv-b2c3d4e5-6789-0abc-def1-234567890abc",
      "name": "NPS Survey",
      "status": "active",
      "responses": 142,
      "created_at": "2026-03-01T10:00:00Z"
    }
  ]
}
POST /api/v1/surveys

Create a new survey with questions, trigger configuration, and appearance settings.

Request
curl -X POST \
  -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "NPS Survey",
    "questions": [
      {
        "id": "q1",
        "type": "rating",
        "text": "How likely are you to recommend us?",
        "min": 0,
        "max": 10
      },
      {
        "id": "q2",
        "type": "text",
        "text": "What could we improve?",
        "required": false
      }
    ],
    "trigger_config": {
      "type": "time_on_page",
      "delay_seconds": 30,
      "pages": ["/dashboard", "/app/*"],
      "show_once": true
    },
    "appearance": {
      "position": "bottom-right",
      "theme": "dark",
      "primary_color": "#6366f1"
    },
    "response_limit": 500
  }' \
  https://pulse.ayushojha.com/api/v1/surveys
GET /api/v1/surveys/{id}

Get a specific survey with its full configuration.

PUT /api/v1/surveys/{id}

Update a survey’s configuration. Only allowed for draft or paused surveys.

DELETE /api/v1/surveys/{id}

Delete a survey and all its responses. Returns 204.

PUT /api/v1/surveys/{id}/status

Update survey status. Valid values: draft, active, paused, archived.

Request
curl -X PUT \
  -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -H "Content-Type: application/json" \
  -d '{"status": "active"}' \
  https://pulse.ayushojha.com/api/v1/surveys/srv-b2c3d4e5-6789-0abc-def1-234567890abc/status
GET /api/v1/surveys/{id}/responses

Get individual responses for a survey.

Response 200
{
  "data": [
    {
      "id": "resp-1234",
      "answers": [
        { "question_id": "q1", "value": 9 },
        { "question_id": "q2", "value": "Love the dashboard!" }
      ],
      "completed": true,
      "path": "/dashboard",
      "submitted_at": "2026-03-22T14:12:00Z"
    }
  ],
  "total": 142
}
GET /api/v1/surveys/{id}/stats

Aggregated response statistics.

Response 200
{
  "survey_id": "srv-b2c3d4e5-6789-0abc-def1-234567890abc",
  "total_responses": 142,
  "completed_responses": 128,
  "completion_rate": 90.1,
  "questions": [
    {
      "id": "q1",
      "text": "How likely are you to recommend us?",
      "avg_value": 8.3,
      "distribution": { "0-6": 12, "7-8": 38, "9-10": 92 }
    }
  ]
}
GET /api/v1/surveys/active

Get all active surveys for the frontend. Used by the tracking script to display surveys to visitors. Requires an ingest-scoped key.

Request
curl -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  "https://pulse.ayushojha.com/api/v1/surveys/active"
Response 200
{
  "data": [
    {
      "id": "srv-b2c3d4e5-6789-0abc-def1-234567890abc",
      "name": "NPS Survey",
      "questions": [ /* ... */ ],
      "trigger_config": { /* ... */ },
      "appearance": { /* ... */ }
    }
  ]
}

CSV Exports

Requires the exports module. Download analytics data as CSV files for use in spreadsheets or data pipelines.

GET /api/v1/exports/{type}

Export data as a CSV file. Returns a file download with Content-Disposition header.

ParameterTypeDescription
typestring (path)Required. One of: stats, pages, referrers, events, devices, geo, campaigns.
start_atstringPeriod start (ISO 8601).
end_atstringPeriod end (ISO 8601).
Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -o pages_export.csv \
  "https://pulse.ayushojha.com/api/v1/exports/pages?start_at=2026-03-01&end_at=2026-03-22"
Response Headers
Content-Type: text/csv
Content-Disposition: attachment; filename="pages_2026-03-01_2026-03-22.csv"
Example CSV Output
path,views,unique_views,avg_duration
/,4821,2190,45.2
/pricing,2314,1820,92.7
/docs/getting-started,1892,1456,214.3
/blog/analytics-guide,1203,987,312.1
/signup,891,834,67.8

Shared Dashboards

Requires the sharing module. Create password-protected, time-limited dashboard links that can be shared with stakeholders without giving them API access.

POST /api/v1/sharing

Create a new shared dashboard link.

Request
curl -X POST \
  -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Q1 Marketing Report",
    "modules": ["pageviews", "referrers", "utm", "geo"],
    "password": "stakeholder2026",
    "expires_at": "2026-04-30T23:59:59Z"
  }' \
  https://pulse.ayushojha.com/api/v1/sharing
Response 201
{
  "id": "shr-e4f5a6b7-8901-2345-cdef-678901234567",
  "name": "Q1 Marketing Report",
  "url": "https://pulse.ayushojha.com/shared/shr-e4f5a6b7",
  "modules": ["pageviews", "referrers", "utm", "geo"],
  "password_protected": true,
  "expires_at": "2026-04-30T23:59:59Z",
  "created_at": "2026-03-22T15:00:00Z"
}
GET /api/v1/sharing

List all shared dashboard links for the project.

DELETE /api/v1/sharing/{id}

Revoke a shared dashboard link. The URL will immediately stop working. Returns 204.

Alerts

Requires the alerts module. Set up threshold-based alert rules that notify you when metrics cross specified boundaries.

GET /api/v1/alerts

List all alert rules for the project.

Response 200
{
  "data": [
    {
      "id": "alt-c3d4e5f6-7890-1234-abcd-ef0123456789",
      "name": "Traffic Spike Alert",
      "module": "pageviews",
      "metric": "visitors",
      "operator": "gt",
      "threshold": 500,
      "window_minutes": 60,
      "enabled": true,
      "last_triggered": "2026-03-20T14:30:00Z"
    }
  ]
}
POST /api/v1/alerts

Create a new alert rule.

FieldTypeDescription
namestringHuman-readable name for the alert.
modulestringModule to monitor (e.g., pageviews, error_tracking).
metricstringMetric within the module (e.g., visitors, error_count).
operatorstringComparison: gt, lt, gte, lte, eq.
thresholdnumberValue to compare against.
window_minutesintegerRolling window size in minutes.
cooldown_minutesintegerMinimum time between repeated alerts.
notify_channelsarrayNotification targets (webhook URLs or email).
Request
curl -X POST \
  -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Error Spike Alert",
    "module": "error_tracking",
    "metric": "error_count",
    "operator": "gt",
    "threshold": 50,
    "window_minutes": 15,
    "cooldown_minutes": 60,
    "notify_channels": [
      {"type": "webhook", "url": "https://hooks.slack.com/services/T00/B00/xxx"},
      {"type": "email", "address": "ops@acme.com"}
    ]
  }' \
  https://pulse.ayushojha.com/api/v1/alerts
PUT /api/v1/alerts/{id}

Update an alert rule’s configuration.

DELETE /api/v1/alerts/{id}

Delete an alert rule. Returns 204.

POST /api/v1/alerts/{id}/toggle

Enable or disable an alert rule without deleting it.

Request
curl -X POST \
  -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  https://pulse.ayushojha.com/api/v1/alerts/alt-c3d4e5f6-7890-1234-abcd-ef0123456789/toggle
Response 200
{
  "id": "alt-c3d4e5f6-7890-1234-abcd-ef0123456789",
  "enabled": false
}

Admin API

Administrative endpoints for managing projects and API keys. All admin endpoints require the admin bearer token.

Admin-only: These endpoints require Authorization: Bearer <PULSE_ADMIN_TOKEN>. Do not expose the admin token in frontend code.
POST /api/admin/projects

Register a new project.

Request
curl -X POST \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My SaaS App",
    "domain": "app.example.com"
  }' \
  https://pulse.ayushojha.com/api/admin/projects
Response 201
{
  "id": "a7b8c9d0-1234-5678-9abc-def012345678",
  "name": "My SaaS App",
  "domain": "app.example.com",
  "created_at": "2026-03-22T16:00:00Z"
}
GET /api/admin/projects

List all registered projects.

Response 200
{
  "data": [
    {
      "id": "a7b8c9d0-1234-5678-9abc-def012345678",
      "name": "My SaaS App",
      "domain": "app.example.com",
      "created_at": "2026-03-22T16:00:00Z"
    }
  ]
}
GET /api/admin/projects/{id}

Get details for a specific project.

POST /api/admin/projects/{id}/keys

Create an API key for a project.

Request — Ingest Key
curl -X POST \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production Ingest",
    "scopes": ["ingest"],
    "expires_at": "2027-01-01T00:00:00Z",
    "allowed_modules": null
  }' \
  https://pulse.ayushojha.com/api/admin/projects/a7b8c9d0-1234-5678-9abc-def012345678/keys
Response 201
{
  "id": "key-f1e2d3c4-b5a6-9870-fedc-ba0987654321",
  "key": "pa_live_xK9mP2vQr7nL4wJ8bY6tZ3sA",
  "name": "Production Ingest",
  "scopes": ["ingest"],
  "allowed_modules": null,
  "expires_at": "2027-01-01T00:00:00Z",
  "created_at": "2026-03-22T16:05:00Z"
}
Important: The full key value is only returned once at creation time. Store it securely — it cannot be retrieved later.
GET /api/admin/projects/{id}/keys

List all API keys for a project. Key values are masked.

Response 200
{
  "data": [
    {
      "id": "key-f1e2d3c4-b5a6-9870-fedc-ba0987654321",
      "name": "Production Ingest",
      "key_prefix": "pa_live_xK9m...",
      "scopes": ["ingest"],
      "allowed_modules": null,
      "expires_at": "2027-01-01T00:00:00Z",
      "created_at": "2026-03-22T16:05:00Z",
      "last_used_at": "2026-03-22T16:42:00Z"
    }
  ]
}
DELETE /api/admin/projects/{id}/keys/{key_id}

Revoke an API key. The key immediately stops working. Returns 204.

Webhooks

Configure webhook integrations to receive real-time notifications when specific events occur in your analytics data.

POST /api/admin/projects/{id}/webhooks

Create a new webhook subscription.

FieldTypeDescription
urlstringRequired. HTTPS endpoint to receive webhook payloads.
eventsarrayRequired. Event types to subscribe to.
secretstringShared secret for HMAC-SHA256 signature verification.

Supported event types:

EventDescription
traffic_spikeTriggered when active visitors exceed 2x the rolling average.
zero_trafficTriggered when no pageviews are recorded for 30+ minutes.
daily_summarySent daily at midnight UTC with the day’s aggregate stats.
Request
curl -X POST \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://hooks.slack.com/services/T00/B00/xxxxx",
    "events": ["traffic_spike", "zero_traffic"],
    "secret": "whsec_my_signing_secret_2026"
  }' \
  https://pulse.ayushojha.com/api/admin/projects/a7b8c9d0-1234-5678-9abc-def012345678/webhooks
Response 201
{
  "id": "wh-d4e5f6a7-8901-2345-bcde-f01234567890",
  "url": "https://hooks.slack.com/services/T00/B00/xxxxx",
  "events": ["traffic_spike", "zero_traffic"],
  "created_at": "2026-03-22T16:30:00Z"
}
GET /api/admin/projects/{id}/webhooks

List all webhooks for a project.

PUT /api/admin/projects/{id}/webhooks/{webhook_id}

Update a webhook’s URL, events, or secret.

Request
curl -X PUT \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "events": ["traffic_spike", "zero_traffic", "daily_summary"]
  }' \
  https://pulse.ayushojha.com/api/admin/projects/a7b8c9d0-1234-5678-9abc-def012345678/webhooks/wh-d4e5f6a7-8901-2345-bcde-f01234567890
DELETE /api/admin/projects/{id}/webhooks/{webhook_id}

Remove a webhook subscription. Returns 204.

POST /api/admin/projects/{id}/webhooks/{webhook_id}/test

Fire a test payload to the webhook URL to verify connectivity.

Request
curl -X POST \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  https://pulse.ayushojha.com/api/admin/projects/a7b8c9d0-1234-5678-9abc-def012345678/webhooks/wh-d4e5f6a7-8901-2345-bcde-f01234567890/test
Response 200
{
  "success": true,
  "status_code": 200,
  "response_time_ms": 142
}

Webhook Payload Format

All webhook payloads follow the same envelope structure:

Webhook Payload Example (traffic_spike)
{
  "event": "traffic_spike",
  "project_id": "a7b8c9d0-1234-5678-9abc-def012345678",
  "timestamp": "2026-03-22T14:30:00Z",
  "data": {
    "active_visitors": 187,
    "rolling_avg": 45,
    "spike_multiplier": 4.16
  }
}
Signature verification: Pulse signs webhook payloads with HMAC-SHA256 using your secret. The signature is sent in the X-Pulse-Signature header. Verify it by computing HMAC-SHA256(secret, raw_body) and comparing.

Rate Limiting

Pulse enforces rate limits per project to ensure fair usage and system stability.

ScopeLimitWindow
Ingest endpoints100 requests/secondPer project
Query endpoints100 requests/secondPer project
Admin endpoints20 requests/secondPer admin token

When you exceed the rate limit, the API returns a 429 Too Many Requests response with a Retry-After header indicating how many seconds to wait.

Rate Limited Response (429)
HTTP/1.1 429 Too Many Requests
Retry-After: 1
Content-Type: application/json

{
  "error": "Rate limit exceeded. Retry after 1 second."
}
Tip: The tracking script automatically batches and throttles requests on the client side, so you typically won’t hit rate limits with normal browser traffic. Rate limits are more relevant for server-side API usage.

Error Codes

All API errors follow a consistent JSON format. The HTTP status code indicates the error category.

Error Response Format
{
  "error": "Human-readable error message describing what went wrong."
}

Status Codes

CodeMeaningCommon Causes
200 OK Request succeeded. Response body contains the requested data.
201 Created Resource created successfully (projects, keys, funnels, etc.).
204 No Content Delete operations completed successfully. No response body.
400 Bad Request Invalid JSON, missing required fields, invalid parameter values.
401 Unauthorized Missing or invalid API key / admin token.
403 Forbidden API key lacks the required scope, or the target module is disabled.
404 Not Found The requested resource (project, funnel, goal, etc.) does not exist.
429 Too Many Requests Rate limit exceeded. Check the Retry-After header.
500 Internal Server Error Unexpected server error. Contact support if it persists.

Common Error Examples

Missing API Key
Response 401
{
  "error": "Missing API key. Provide it via X-Pulse-Key header or ?key= query parameter."
}
Invalid API Key
Response 401
{
  "error": "Invalid API key."
}
Insufficient Scope
Response 403
{
  "error": "API key does not have the required 'query' scope for this endpoint."
}
Module Disabled
Response 403
{
  "error": "Module 'funnels' is not enabled for this project."
}
Invalid Request Body
Response 400
{
  "error": "Invalid request body: missing required field 'name'."
}
Resource Not Found
Response 404
{
  "error": "Funnel 'f1a2b3c4-5678-9abc-def0-1234567890ab' not found."
}