API and MCP access is included on all plans. Browse the API Reference for the full endpoint contract.
Visibility
Visibility, mentions, position, sentiment and rank, or one row per AI answer. Dashboard, Rankings, Analyse, Prompts and Brand Mentions.
Sources
Cited domains, hostnames, URLs and pages. Sources overview, tables, gaps and page sentiment.
Narratives
Reputation narratives, themes and sentiment. Requires the Reputation feature.
Endpoints
All three use a team API key as a Bearer token.
{project} is the project UUID from GET /api/v1/projects.
Your first query
Visibility per brand over the last four weeks, best first:columns tells you how to format each cell. rows are raw values keyed by column id. A percent column is a decimal: 0.203 is 20.3%.
The request
Rows and dimensions
A row is one dimension group."dimensions": ["brand"]is one row per brand."dimensions": ["brand", "service"]is one row per brand per platform."dimensions": ["brand", "date"]is a timeline, one row per brand per day.weekandmonthbucket the same way. Use one time bucket per query."dimensions": ["service"]on visibility, withoutbrand, aggregates across the project’s brands: one row per platform.- No
dimensionsat all gives one project-level row, which is what a KPI card needs.
brand comes with brand_uuid, prompt with prompt_uuid, topic with topic_uuid, and so on. Prompts without a topic form one group whose topic and topic_uuid are null.
Filters
Every filter field has a role. The role decides where the condition runs and which operators it accepts. You never choose between awhere and a having; the field does.
Two things follow from this:
- “ChatGPT only” changes visibility.
service in ["chatgpt"]makes visibility = mentions on ChatGPT ÷ answers on ChatGPT, the same as the platform filter in the app. - A brand filter does not.
brand in ["…"]keeps one row and drops the rest. That brand’s visibility is exactly what it was with no filter. Use it to pick your brand for a KPI, not to change the maths.
in and not_in take values (a list). Comparisons take a numeric value. contains takes a text value and matches the label case-insensitively. The wrong pairing is a 422.
Population filter values
No
service filter means the project’s active platforms, the same default as the rest of the API.
The content cuts, mentions_brand, cites_domain and (on sources) mention_sentiment, make “answers that name us” or “answers that cite Reddit” the whole population. Every ratio is then a share of those answers. Add the answers measure to see the denominator.
Compare window
compare_start_date and compare_end_date run the same query over a second window. Same filters, other dates. Every measure then gains two columns, right after it:
- Both work in
filtersandorder_by. “Movers” isorder_by: [{ "field": "share_change", "dir": "desc" }]. “Dropped 10 points” is{ "field": "visibility_change", "op": "lte", "value": -0.1 }. - Rows match on their dimension keys. A
date,weekormonthrow matches by position from each window’s start: day 1 pairs with day 1, so a 28-day chart overlays the previous 28 days. A day with no answers leaves a hole and does not shift the days after it. Windows of different lengths pair from the start; the longer tail readsnull. - A single-row result also fills
benchmarks[0]withvalue,difference,changeandfavourablefor the first measure, so a KPI card can show its trend.avg_positioncounts down as good. - Ratios compare fairly across unequal windows. Counts do not: 186 mentions over 28 days against 71 over 14 is not a trend.
- Visibility with the
answerdimension is a list and takes no compare window.
Paging and rank
limitandoffsetcut the rows.page.totalin the response is the row count before the cut, so you can build a pager. It is correct even on a page past the end.- The
rankmeasure numbers rows within their group by the entity’s main measure: visibility on visibility, citations on sources. It is assigned before any entity or measure filter, so{ "field": "rank", "op": "lte", "value": 3 }on["brand", "service"]is “the top three brands on each platform” and does not renumber anyone. topkeeps the best values of one dimension over the whole window, on every row."top": { "field": "brand", "by": "visibility", "limit": 6 }on["brand", "date"]gives the six most visible brands for every day, wherelimit: 6would give six rows. The field must be indimensionsand cannot bedate,weekormonth. Measures where lower is better, such asavg_position, rank ascending. It hides rows and never changes a number.containson an entity field is your search box:{ "field": "url", "op": "contains", "value": "reddit" }.
Related blocks
Some entities offer extra per-row lists that would not fit a flat row: the brands named on a cited page, the sources an answer cites, a narrative’s evidence. Ask for them inrelated; they come back under related in the response, one list per block. Each entry carries row, the index of its row in rows, so filtering or sorting rows on your side never loses the link.
The response
Finding identifiers
Filters take public identifiers only: UUIDs, slugs, codes and names. Two endpoints tell you what to send. The schema endpoint lists what an entity accepts. Itsfilter_fields carry the role and operators of every field:
value in the filter. label and description are for people: the primary brand is marked, domains carry their citation count, narratives their sentiment and theme. Fields with a list: brand, mentions_brand, page_brand, narrative, carries_narrative, theme, carries_theme, prompt, topic, tag, service, country, domain, cites_domain, page_type, category, brand_on_page, mention_sentiment, page_sentiment, narrative_sentiment.
Errors
Validation problems return422 with errors keyed by the offending path, so a wrong body tells you where.
403 means the query reads reputation data (the narratives entity, or a carries_narrative / carries_theme filter) and the team does not have the Reputation feature. 404 means the project is not in the key’s team.
Deprecated endpoints
The Reports API replaces these single-report endpoints. They keep working until a removal date is announced. New integrations should use the report query.
All paths start with
/api/v1. The detail endpoints (one theme, narrative or response by UUID), mentions and visibility-decomposition are not deprecated.
Using it from an AI assistant
The MCP server exposes the same capability as tools:run_report runs a query, get_report_schema and list_report_values describe what it accepts, and get_report_query_guide explains how to write one. An assistant connected to Surfacd can build any of the reports on the entity pages on request.