Working with Analyze

The Analyze API lets you read a site’s analytics through the Data API, including traffic, page engagement, and goal reporting. Use it to build dashboards, sync metrics into other tools, or answer questions about how visitors use a site.

Every report is a read-only GET request. All reports share one filter model and report over a time window you choose. List goals is the exception: it’s a catalog of the site’s goal configuration and takes no time window or filters.

Analyze add-on required
Analyze endpoints require an access token with the sites:read scope and a workspace that has the Analyze add-on.

Choose a report

Each report answers a different question. Pick the one that matches what you want to measure.

Core concepts

Metric scopes

Some reports measure activity in one of three units, set with metricScope:

  • session — a single visit that groups a visitor’s activity.
  • user — a unique visitor.
  • pageview — a single page load.

Support varies by report:

  • Traffic, Time on page, and Page flow take any of the three.
  • Top dimensions takes session or user.
  • Top pages returns all three counts per row and uses sortBy to rank them.
  • Top events, Scroll depth, Browser window height, and Goal performance don’t use a metricScope.
Top events counts how many times each event occurred. Goal performance always counts unique conversions.

Time windows

Pass startTime and endTime as UTC timestamps ending in Z, for example 2026-04-01T00:00:00Z. Numeric offsets such as -04:00 or +00:00 aren’t accepted.

A few rules apply to every report that takes a time window:

  • The window can span at most 100 days.
  • The earliest supported startTime is 2025-04-09T00:00:00Z.

Bucket time zones

Reports that return buckets — fixed time intervals, such as one data point per day — also include a bucketing field that echoes how the data was cut. How you request buckets varies by report:

  • Traffic is always bucketed, so it requires bucketTimeZone.
  • Top pages and Top events return buckets only when you request timeseries[bucketTimeZone].
  • Time on page returns buckets only when you request both timeseries[granularityPeriod] and timeseries[bucketTimeZone].
  • Goal performance is always bucketed by day. bucketTimeZone is optional and defaults to UTC.

bucketTimeZone must be a valid IANA time zone. Use canonical names such as UTC or America/New_York. Bucket timestamps are still returned as UTC instants. For example, America/New_York local midnight on April 1, 2026 is returned as 2026-04-01T04:00:00.000Z. Invalid time zones return 400 analyze_input_validation.

Filtering

Narrow any report to a subset of traffic in one of two ways:

  • Top-level query parameters — shortcuts for common dimensions, such as country, deviceType, trafficSource, and browser. For example, ?country=US&deviceType=desktop.
  • The filter parameter — the full supported set, with operators. Use bracket notation: scalars take one value (filter[country][eq]=US), and arrays use indexed brackets (filter[country][in][0]=US&filter[country][in][1]=CA). Comma-separated values aren’t supported.

Each dimension accepts eq, in, ne, or nin. Filter a given dimension in one place only — either a top-level parameter or a filter entry, not both. Using both returns 400 analyze_filter_conflict. Each report supports its own set of dimensions; see the filter parameter on the report’s reference page for the list.

Finding values to filter by
To filter by an enumerable dimension, call Top dimensions with that dimension and reuse each row’s attributeKey as the filter value. For example, dimension=browser returns Chrome, which you pass as filter[browser][eq]=Chrome or the browser=Chrome shortcut.

Rate and concurrency limits

Analyze applies two limits in addition to standard authentication:

  • Rate limit — the standard per-minute Data API rate limit, based on the site plan.
  • Concurrency limit — each access token can have one Analyze request in flight at a time, across all Analyze endpoints.

If either limit is exceeded, the request fails with 429 Too Many Requests and a Retry-After header. Serialize your Analyze calls — wait for one to finish before starting the next — and retry after the Retry-After interval.

Errors

Analyze validates each request and returns 400 Bad Request with a code that identifies the rule that failed:

CodeMeaning
time_range_too_wideThe window spans more than 100 days.
before_historical_floorstartTime is earlier than 2025-04-09T00:00:00Z.
invalid_time_rangeendTime is not greater than startTime.
analyze_input_validationA parameter value is invalid, or a parameter isn’t accepted by the report. See the message field for details.
analyze_filter_conflictA dimension was set both as a top-level parameter and inside filter.
analyze_unsupported_filterThe request used a dimension or operator the report doesn’t support.

A 403 Forbidden means the token is missing the sites:read scope, or the workspace doesn’t have the Analyze add-on.

A 404 Not Found means the site doesn’t exist, or the goal_id in a Goal performance request doesn’t match any of the site’s goals.

Walkthrough: build a traffic view

Imagine you’re building a weekly traffic digest for a client — they want to know how desktop visitors from the UK engaged with their site last week. This walkthrough pulls that report step by step, starting broad and narrowing down with filters.

1

Request the traffic report

Call the Traffic report with a window, a metricScope, a bucketTimeZone, and a deviceType shortcut.

curl --request GET \
--url 'https://api.webflow.com/beta/sites/{site_id}/analyze/reports/traffic?startTime=2026-04-01T00:00:00Z&endTime=2026-04-08T00:00:00Z&metricScope=session&bucketTimeZone=UTC&deviceType=desktop' \
--header 'authorization: Bearer YOUR_API_TOKEN' \
--header 'accept: application/json'
2

Read the response

Each data point is one bucket. Because this request used bucketTimeZone=UTC, the bucket timestamps are UTC midnight. The bucketing field echoes the bucket settings, and filter confirms how the report was scoped.

{
"report": "traffic",
"metricScope": "session",
"window": {
"startTime": "2026-04-01T00:00:00Z",
"endTime": "2026-04-08T00:00:00Z"
},
"bucketing": {
"granularityPeriod": "day",
"bucketTimeZone": "UTC"
},
"data": [
{ "timestamp": "2026-04-01T00:00:00Z", "count": 1234 },
{ "timestamp": "2026-04-02T00:00:00Z", "count": 1180 },
{ "timestamp": "2026-04-03T00:00:00Z", "count": 1310 },
{ "timestamp": "2026-04-04T00:00:00Z", "count": 1095 },
{ "timestamp": "2026-04-05T00:00:00Z", "count": 742 },
{ "timestamp": "2026-04-06T00:00:00Z", "count": 688 },
{ "timestamp": "2026-04-07T00:00:00Z", "count": 1021 }
],
"filter": { "deviceType": { "eq": "desktop" } }
}
3

Narrow it further

Add another dimension to focus the view. Here, country=GB limits the report to visitors from the United Kingdom — the filter value is the ISO code (GB), not the country name.

curl --request GET \
--url 'https://api.webflow.com/beta/sites/{site_id}/analyze/reports/traffic?startTime=2026-04-01T00:00:00Z&endTime=2026-04-08T00:00:00Z&metricScope=session&bucketTimeZone=UTC&deviceType=desktop&country=GB' \
--header 'authorization: Bearer YOUR_API_TOKEN' \
--header 'accept: application/json'

Walkthrough: track goal conversions

Imagine the same client now wants to know how many US visitors completed their Contact Sales goal each day. Goals are set up in Webflow, so you first list the site’s goals, then request daily conversions for one of them.

1

List the site's goals

List goals takes no time window or filters. Find the goal you want and copy its goalId.

curl --request GET \
--url 'https://api.webflow.com/beta/sites/{site_id}/analyze/goals' \
--header 'authorization: Bearer YOUR_API_TOKEN' \
--header 'accept: application/json'

The goalId is a numeric string, such as "198020177". It isn’t a Webflow object ID.

2

Request the goal's daily conversions

Pass the goalId as goal_id in the path, along with a window, a bucketTimeZone, and a country shortcut.

curl --request GET \
--url 'https://api.webflow.com/beta/sites/{site_id}/analyze/goals/198020177/performance?startTime=2026-04-01T00:00:00Z&endTime=2026-04-04T00:00:00Z&bucketTimeZone=America/New_York&country=US' \
--header 'authorization: Bearer YOUR_API_TOKEN' \
--header 'accept: application/json'
3

Read the response

The response includes the goal’s configuration, so you don’t need to call List goals again. Each data point is one day of unique conversions. Because this request used bucketTimeZone=America/New_York, each day starts at local midnight, returned as a UTC instant.

{
"goal": {
"goalId": "198020177",
"name": "Contact Sales",
"isSiteGoal": true,
"goalType": "integration",
"integrationSource": "hubspot",
"formId": "3b7e2a1c-5d4f-4e8a-9b6c-1a2d3e4f5a6b",
"scope": "session",
"type": "conversion",
"eventIds": ["157024554"],
"updatedAt": "2026-04-01T12:00:00.000Z"
},
"window": {
"startTime": "2026-04-01T00:00:00Z",
"endTime": "2026-04-04T00:00:00Z"
},
"bucketing": {
"granularityPeriod": "day",
"bucketTimeZone": "America/New_York"
},
"data": [
{ "timestamp": "2026-04-01T04:00:00.000Z", "count": 12 },
{ "timestamp": "2026-04-02T04:00:00.000Z", "count": 18 },
{ "timestamp": "2026-04-03T04:00:00.000Z", "count": 9 }
],
"filter": { "country": { "eq": "US" } }
}

From here, try another report to answer a different question:

  • Top pages — your most-visited content
  • Page flow — where visitors go before and after a page
  • Top dimensions — visitors by country, device, or traffic source

To browse every report, see Choose a report.