> This page is for Data API, version v2 (default).
> For other versions, use one of these documentation indexes:
> - v2 (default): https://developers.webflow.com/data/v2.0.0/llms.txt
> - v2 Beta: https://developers.webflow.com/data/v2.0.0-beta/llms.txt
> - v1: https://developers.webflow.com/data/v1.0.0/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developers.webflow.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developers.webflow.com/_mcp/server.

# Working with Optimize

> Use the Webflow Optimize API to read optimization results, including variation performance, audience insights, and performance over time.

The Optimize API lets you read the optimizations running on your site and how they are performing. Use it to build reporting dashboards, sync results into other tools, or check which variation is winning.

Every endpoint is a read-only `GET` request.

> **Optimize add-on required**
>
> Optimize endpoints require an access token with the `sites:read` scope and a Webflow site with the Optimize add-on.

> **Using Optimize on a site that isn't built in Webflow?**
>
> The same endpoints work for non-Webflow sites that use Optimize. These sites accept only OAuth app tokens, and the response identifies them as `optimize-site` instead of `webflow-site`.

## Choose an endpoint

Start with List Optimize IDs and List optimizations. They return the IDs that every other endpoint needs.

#### [List Optimize IDs](/data/reference/optimize/list-optimize-ids)

The Optimize ID for each site your token can access.

#### [List optimizations](/data/reference/optimize/list-optimizations)

Each optimization's type, status, and intervals.

#### [Get optimization results](/data/reference/optimize/get-optimization-results)

Each variation's sessions, users, conversions, and conversion rate for one goal.

#### [Get audience insights](/data/reference/optimize/get-audience-insights)

The same results, split by the segments of one dimension, such as country or device.

#### [Get performance over time](/data/reference/optimize/get-performance-over-time)

The same results, by day or week.

#### [List optimization goals](/data/reference/optimize/list-optimization-goals)

The goals for an interval and which are target goals, plus the optimization's current goals.

#### [List dimensions](/data/reference/optimize/list-dimensions)

The dimensions you can group or filter results by.

#### [List dimension values](/data/reference/optimize/list-dimension-values)

The values seen for a dimension, to use in filters.

## Core concepts

### Optimize IDs

Optimize identifies your data with an `optimizeId` instead of a site ID. Every other endpoint takes an `optimizeId` in its path.

Call [List Optimize IDs](/data/reference/optimize/list-optimize-ids) to see which `optimizeId` belongs to which site. Each entry includes a `resource` with the site's `siteId` and `displayName`.

If an `optimizeId` matches more than one resource your token can access, other endpoints return `409 Conflict`.

### Optimizations

An optimization has one of three types:

* `test`: A traditional A/B test. It shows different versions of your content to a share of your traffic and declares a winner once the result reaches statistical significance.
* `personalize`: A manual personalization. It shows a variation to visitors who match rules you define, and it runs until you stop it. It has no winner.
* `aiOptimize`: An AI-optimized test. It uses AI to deliver the best-performing variation to each visitor.

Each optimization has one or more variations. Reports for tests and AI-optimized optimizations compare each variation against the base variation.

[List optimizations](/data/reference/optimize/list-optimizations) returns live and draft optimizations by default. Pass `status` to include `ready`, `archived`, or `off` optimizations.

The optimization type changes what each result includes:

| Type          | Comparison fields in results                                                                                                                               |
| :------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `test`        | `isWinner`, `lift`, and [`statisticalSignificance`](https://help.webflow.com/hc/en-us/articles/34167147351315-Variation-statistical-significance-overview) |
| `aiOptimize`  | [`strength`](https://help.webflow.com/hc/en-us/articles/33615967793427-Variation-strength-overview)                                                        |
| `personalize` | None                                                                                                                                                       |

### Intervals

An interval is a snapshot of an optimization's configuration, such as which variations are live and which goals are target goals. When you change the configuration in a way that affects results, the current interval ends and a new one starts.

Each report covers one date range inside one interval. A range that crosses from one interval into the next is rejected. To choose a range, take the `start` and `end` of an interval from List optimizations. An interval with no `end` is still running.

### Goals

A goal defines the action that counts as a conversion. Each interval has its own goals. Target goals, marked `isPrimary: true`, are the goals Optimize uses to measure success. For AI-optimized optimizations, they're also what the AI optimizes for. An interval usually has one target goal, but it can have several, such as when the optimization targets all site goals, or none. Other goals are used only for reporting.

An optimization's goals come in two lists:

* An interval's `goals` are the goals saved when that interval started. They include its target goals and the goals created in the optimization by then.
* The optimization's `currentGoals` are its goals as they're set up now. The list is the same for every interval, and it includes goals that an interval's `goals` leave out. Each goal's `source` shows where it comes from:
  * `site`: A site goal. Site goals apply to every optimization on the site, so each optimization has them without anyone adding them.
  * `existing`: A goal created outside this optimization that was added to it or chosen as a target goal. It isn't a site goal, and other optimizations can use it too.
  * `optimization`: A goal created in this optimization, such as its AutoGoal. Only this optimization has it.

Reports measure one goal at a time. Pass `goalId` to choose it, using an ID from either list. If you omit `goalId`, the report uses the interval's first target goal, or its first goal if it has no target goal. If a goal is in both lists, the report uses the interval's version. A goal from `currentGoals` is measured with its current settings over the whole date range, including dates before it was added to the optimization.

[List optimizations](/data/reference/optimize/list-optimizations) and [List optimization goals](/data/reference/optimize/list-optimization-goals) both return the two lists.

A goal's `metricType` changes the response:

* `conversion`: Results show conversions and conversion rate.
* `value`: Results also include the total `value` and the average `valuePerConversion`.

A goal's `scope` is the unit it counts conversions in: `session`, `user`, or `pageview`.

A goal's `countingMethod` sets how repeat conversions count. With `unique`, a session or user counts once. With `cumulative`, every conversion counts. Some goals, such as Shopify purchase goals, default to `cumulative`.

Only compare goals directly when they use the same `scope` and `countingMethod`. Different settings change `conversions` and `conversionRate`.

### Reading results

Each variation in a report includes the following core metrics:

* `sessions`: A period of continuous activity by a visitor. A session ends after 30 minutes of inactivity.
* `users`: The number of unique visitors who saw the variation. A visitor who returns counts as one user.
* `conversions`: How many times visitors took the action the goal tracks.
* `conversionRate`: Conversions divided by sessions, returned as a fraction. For example, `0.1193` means 11.93%.

`lift` is also a fraction. A `lift` of `0.19` means the variation converts 19% better than the base variation.

To learn what each metric means, see [Review your optimization results](https://help.webflow.com/hc/en-us/articles/34167118964883-Review-your-optimization-results).

> **Data freshness**
>
> After you launch an optimization, initial processing takes about 30 minutes before data appears. After that, results update every minute.

### Date ranges

Pass `intervalStart` and `intervalEnd` as UTC timestamps ending in `Z`, for example `2026-09-01T00:00:00Z`. Offsets and date-only values aren't accepted.

Two rules apply to every report:

* `intervalEnd` must be later than `intervalStart`.
* `intervalStart` can't be earlier than 18 months ago.

### Dimensions and filters

A dimension is an attribute of a visitor, such as country, device, or traffic source. Use dimensions to split results with `groupBy` on [Get audience insights](/data/reference/optimize/get-audience-insights), or to narrow any report with `filter`.

Call [List dimensions](/data/reference/optimize/list-dimensions) for the dimensions available in your date range. `groupBy` accepts any dimension except `urlParameter`.

`filter` uses bracket notation with `in` to include values or `nin` to exclude them. Use indexed brackets for multiple values:

```text
filter[country][in][0]=US&filter[country][in][1]=GB&filter[deviceType][nin][0]=mobile
```

Separate dimensions combine with AND. Values within one dimension combine with OR. A filter can name at most 20 dimensions, with at most 50 values per operator and 500 values in total.

> **Finding values to filter by**
>
> Call [List dimension values](/data/reference/optimize/list-dimension-values) with a dimension's `id`. Use each row's `value` in your filter, not its `label`. For example, the `country` dimension returns `GB` as the `value` and `United Kingdom` as the `label`.

### Pagination

List optimizations and List dimension values return results in pages. Use `pageSize` to set the page size (default 100, maximum 200). When a response includes `nextCursor`, pass it as `cursor` to get the next page. A response without `nextCursor` is the last page.

A cursor stays tied to the request that created it. For List optimizations, repeat the same `status`. For List dimension values, repeat the same dimension, date range, and `filter`. You can change `pageSize` between pages.

## Rate and concurrency limits

Optimize applies two limits in addition to standard authentication:

* **Rate limit**: The standard per-minute Data API [rate limit](/data/reference/rate-limits), based on the site plan.
* **Concurrency limit**: Each access token can have one Optimize request in flight at a time, across all Optimize endpoints.

If either limit is exceeded, the request fails with `429 Too Many Requests` and a `Retry-After` header. Send Optimize calls one at a time, and retry after the `Retry-After` interval.

## Errors

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

| Code                               | Meaning                                                                                                                           |
| :--------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------- |
| `optimize_input_validation`        | A parameter value is invalid. The `details` array names the failing field.                                                        |
| `optimize_invalid_interval`        | `intervalEnd` isn't later than `intervalStart`, the optimization has never run, or the date range isn't within a single interval. |
| `optimize_before_historical_floor` | `intervalStart` is earlier than 18 months ago.                                                                                    |
| `optimize_unknown_goal`            | `goalId` doesn't match a goal in the interval's `goals` or the optimization's `currentGoals`.                                     |
| `optimize_unknown_dimension`       | `groupBy`, a `filter` key, or the dimension in the path doesn't match a dimension for this optimization and interval.             |
| `optimize_unsupported_group_by`    | The `groupBy` dimension can't be used to group, such as `urlParameter`.                                                           |

Other requests can fail with these errors:

| Status | Code                            | Meaning                                                                |
| :----- | :------------------------------ | :--------------------------------------------------------------------- |
| `403`  | `missing_scopes`                | The token is missing the `sites:read` scope.                           |
| `403`  | `insufficient_permissions`      | The user who authorized the token doesn't have permission.             |
| `404`  | `resource_not_found`            | The `optimizeId` wasn't found.                                         |
| `404`  | `optimize_unknown_optimization` | No optimization matches the `optimization_id`.                         |
| `409`  | `conflict`                      | The `optimizeId` matches more than one resource your token can access. |

## Walkthrough: check which variation is winning

Imagine you're reporting to a client on a hero headline A/B test. They want to know which variation is winning, and whether the result holds for visitors in the United Kingdom. This walkthrough finds the test, reads its results, and then splits them by country.

#### Find the Optimize ID

List the Optimize IDs your token can access, then find the entry whose `resource.siteId` matches your site.

```bash
curl --request GET \
  --url 'https://api.webflow.com/v2/optimize' \
  --header 'authorization: Bearer YOUR_API_TOKEN' \
  --header 'accept: application/json'
```

The response lists each `optimizeId` with the site it belongs to:

```json
{
  "references": [
    {
      "optimizeId": "117420042",
      "resource": {
        "type": "webflow-site",
        "siteId": "6512a0f4c2b3d4e5f6a7b8c9",
        "workspaceId": "650e1f2a3b4c5d6e7f809142",
        "displayName": "Hitchhiker's Guide to the Galaxy"
      }
    }
  ]
}
```

#### Find the optimization and its interval

List the live optimizations for that `optimizeId`.

```bash
curl --request GET \
  --url 'https://api.webflow.com/v2/optimize/117420042/optimizations?status=live' \
  --header 'authorization: Bearer YOUR_API_TOKEN' \
  --header 'accept: application/json'
```

Find the test you want and note its `optimizationId`. Then choose an interval. Here, "Don't Panic hero headline" has two intervals, and the example shows the second. It started on `2026-08-15T17:30:00.000Z` and has no `end`, so it's still running. Its target goal is "Towel purchase".

```json
{
  "optimizationId": "417200971",
  "name": "Don't Panic hero headline",
  "optimizationType": "test",
  "status": "live",
  "intervals": [
    {
      "start": "2026-08-15T17:30:00.000Z",
      "goals": [
        { "goalId": "198042421", "name": "Towel purchase", "isPrimary": true, "metricType": "value" }
      ]
    }
  ],
  "currentGoals": [
    { "goalId": "198042421", "name": "Towel purchase", "metricType": "value", "source": "site" },
    { "goalId": "198042425", "name": "Book club signup", "metricType": "conversion", "source": "existing" },
    { "goalId": "198042420", "name": "AutoGoal: click engagement", "metricType": "conversion", "source": "optimization" }
  ]
}
```

#### Get the results

Request the results for a date range inside that interval. Because you omit `goalId`, the report measures the target goal.

```bash
curl --request GET \
  --url 'https://api.webflow.com/v2/optimize/117420042/optimizations/417200971/summary?intervalStart=2026-09-01T00:00:00Z&intervalEnd=2026-09-15T00:00:00Z' \
  --header 'authorization: Bearer YOUR_API_TOKEN' \
  --header 'accept: application/json'
```

Each entry in `variations` is one variation. The `comparison` object shows how it compares with the base variation. Here, the second variation has a conversion rate of 11.9% against the base variation's 10%, which is a lift of about 19%. It's marked as the winner with a statistical significance of 0.97.

```json
{
  "report": "optimization_summary",
  "goal": { "goalId": "198042421", "name": "Towel purchase" },
  "optimizationType": "test",
  "variations": [
    {
      "variationId": "617424201",
      "name": "Base variation",
      "sessions": 4100,
      "conversions": 410,
      "conversionRate": 0.1,
      "comparison": { "isBaseline": true }
    },
    {
      "variationId": "617424202",
      "name": "Don't Panic in large, friendly letters",
      "sessions": 4242,
      "conversions": 506,
      "conversionRate": 0.1193,
      "comparison": {
        "isBaseline": false,
        "isWinner": true,
        "lift": 0.193,
        "statisticalSignificance": 0.97
      }
    }
  ]
}
```

#### Split the results by country

Call [Get audience insights](/data/reference/optimize/get-audience-insights) with `groupBy=country` to see whether the result holds across regions. The request takes the same date range and `goalId` options as the results endpoint.

```bash
curl --request GET \
  --url 'https://api.webflow.com/v2/optimize/117420042/optimizations/417200971/audience-insights?intervalStart=2026-09-01T00:00:00Z&intervalEnd=2026-09-15T00:00:00Z&groupBy=country' \
  --header 'authorization: Bearer YOUR_API_TOKEN' \
  --header 'accept: application/json'
```

Each variation now includes one entry per country in `segments`. For the non-base variation, `trendDirection` shows how it compares with the base variation in that segment. `confidence` shows how much to trust that reading.

This example shows the United Kingdom segment for the non-base variation. It's trending `up` with a confidence of 0.93.

```json
{
  "segment": "GB",
  "segmentLabel": "United Kingdom",
  "sessions": 1337,
  "conversions": 191,
  "conversionRate": 0.1969,
  "lift": 0.6408,
  "trendDirection": "up",
  "confidence": 0.93
}
```

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

* [Get performance over time](/data/reference/optimize/get-performance-over-time): whether the lift is steady or fades
* [List optimization goals](/data/reference/optimize/list-optimization-goals): other goals to measure, using `goalId`
* [List dimension values](/data/reference/optimize/list-dimension-values): values to use in a `filter` for one audience

To browse every endpoint, see [Choose an endpoint](#choose-an-endpoint).