> ## Documentation Index
> Fetch the complete documentation index at: https://docs.holydocs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Analytics API

REST API endpoints for retrieving documentation analytics including page views, search queries, and reader engagement.

## Overview

The Analytics API provides read-only access to your documentation's performance data. Track page views, search behavior, AI assistant usage, and reader feedback programmatically. Use this data to build custom dashboards, pipe metrics into your data warehouse, or trigger alerts when content underperforms.

**Base path:** `https://api.holydocs.com/api/v1/analytics`

All analytics endpoints require authentication with the `analytics:read` scope.

Analytics data is aggregated in 1-hour buckets. Real-time data may have up to a 5-minute delay before appearing in API responses.

## Overview Endpoint

Retrieve high-level metrics for a project over a given time period.

```bash
GET /api/v1/analytics/overview?projectId=PROJECT_ID&period=30d
```

### Query Parameters

| Parameter | Type | Required | Description |
|---|---|---|---|
| `projectId` | string | Yes | Project ID to query analytics for |
| `period` | `7d` \| `30d` \| `90d` | No | Time period (default: `30d`) |

### Response

```json
{
  "data": {
    "period": "30d",
    "startDate": "2026-03-12T00:00:00Z",
    "endDate": "2026-04-11T00:00:00Z",
    "totalViews": 48230,
    "uniqueVisitors": 12847,
    "avgTimeOnPage": 142,
    "bounceRate": 0.34,
    "topPages": [
      {
        "path": "/quickstart",
        "title": "Quickstart Guide",
        "views": 8420,
        "uniqueVisitors": 3210,
        "avgTimeOnPage": 185
      },
      {
        "path": "/api/authentication",
        "title": "API Authentication",
        "views": 5130,
        "uniqueVisitors": 2890,
        "avgTimeOnPage": 210
      },
      {
        "path": "/components",
        "title": "Components",
        "views": 4015,
        "uniqueVisitors": 1980,
        "avgTimeOnPage": 165
      }
    ],
    "viewsByDay": [
      { "date": "2026-04-10", "views": 1620, "uniqueVisitors": 430 },
      { "date": "2026-04-11", "views": 1580, "uniqueVisitors": 415 }
    ]
  }
}
```

### Response Fields

**Field reference**

| Field | Type | Description |
|---|---|---|
| `totalViews` | number | Total page views across all pages |
| `uniqueVisitors` | number | Unique visitors (based on visitor fingerprint, not cookies) |
| `avgTimeOnPage` | number | Average time on page in seconds |
| `bounceRate` | number | Fraction of visitors who viewed only one page (0.0 - 1.0) |
| `topPages` | array | Top 10 pages by view count |
| `viewsByDay` | array | Daily breakdown of views and unique visitors |

Copy as context

**cURL**

```bash
    curl "https://api.holydocs.com/api/v1/analytics/overview?projectId=$PROJECT_ID&period=30d" \
      -H "Authorization: Bearer $HOLYDOCS_API_KEY"
    ```
  **CLI**

```bash
    holydocs api get "/analytics/overview?projectId=$PROJECT_ID&period=30d"
    ```
  **SDK**

```ts
    import { HolyDocs } from '@holydocs/sdk';
    const client = new HolyDocs({ apiKey: process.env.HOLYDOCS_API_KEY });
    const { data } = await client.analytics.overview(projectId, { period: '30d' });
    console.log(`${data.totalViews} views from ${data.uniqueVisitors} visitors`);
    ```

## Page Analytics

Retrieve per-page analytics with granular engagement data.

```bash
GET /api/v1/analytics/pages?projectId=PROJECT_ID
```

### Query Parameters

| Parameter | Type | Required | Description |
|---|---|---|---|
| `projectId` | string | Yes | Project ID |
| `period` | `7d` \| `30d` \| `90d` | No | Time period (default: `30d`) |
| `page` | number | No | Page number (default: 1) |
| `perPage` | number | No | Results per page (default: 50, max: 200) |
| `sortBy` | `views` \| `timeOnPage` \| `feedback` | No | Sort field (default: `views`) |
| `order` | `asc` \| `desc` | No | Sort order (default: `desc`) |

### Response

```json
{
  "data": [
    {
      "path": "/quickstart",
      "title": "Quickstart Guide",
      "views": 8420,
      "uniqueVisitors": 3210,
      "avgTimeOnPage": 185,
      "scrollDepth": 0.72,
      "feedbackScore": 4.6,
      "feedbackCount": 34,
      "entrances": 2100,
      "exits": 1850
    },
    {
      "path": "/api/authentication",
      "title": "API Authentication",
      "views": 5130,
      "uniqueVisitors": 2890,
      "avgTimeOnPage": 210,
      "scrollDepth": 0.85,
      "feedbackScore": 4.8,
      "feedbackCount": 22,
      "entrances": 980,
      "exits": 1200
    }
  ],
  "meta": {
    "total": 42,
    "page": 1,
    "perPage": 50,
    "totalPages": 1
  }
}
```

**Field reference**

| Field | Type | Description |
|---|---|---|
| `path` | string | Page path relative to docs root |
| `title` | string | Page title from frontmatter |
| `views` | number | Total page views |
| `uniqueVisitors` | number | Unique visitors to this page |
| `avgTimeOnPage` | number | Average time spent on page in seconds |
| `scrollDepth` | number | Average scroll depth (0.0 - 1.0) |
| `feedbackScore` | number \| null | Average feedback rating (1-5), null if no feedback |
| `feedbackCount` | number | Number of feedback submissions |
| `entrances` | number | Times this was the first page viewed in a session |
| `exits` | number | Times this was the last page viewed in a session |

Use the `entrances` and `exits` fields to identify pages where readers drop off. A high exit-to-entrance ratio on a non-terminal page may indicate content gaps.

## Search Analytics

Understand what your readers are searching for and whether they find what they need.

```bash
GET /api/v1/analytics/search?projectId=PROJECT_ID
```

### Query Parameters

| Parameter | Type | Required | Description |
|---|---|---|---|
| `projectId` | string | Yes | Project ID |
| `period` | `7d` \| `30d` \| `90d` | No | Time period (default: `30d`) |
| `page` | number | No | Page number (default: 1) |
| `perPage` | number | No | Results per page (default: 50, max: 200) |
| `filter` | `all` \| `zero_results` | No | Filter by result type (default: `all`) |

### Response

```json
{
  "data": {
    "totalSearches": 3420,
    "uniqueQueries": 1280,
    "avgClickThrough": 0.68,
    "zeroResultRate": 0.09,
    "queries": [
      {
        "query": "authentication",
        "count": 185,
        "clickThrough": 0.82,
        "topResult": "/api/authentication",
        "avgPosition": 1.2
      },
      {
        "query": "rate limiting",
        "count": 142,
        "clickThrough": 0.75,
        "topResult": "/api/authentication",
        "avgPosition": 1.5
      },
      {
        "query": "deploy github actions",
        "count": 98,
        "clickThrough": 0.45,
        "topResult": "/deployments",
        "avgPosition": 2.8
      },
      {
        "query": "graphql support",
        "count": 67,
        "clickThrough": 0.0,
        "topResult": null,
        "avgPosition": null
      }
    ]
  },
  "meta": {
    "total": 1280,
    "page": 1,
    "perPage": 50,
    "totalPages": 26
  }
}
```

**Field reference**

| Field | Type | Description |
|---|---|---|
| `totalSearches` | number | Total search queries performed |
| `uniqueQueries` | number | Distinct search query strings |
| `avgClickThrough` | number | Average click-through rate across all queries (0.0 - 1.0) |
| `zeroResultRate` | number | Fraction of searches returning no results (0.0 - 1.0) |
| `queries[].query` | string | The search query text |
| `queries[].count` | number | Times this query was searched |
| `queries[].clickThrough` | number | Click-through rate for this query |
| `queries[].topResult` | string \| null | Most-clicked page path, null for zero-result queries |
| `queries[].avgPosition` | number \| null | Average position of the clicked result |

Zero-result queries are content opportunities. Use the `filter=zero_results` parameter to find terms your readers search for that your docs don't cover, then prioritize creating that content.

## AI Assistant Analytics

Track AI assistant usage, popular questions, and tool utilization.

```bash
GET /api/v1/analytics/ai?projectId=PROJECT_ID
```

### Query Parameters

| Parameter | Type | Required | Description |
|---|---|---|---|
| `projectId` | string | Yes | Project ID |
| `period` | `7d` \| `30d` \| `90d` | No | Time period (default: `30d`) |

### Response

```json
{
  "data": {
    "totalConversations": 1240,
    "totalMessages": 5680,
    "avgMessagesPerConversation": 4.6,
    "avgResponseTime": 2.3,
    "satisfactionRate": 0.87,
    "toolUsage": {
      "search_docs": 4200,
      "get_page": 2850,
      "list_pages": 920
    },
    "topQuestions": [
      {
        "question": "How do I set up custom domains?",
        "count": 89,
        "avgSatisfaction": 0.92
      },
      {
        "question": "What authentication methods are supported?",
        "count": 76,
        "avgSatisfaction": 0.88
      },
      {
        "question": "How do I deploy from GitHub Actions?",
        "count": 62,
        "avgSatisfaction": 0.85
      },
      {
        "question": "Can I use MDX components in my docs?",
        "count": 54,
        "avgSatisfaction": 0.91
      }
    ],
    "unansweredTopics": [
      "GraphQL schema integration",
      "Monorepo multi-project setup",
      "Custom theme CSS variables"
    ]
  }
}
```

**Field reference**

| Field | Type | Description |
|---|---|---|
| `totalConversations` | number | Total AI chat conversations started |
| `totalMessages` | number | Total messages sent by visitors |
| `avgMessagesPerConversation` | number | Average messages per conversation |
| `avgResponseTime` | number | Average response time in seconds |
| `satisfactionRate` | number | Fraction of conversations rated positively (0.0 - 1.0) |
| `toolUsage` | object | Count of each tool invocation by the assistant |
| `topQuestions` | array | Most frequently asked questions |
| `unansweredTopics` | array | Topics where the assistant could not find relevant content |

The `unansweredTopics` array surfaces documentation gaps that are only visible through AI interactions. Use it alongside zero-result search queries to prioritize new content.

## Feedback Analytics

Retrieve reader feedback entries with page context, ratings, and comments.

```bash
GET /api/v1/analytics/feedback?projectId=PROJECT_ID
```

### Query Parameters

| Parameter | Type | Required | Description |
|---|---|---|---|
| `projectId` | string | Yes | Project ID |
| `period` | `7d` \| `30d` \| `90d` | No | Time period (default: `30d`) |
| `page` | number | No | Page number (default: 1) |
| `perPage` | number | No | Results per page (default: 50, max: 200) |
| `rating` | `1` \| `2` \| `3` \| `4` \| `5` | No | Filter by rating |
| `hasComment` | boolean | No | Filter to entries with comments only |

### Response

```json
{
  "data": {
    "summary": {
      "totalEntries": 156,
      "avgRating": 4.3,
      "distribution": {
        "1": 4,
        "2": 8,
        "3": 18,
        "4": 52,
        "5": 74
      }
    },
    "entries": [
      {
        "id": "fb_abc123",
        "pagePath": "/quickstart",
        "pageTitle": "Quickstart Guide",
        "rating": 5,
        "comment": "Clear and easy to follow. Had my docs deployed in under 5 minutes.",
        "visitorId": "v_anon_xyz",
        "createdAt": "2026-04-10T14:23:00Z"
      },
      {
        "id": "fb_def456",
        "pagePath": "/api/authentication",
        "pageTitle": "API Authentication",
        "rating": 3,
        "comment": "Missing examples for Python. Only shows cURL.",
        "visitorId": "v_anon_abc",
        "createdAt": "2026-04-09T09:15:00Z"
      },
      {
        "id": "fb_ghi789",
        "pagePath": "/openapi",
        "pageTitle": "OpenAPI Integration",
        "rating": 2,
        "comment": null,
        "visitorId": "v_anon_def",
        "createdAt": "2026-04-08T18:42:00Z"
      }
    ]
  },
  "meta": {
    "total": 156,
    "page": 1,
    "perPage": 50,
    "totalPages": 4
  }
}
```

**Field reference**

| Field | Type | Description |
|---|---|---|
| `summary.totalEntries` | number | Total feedback entries in the period |
| `summary.avgRating` | number | Average rating across all entries |
| `summary.distribution` | object | Count of entries per rating value (1-5) |
| `entries[].id` | string | Unique feedback entry ID |
| `entries[].pagePath` | string | Path of the page that received feedback |
| `entries[].pageTitle` | string | Title of the page |
| `entries[].rating` | number | Rating value (1-5) |
| `entries[].comment` | string \| null | Optional comment text |
| `entries[].visitorId` | string | Anonymous visitor identifier |
| `entries[].createdAt` | string | ISO 8601 timestamp |

## Common Patterns

### Building a Content Quality Dashboard

Combine multiple analytics endpoints to identify underperforming pages:

**Identify low-engagement pages**

Query `/analytics/pages` sorted by `timeOnPage` ascending to find pages where readers leave quickly.
  **Cross-reference with feedback**

Query `/analytics/feedback` filtered by `rating=1` or `rating=2` to find pages with poor reader satisfaction.
  **Check search gaps**

Query `/analytics/search?filter=zero_results` to find topics readers want but cannot find.
  **Review AI gaps**

Query `/analytics/ai` and inspect `unansweredTopics` for content the assistant cannot answer about.

### Exporting Analytics to a Data Warehouse

```javascript
// Nightly export of page analytics to your data warehouse
async function exportAnalytics(projectId, apiKey) {
  let page = 1;
  let hasMore = true;

  while (hasMore) {
    const response = await fetch(
      `https://api.holydocs.com/api/v1/analytics/pages?projectId=${projectId}&period=7d&page=${page}&perPage=200`,
      { headers: { Authorization: `Bearer ${apiKey}` } }
    );
    const { data, meta } = await response.json();

    await insertIntoWarehouse('holydocs_page_analytics', data);

    hasMore = page < meta.totalPages;
    page++;
  }
}
```

## Rate Limits

Analytics endpoints share the standard rate limits for your plan:

| Plan | Requests per Minute |
|---|---|
| Free | 60 |
| Starter | 120 |
| Pro | 600 |
| Business | 3,000 |
| Enterprise | 6,000 |

Analytics queries over large time periods (90d) are computationally expensive. If you need frequent access to historical data, cache responses on your side or use shorter periods with pagination.

## Error Codes

| Code | Status | Description |
|---|---|---|
| `NOT_FOUND` | 404 | Project not found |
| `VALIDATION_ERROR` | 400 | Invalid query parameters (bad period, invalid projectId) |
| `AUTH_ERROR` | 401 | Missing or invalid authentication |
| `FORBIDDEN` | 403 | API key lacks `analytics:read` scope |
| `LIMIT_EXCEEDED` | 429 | Rate limit exceeded |