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

# Survey analytics

> How people move through a survey: views, starts, submissions, drop-off per question, timing, devices and channels — in the dashboard, over the API, for AI agents and in your own GA4, Google Tag Manager or Meta Pixel.

The **Analytics** tab of a survey shows how people move through it, not only what they answered: how many saw it, started and finished, which question they left on, how long each question took, and how this compares with the previous period. It works for every channel — in-app, survey page, links, embeds and email.

## What's tracked

Surveys 2.0 report a small set of events while someone takes a survey:

| Event | When |
| - | - |
| `survey_shown` | The survey became visible (one per response). |
| `survey_resumed` | A started response was continued (for example from a reminder). |
| `welcome_viewed` | The [welcome screen](/documentation/surveys/definition#welcome-screen) became visible. |
| `welcome_started` | Start was pressed on the welcome screen. |
| `step_viewed` | A question became visible. |
| `step_answered` | A question was answered, with the time spent on it. |
| `step_skipped` | An optional question was skipped. |
| `step_back` | The person went back to the previous question. |
| `survey_closed` | The survey was closed, with the question it was closed on. |
| `survey_completed` | An ending was reached, with the total duration. |
| `reminder_sent` | Gleap sent a reminder to finish a started response. |

Each event carries the survey version, channel (`web`, `ios`, `android`, `page`, `email`, `embed`), device class (`phone`, `tablet`, `desktop`), operating system, SDK version, language, the page path (without query string) and, on the survey page, the referrer host and UTM parameters.

<Note>
  **No answers in analytics.** Events never contain answer values — only the score of `nps`, `csat`, `rating` and `scale` questions, so score trends can be split by device or channel. Viewers are counted with a hashed id (contact, session or an anonymous per-browser id), never an email or name. Events are kept for **13 months**; responses themselves follow your normal data retention.
</Note>

## In the dashboard

* **Header**: views, unique views, starts, submissions, start rate (starts ÷ views), completion rate (submissions ÷ starts) and average time to complete — each with the change against the previous period of the same length, and split by device. Surveys with a welcome screen also show **Welcome → Start**: of the people who saw the welcome screen, the share that pressed Start.
* **Funnel**: every question in order with viewed, answered and skipped counts, how many people closed the survey on it (drop-off), median and 75th-percentile time on the question and back navigations. The question with the highest drop-off is highlighted.
* **Dismissed**: people who closed the survey without answering anything, and how quickly.
* **Trend**: views, starts and submissions per day or week, plus the score over time.
* **Breakdowns**: channel, device, operating system and language, with completion rate and score per row; per-version results after you publish changes.
* **Sources**: top page paths, referrers and UTM campaigns.
* **Best time**: the hours and weekdays when people submit, in your time zone.
* **Reminders and email**: reminders sent, responses resumed and completed after a reminder; email campaigns with sent, opened, answered and completed.
* **Quality**: speeders (completed faster than 30% of the median completion time) and straight-liners (the same value on every scale question).

Filter everything by date range, version, channel and device.

## API

`GET /v3/engagement/surveys/{id}/analytics`

Uses the [admin REST API](/documentation/surveys/rest-api) authentication and needs permission to view surveys.

| Query | |
| - | - |
| `from`, `to` | Days as `YYYY-MM-DD` (UTC, inclusive). Default: the last 30 days. At most 400 days. |
| `version` | Only this published version. |
| `channel` | `web`, `ios`, `android`, `page`, `email`, `embed` |
| `device` | `phone`, `tablet`, `desktop` |
| `interval` | `day` (default) or `week` — bucket size of `series` (weeks start on Monday). |
| `tz` | IANA time zone for `heatmap` (default `UTC`). |

The previous period is the same number of days right before `from`. Results are cached for 60 seconds. When `available` is `false` there are no events for the survey in the range (for example a legacy survey) — use [stats](/documentation/surveys/rest-api#stats) instead.

Rates are fractions from 0 to 1, times are milliseconds. A trimmed example:

```json theme={null}
{
  "available": true,
  "range": { "from": "2026-09-01", "to": "2026-09-30", "previousFrom": "2026-08-02", "previousTo": "2026-08-31", "interval": "day", "tz": "UTC" },
  "header": {
    "views": { "value": 4210, "previous": 3980, "byDevice": { "desktop": 2900, "tablet": 110, "phone": 1200 } },
    "uniqueViews": { "value": 3650, "previous": 3420, "byDevice": { … } },
    "starts": { "value": 1630, "previous": 1490, "byDevice": { … } },
    "submissions": { "value": 1288, "previous": 1205, "byDevice": { … } },
    "completionRate": { "value": 0.79, "previous": 0.81, "byDevice": { … } },
    "startRate": { "value": 0.387, "previous": 0.374, "byDevice": { … } },
    "avgTimeToCompleteMs": { "value": 38000, "previous": 41000, "byDevice": { … } },
    "welcomeViews": { "value": 0, "previous": 0, "byDevice": { … } },
    "welcomeStartRate": { "value": null, "previous": null, "byDevice": { … } }
  },
  "funnel": [
    { "blockId": "q_nps", "key": "nps_score", "type": "nps", "index": 0, "title": "How likely are you to recommend us?",
      "viewed": 1630, "answered": 1601, "skipped": 0, "closedHere": 29, "dropOff": 0.018, "medianMs": 3100, "p75Ms": 5200, "backFrom": 0 },
    { "blockId": "q_detractor", "key": "detractor_reason", "type": "long", "index": 1, "title": "What disappointed you?",
      "viewed": 276, "answered": 201, "skipped": 0, "closedHere": 75, "dropOff": 0.272, "medianMs": 21000, "p75Ms": 44000, "backFrom": 6 }
  ],
  "worstStepBlockId": "q_detractor",
  "dismissed": { "count": 2580, "rate": 0.613, "medianMsToDismiss": 2400 },
  "timing": { "firstAnswerMedianMs": 3100, "firstAnswerP75Ms": 5200, "completeMedianMs": 34000, "completeP75Ms": 61000 },
  "backNavigation": 41,
  "closePoints": [{ "blockId": "q_detractor", "key": "detractor_reason", "title": "What disappointed you?", "count": 75 }],
  "series": [{ "bucket": "2026-09-01", "views": 140, "starts": 52, "submissions": 41 }],
  "breakdowns": {
    "channel": [{ "value": "web", "views": 2900, "starts": 1100, "submissions": 880, "completionRate": 0.8, "score": 44 }],
    "device": [ … ], "os": [ … ], "language": [ … ]
  },
  "heatmap": { "tz": "UTC", "cells": [{ "dow": 1, "hour": 14, "count": 37 }] },
  "versions": [{ "version": 3, "views": 4210, "starts": 1630, "submissions": 1288, "completionRate": 0.79, "score": 42, "n": 1288 }],
  "score": { "kind": "nps", "key": "nps_score", "blockId": "q_nps", "current": 42, "previous": 38, "n": 1601,
             "series": [{ "bucket": "2026-09-01", "score": 40, "n": 52 }] },
  "reminders": { "sent": 310, "resumed": 96, "completed": 71 },
  "email": { "campaigns": [{ "id": "…", "name": "Q3 NPS", "sent": 5000, "opened": 2100, "answered": 640, "completed": 512 }],
             "totals": { "sent": 5000, "opened": 2100, "answered": 640, "completed": 512 } },
  "sources": {
    "pagePaths": [{ "value": "/pricing", "views": 820, "submissions": 210 }],
    "referrers": [{ "value": "google.com", "views": 120, "submissions": 31 }],
    "utm": [{ "source": "newsletter", "medium": "email", "campaign": "q3", "views": 400, "submissions": 118 }]
  },
  "quality": { "speeders": 14, "speederThresholdMs": 10200, "straightLiners": 6 }
}
```

| Field | |
| - | - |
| `header.*` | `{ value, previous, byDevice }`. Views count shows (one per response), unique views count distinct viewers, starts count responses with at least one answer, submissions count completed responses. `welcomeViews` counts responses that saw the welcome screen and `welcomeStartRate` the share of those that pressed Start (`null` without a welcome screen). |
| `funnel` | Questions of the current definition in order. `closedHere` counts responses whose last viewed question was this one and that never completed; `dropOff` = `closedHere ÷ viewed`. Times count answered and skipped questions. |
| `worstStepBlockId` | The question with the highest drop-off among questions viewed at least 5 times (otherwise the highest overall). |
| `heatmap.cells` | Submissions per weekday (`dow`, 0 = Monday) and hour in `tz`. |
| `score` | The survey's score question (`nps`, `csat`, `rating` or `scale`): NPS from −100 to 100, otherwise the average. `score` in breakdowns and versions uses the same kind. `null` without a score question. |

## Response filters and export

The [responses list](/documentation/surveys/rest-api#responses) and the CSV/JSON export take these filters in addition to `status`:

| Query | |
| - | - |
| `field[<hidden field key>]` | Exact match on a [hidden field](/documentation/surveys/overview#hidden-fields), e.g. `field[plan]=pro`. Up to 10. |
| `device` | `phone`, `tablet`, `desktop` |
| `quality` | `speeder` (completed faster than 30% of the median completion time) or `straight_line` |

List items include `device` (`{ class, os }` or `null`), `startedAt`, `fields` (hidden field values), `source` (UTM parameters and referrer) and `quality` (`{ speeder: true }` and/or `{ straightLine: true }` when flagged).

Export columns: `startedAt`, `submittedAt`, `durationMs`, `device`, `os`, `language`, `pageUrl`, `utm_source`, `utm_medium`, `utm_campaign`, `referrer`, then one `field_<key>` column per hidden field — next to the answer columns (one per answer key).

## AI agents (MCP)

The [Gleap MCP server](/documentation/surveys/mcp) has a `get_survey_analytics` tool with the same filters (`from`, `to`, `version`, `channel`, `device`, `interval`, `tz`). It returns a compact view for agents: header metrics with the previous period and the change, the funnel (up to 30 questions) with drop-off in percent and median seconds, the worst question, dismissals, timing, the top 5 close points, the score against the previous period, versions, the top rows per breakdown and source, the three busiest day/hour slots, reminders, email totals and quality counts. `get_survey_results` also includes the funnel when events exist.

> Which question loses the most people in the churn survey, and is it worse on phones?

## Send survey events to your analytics

Gleap can also push survey events from the [JavaScript SDK](/documentation/javascript/surveys) into the analytics already on your site — Google Analytics 4, Google Tag Manager or Meta Pixel — so you can build audiences and conversions on them. It's **off by default**.

**Turn it on** for one survey with the analytics toggle under **Share → In the app**, or for every survey in the project with the project setting `surveyAnalyticsForwarding` (flow config).

| Event | When |
| - | - |
| `gleap_survey_shown` | The survey became visible. |
| `gleap_survey_step_viewed` | A question became visible. |
| `gleap_survey_answered` | A question was answered. |
| `gleap_survey_completed` | An ending was reached. |

Parameters (only those that apply):

| Parameter | |
| - | - |
| `survey_id` | The survey's id. |
| `survey_name` | The survey's name. |
| `step_index` | Position of the question, starting at 0. |
| `block_key` | The question's answer key, e.g. `nps_score`. |

Answer values are never sent. Closing a survey isn't forwarded.

Where the events go:

| Tool | Call |
| - | - |
| Google Tag Manager | `window.dataLayer.push({ event: "gleap_survey_completed", survey_id: "…", survey_name: "…", … })` — only when `window.dataLayer` exists. Use a **Custom Event** trigger with the event name. |
| Google Analytics 4 (gtag.js) | `gtag("event", "gleap_survey_completed", { survey_id: "…", … })` — when `gtag` is loaded. Register the parameters as custom dimensions to report on them. |
| Meta Pixel | `fbq("trackCustom", "gleap_survey_completed", { survey_id: "…", … })` — when `fbq` is loaded. |

Each destination is optional; Gleap never loads these scripts itself. Events are sent from the page the survey runs in, so they follow your site's own consent setup.

To react to survey events in your own code instead, listen to the SDK events:

```javascript theme={null}
Gleap.on("survey-step-viewed", (data) => {
  // { surveyId, version, responseId, stepIndex, key }
});
Gleap.on("survey-completed", (data) => {
  console.log("Survey completed", data.surveyId);
});
```

`survey-shown`, `survey-step-viewed`, `survey-answered`, `survey-completed` and `survey-closed` are available in the JavaScript SDK.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.