Working with AEO Analytics

The AEO Analytics API lets you read how AI bots visit your site, and how answer engines mention and cite you. AEO, or answer engine optimization, is the practice of improving how your content appears in AI-generated answers. Use the API to build reporting dashboards, sync metrics into other tools, or track how your brand shows up in answers over time.

Every endpoint is a read-only GET request. To read and manage AEO recommendations for a site, use the separate AEO Recommendations API.

AEO analytics entitlement required
AEO Analytics endpoints require an access token with the sites:read scope and a site or workspace with the AEO analytics entitlement. Every endpoint except the two bot traffic endpoints also requires Webflow AI to be enabled for the workspace.

Choose an endpoint

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

Bot traffic

Prompts

Visibility

Citations

Core concepts

Bot traffic and prompt data

The endpoints read two separate sets of data:

  • Bot traffic counts requests made to your site by the AI bots Webflow tracks, such as crawlers from OpenAI or Anthropic. Webflow records it from requests that reach your site.
  • Prompt data covers everything else. You track prompts, which are questions that Webflow asks answer engines. The visibility and citation reports are all calculated from the responses to those prompts.

Filtering one set doesn’t affect the other. A bot provider such as openai isn’t the same as an answer engine such as chat-gpt, and each is filtered with its own parameter.

Bot traffic counts requests from bots, not visitors or page views. For visitor traffic, use the Analyze API.

Topics, prompts, and executions

Prompt data is organized in three levels:

  • Topic: A group of related prompts, such as “Galactic travel.”
  • Prompt: A question you track, such as “What’s the best guide for traveling the galaxy?” Webflow runs each prompt through ChatGPT, Claude, Gemini, and Perplexity once a day.
  • Prompt execution: One answer from one answer engine to one prompt.

Call List Prompts first. Each topic and prompt has an id. Use these IDs as filters in the visibility and citation endpoints.

A prompt’s status is active, paused, pending, or archived. List Prompts returns active, paused, and pending prompts by default. Set status to archived or all to include archived prompts.

Each execution lists the mentions and citations found in the answer. A mention is your organization name or one of its aliases. A citation is a URL the answer cited. The competitorMentions field is reserved and is currently always empty. To see competitor citations, use List Competitor Citations.

Visibility score and citation rate

Both measures are fractions from 0 to 1. For example, 0.42 means 42%.

  • Visibility score: The share of responses to your tracked prompts that mention your organization name or one of its aliases.
  • Citation rate: Of the responses that cited at least one source, the share that cited one of your domains.

Your domains are your Allowed domains and your webflow.io subdomain, each with and without www.. Citation responses list them in domains.

Organization info

Some endpoints depend on the organization details saved on the site:

  • The visibility endpoints match responses against the organization name and aliases saved in the site’s Organization info. They return brandNames so you can see which names were matched. Without an organization name, they return 400 brand_not_configured.
  • List Competitor Citations uses the competitors saved in Organization info. Without an organization name, it returns 400 brand_not_configured. If no competitor has a domain, it returns 400 competitors_not_configured.

The bot traffic, prompt, and execution endpoints don’t return brandNames. A missing organization name doesn’t block them. Sending brandNames as a query parameter returns 400 aeo_input_validation.

Time windows

Pass startTime and endTime as UTC timestamps ending in Z, for example 2026-09-01T00:00:00Z. Don’t send offsets or date-only values. startTime is inclusive, and endTime is exclusive.

The rules depend on the endpoint:

  • Reports require startTime and endTime. The window can span at most 100 days, and endTime must be later than startTime. This covers the bot traffic, visibility, and citation endpoints.
  • Bot traffic also requires endTime to be no later than the start of the current UTC day, because today’s data isn’t available yet.
  • List Prompt Executions takes an optional window of at most 90 days. If you omit endTime, it’s now. If you omit startTime, it’s 90 days before endTime.
  • List Prompts and Get Prompt Execution take no time window.

Filtering

Narrow the visibility and citation reports to a subset of your prompt data with these parameters. Repeat a parameter to pass more than one value, for example topicIds=ID1&topicIds=ID2. Don’t use comma-separated values.

  • topicIds: Include only these topics, up to 50.
  • promptIds: Include only these prompts, up to 50.
  • answerEngine: Include only one answer engine. The visibility endpoints and List Prompt Executions use this singular form.
  • answerEngines: Include only these answer engines, up to 4. The citation endpoints use this plural form.

Answer engines are chat-gpt, gemini, claude, and perplexity.

List Prompts and List Prompt Executions filter by a single topicId, and List Prompt Executions also takes a single promptId. You can’t send promptId and topicId together.

Separate filters combine with AND. Values within one filter combine with OR. Each endpoint supports its own set of filters, so check the parameters on the endpoint’s reference page.

Bot traffic endpoints filter by bot instead. Use botProviders for one or more providers, and botCategory for one of agent, indexer, training, or multiple.

Daily and weekly series

Get Visibility Score returns one score for the whole time range. To get one score per day or week, send both timeseries[granularityPeriod] and timeseries[bucketTimeZone]:

timeseries[granularityPeriod]=day&timeseries[bucketTimeZone]=America/New_York

granularityPeriod is day or week. Weeks start on Sunday. bucketTimeZone must be a valid IANA time zone, such as UTC or America/New_York. Bucket timestamps are returned as UTC instants.

Pagination

Endpoints paginate in one of three ways:

  • List Prompts and List Prompt Executions use cursors. When a response includes nextCursor, pass it as cursor to get the next page. nextCursor is present whenever a page is full, so the next page can be empty.
  • List Citations uses limit and offset. To get the next page, set offset to the previous offset plus limit. The response includes hasMore.
  • All other endpoints return a single response and aren’t paginated.

Rate and concurrency limits

AEO Analytics has two limits:

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

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

Errors

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

CodeMeaning
aeo_input_validationA parameter is missing, invalid, or not recognized, or a cursor isn’t valid. The details array lists each failing field.
validation_errorsite_id or execution_id isn’t a valid ID.
invalid_time_rangeendTime isn’t later than startTime.
time_range_too_wideThe time range is longer than 100 days, or longer than 90 days for List Prompt Executions.
end_time_too_recentOn the bot traffic endpoints, endTime is later than the start of the current UTC day.
unsupported_bot_providerA botProviders value isn’t a supported provider.
conflicting_scope_filtersA List Prompt Executions request sent both promptId and topicId.
brand_not_configuredThe site has no organization name in Organization info. This applies to the visibility endpoints and List Competitor Citations.
competitors_not_configuredNo competitor in Organization info has a domain. This applies to List Competitor Citations.

Requests can also fail with these errors:

StatusCodeMeaning
403missing_scopesThe token is missing the sites:read scope.
403forbiddenThe site doesn’t have the AEO analytics entitlement, or Webflow AI isn’t enabled for the workspace.
403insufficient_permissionsThe user who authorized the token doesn’t have permission.
404resource_not_foundThe site wasn’t found, or the access token can’t access it. For Get Prompt Execution, no execution on the site matches execution_id.

Walkthrough: measure AI bot traffic

Imagine you’re reporting on how AI bots visit your site. You want to know how many requests the agent bots from OpenAI made last week, and then which pages they requested most.

1

Request the bot traffic total

Call Get Bot Traffic with a window that ends at the start of a UTC day, a botProviders value, and a botCategory.

curl --request GET \
--url 'https://api.webflow.com/beta/sites/{site_id}/aeo/reports/bot_traffic?startTime=2026-09-01T00:00:00Z&endTime=2026-09-08T00:00:00Z&botProviders=openai&botCategory=agent' \
--header 'authorization: Bearer YOUR_API_TOKEN' \
--header 'accept: application/json'
2

Read the total

totalRequestCount is the number of successful requests the bots made to your site. It isn’t a count of visitors. The filter field echoes the filters you sent.

{
"report": "bot_traffic",
"window": {
"startTime": "2026-09-01T00:00:00Z",
"endTime": "2026-09-08T00:00:00Z"
},
"totalRequestCount": 4242,
"filter": {
"botProviders": ["openai"],
"botCategory": "agent"
}
}
3

List the pages bots requested most

Call Get Bot Traffic by Page with the same window and filters. Results are sorted by requestCount, highest first.

curl --request GET \
--url 'https://api.webflow.com/beta/sites/{site_id}/aeo/reports/bot_traffic_pages?startTime=2026-09-01T00:00:00Z&endTime=2026-09-08T00:00:00Z&botProviders=openai&botCategory=agent&limit=10' \
--header 'authorization: Bearer YOUR_API_TOKEN' \
--header 'accept: application/json'

Each row is a page, a CMS item, or a file. A file with no page, such as /sitemap.xml, has only a title, set to its path.

{
"report": "bot_traffic_pages",
"window": {
"startTime": "2026-09-01T00:00:00Z",
"endTime": "2026-09-08T00:00:00Z"
},
"data": [
{ "requestCount": 420, "page": { "pageId": "68cdb4b5a699862fcd8c1a31", "title": "Home" } },
{
"requestCount": 42,
"page": {
"pageId": "68cdb4b5a699862fcd8c1a30",
"collectionId": "68cdb4b5a699862fcd8c1a40",
"itemSlug": "dont-panic",
"title": "Don't Panic"
}
},
{ "requestCount": 7, "page": { "title": "/sitemap.xml" } }
]
}

Walkthrough: find where competitors are cited instead of you

Imagine you want to know which topics answer engines rarely connect with your brand, and which competitor sites they cite instead. This walkthrough finds your topics, compares their visibility, and then lists the responses that cited a competitor.

1

List your topics

Call List Prompts to see your tracked prompts and the topic each belongs to. Note the topic IDs.

curl --request GET \
--url 'https://api.webflow.com/beta/sites/{site_id}/aeo/prompts?limit=50' \
--header 'authorization: Bearer YOUR_API_TOKEN' \
--header 'accept: application/json'

Each item includes the prompt’s topic. In this example, the prompt belongs to the “Galactic travel” topic.

{
"items": [
{
"id": "68cdb4b5a699862fcd8c1a01",
"text": "What's the best guide for traveling the galaxy?",
"topic": { "id": "68cdb4b5a699862fcd8c1a02", "name": "Galactic travel" },
"tags": [],
"status": "active",
"createdAt": "2026-08-01T16:20:42.000Z"
}
]
}
2

Compare visibility by topic

Call Get Visibility Score Breakdown with breakdown=topic. Results are sorted by visibilityScore, highest first.

curl --request GET \
--url 'https://api.webflow.com/beta/sites/{site_id}/aeo/reports/visibility_breakdown?startTime=2026-09-01T00:00:00Z&endTime=2026-09-08T00:00:00Z&breakdown=topic' \
--header 'authorization: Bearer YOUR_API_TOKEN' \
--header 'accept: application/json'

brandNames shows the organization name and aliases the report matched. Here, the “Galactic travel” topic has a visibility score of 0.42, so 42% of its responses mention your organization. That’s much lower than the “Towels” topic at 0.9.

{
"report": "visibility_breakdown",
"window": {
"startTime": "2026-09-01T00:00:00Z",
"endTime": "2026-09-08T00:00:00Z"
},
"breakdown": "topic",
"brandNames": ["Hitchhiker's Guide", "The Guide"],
"data": [
{ "visibilityScore": 0.9, "topic": { "id": "68cdb4b5a699862fcd8c1a03", "name": "Towels" } },
{ "visibilityScore": 0.42, "topic": { "id": "68cdb4b5a699862fcd8c1a02", "name": "Galactic travel" } }
]
}
3

List the responses that cited a competitor

Call List Competitor Citations with the weaker topic’s ID. It returns the newest responses that cited a competitor’s domain and none of your domains.

curl --request GET \
--url 'https://api.webflow.com/beta/sites/{site_id}/aeo/reports/competitor_citations?startTime=2026-09-01T00:00:00Z&endTime=2026-09-08T00:00:00Z&topicIds=68cdb4b5a699862fcd8c1a02&limit=10' \
--header 'authorization: Bearer YOUR_API_TOKEN' \
--header 'accept: application/json'

competitorDomains lists the competitor domains the report checked, which come from the competitors in Organization info. Each row shows the prompt, the answer engine, and the URLs the response cited.

{
"report": "competitor_citations",
"window": {
"startTime": "2026-09-01T00:00:00Z",
"endTime": "2026-09-08T00:00:00Z"
},
"competitorDomains": ["example.net", "www.example.net"],
"data": [
{
"timestamp": "2026-09-07T06:00:12.000Z",
"topic": { "id": "68cdb4b5a699862fcd8c1a02", "name": "Galactic travel" },
"prompt": {
"id": "68cdb4b5a699862fcd8c1a01",
"text": "What's the best guide for traveling the galaxy?"
},
"answerEngine": { "key": "perplexity", "name": "Perplexity" },
"citations": [
"https://example.net/travel",
"https://example.org/travel/guides"
]
}
]
}

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

To browse every endpoint, see Choose an endpoint.