Skip to main content
Every recipe in the cookbook follows these rules. Most wrong numbers come from breaking one of them, and none of them raises an error when you do.

Dates are inclusive UTC days

start_date and end_date are YYYY-MM-DD and both are included. September is 2026-09-01 to 2026-09-30, not 2026-10-01.
  • Answer reports (visibility, citations, sentiment, answers) file each answer under the UTC day it was collected.
  • Traffic reports (AI crawlers, AI referrals) take an optional timezone (an IANA name such as Asia/Karachi, default UTC) for the day boundaries and the date buckets.
  • An answer report covers at most 92 days, a traffic report at most 400 days. For a longer history, make one call per quarter.
To compare with the previous period, use a window of the same length that ends the day before the current one starts:

Know the scale of each number

Multiply a fraction by 100 to show a percentage. Report a change in a rate as percentage points (0.42 → 0.47 is +5 pp), not as a percentage of a percentage.

Don’t average daily rows to get a period score

A monthly Visibility Score is computed from every answer in the month. The average of thirty daily scores is a different number: it gives a day with three answers the same weight as a day with forty, and prompt_coverage in particular does not average at all (a prompt named on one day of the month counts as covered for the month). Ask for the period in one call without date in group_by. Use daily rows for the trend line only. The same applies to weeks and months: there are no week or month buckets, so make one call per week or per month. See Headline and daily together. Counts are different: answers and citations add up across days.

Changes are computed client-side

No endpoint returns a delta. Make two calls with windows of equal length and subtract. See Visibility Score with change.

Rows have named fields

Every row is an object with named fields, so you never read a value by position. Grouped dimensions appear on the row under their own names: date, platform, category, and prompt as { id, text }. Visibility rows always carry brand: { name, owned } and rank.

citation_share is computed after the filter

In the citations report, citation_share is each row’s share of the citations that pass your filter, within its group. Filter on owner = own and your own domains will always add up to 1. To get the share of all citations that went to you, request the report unfiltered and sum the rows where owner is own. See Owned citation share.

Ordering

  • Visibility rows are sorted by the grouped dimensions (dates ascending), then by rank. Pass sort: { field, dir } to sort by a metric instead.
  • Citation rows are sorted by the grouped dimensions, then by rank: most answers citing it first.
  • Within each group, rank restarts at 1.
Sort again in your own code when you need a particular order. It costs nothing and makes the direction explicit.

Pagination

Reports return at most 100 rows a page (traffic reports 500, answers 50). info.next_cursor is null on the last page; otherwise pass it back as cursor with the same query. A cursor is bound to its query, so changing a date or a filter with an old cursor is refused with 400 invalid_cursor. The SDKs handle this for you: A leaderboard or a top-N list usually fits on one page. Anything you sum or join across rows, such as an owned share, needs every page.

Google pages with no AI Overview are left out

About three in ten Google result pages show no AI Overview at all. Those are a finding about the question, not answers that failed to name you, so they are excluded from every rate and from answers on ai_overviews and ai_mode. A day with few answers on those engines is usually this, not a broken scan.

Engines and prompts start on different days

An engine added to the site, or a prompt switched on, produces data from its first scan. Check answers before reading a rate: a rate over two answers is noise. Engine IDs are listed by GET /v1/platforms.

Rate limits

120 requests a minute per key, 20 of them writes. A 429 carries Retry-After; the SDKs wait exactly that long and retry. Cache anything that changes rarely, such as your site list and competitors. See Rate limits.

Keys stay on a server

The API sends no CORS headers on purpose. Call it from a server, a script or a notebook, never from code a browser downloads.