DocsAPI & MCP

Resource reference

Every v1 REST resource — request contract, response fields, types, and scales — for brands, overview, prompts, competitors, sources, urls, answers, shopping, and crawler ingest.

All read resources share one request contract; the ingest resource is documented separately at the end. Read Conventions first — it defines the scales, the fixed 7-day window, and null handling referenced throughout.

Shared request contract (read resources)

  • Method / path: GET /api/v1/{resource}
  • Auth: Authorization: Bearer <key> (see Authentication).
  • Query params: brand=<brandId> — optional; defaults to your first brand. There are no date parameters; the window is fixed at 7 days. The list resources also take optional cursor/limit pagination params (additive, W5) — see Limits; a request without them gets the exact pre-pagination response.
  • Response: JSON. Metric fields carry the scale conventions; nullable fields are null, never 0, when empty. Every /api/v1 and /api/mcp response carries the X-MF-API-Version header — see the Changelog.

The valid resources are: brands, overview, prompts, competitors, sources, urls, answers, shopping, shopping-trend.

Two additive endpoints sit outside the fixed 7-day contract: GET /api/v1/metrics/history and GET /api/v1/crawlers take window parameters.

GET /api/v1/brands

Lists the brands your key can see. Takes no brand param.

{ "brands": [ { "id": "…", "name": "…", "domain": "…", "archived": false } ] }

archived (additive) is true for a brand whose collection was stopped from Brand hub → Danger Zone. Archived brands never leave this list, and every read resource keeps answering for them — the flag tells you the data stops at the archive date; it doesn't hide it.

GET /api/v1/overview

The visibility scorecard for one brand over the 7-day window.

FieldTypeMeaning
brand{id,name,domain}the tracked brand
window_daysint7
run_countintblended runs in the window
visibilitynumber 0–100rounded to 0.1
presence_ratenumber 0–1rounded to 0.001
share_of_voicenumber 0–100deprecated, frozen — percentage, rounded to 0.1
share_of_voice_fractionnumber 0–1additive — same number on the fraction rule, rounded to 0.001
citation_sharenumber 0–1 | nullunrounded
sentiment_indexnumber 0–100 | null50 = neutral
avg_positionnumber | nullmean 1-based rank
low_confidencebooltrue when run_count < 5
has_prior_windowboolwhether a prior 7-day window has runs

GET /api/v1/prompts

{ brand, prompts: [...] } — all active prompts for the brand.

FieldTypeMeaning
idstringprompt id
textstringthe prompt
stagestringfunnel stage label, or
topicstring | nullfrozen legacy field — the first tag
topic_idstring | nulltopic folder id (additive)
topic_namestring | nulltopic folder name (additive)
tagsstring[]tags
est_volumenumber | nulllegacy point volume estimate
est_volume_band[low,high] | nulldemand band (AI asks/mo)
volume_confidencestring | nullconfidence tier
matched_gsc_querystring | nullGSC query behind a measured tier
gsc_impressions_28dnumber | nullthat query's 28-day site impressions
presence_ratenumber 0–1rounded to 0.001
visibilitynumber 0–100rounded to integer
avg_positionnumber | nullmean 1-based rank

topic is frozen (it stays the first tag); folders are exposed additively as topic_id/topic_name — see Conventions.

GET /api/v1/competitors

{ brand, competitors: [...] } — one row per tracked entity, including your own brand (flagged is_brand).

FieldTypeMeaning
idstringentity id
namestringentity name
domainstring"" if the competitor has none
is_brandbooltrue for your brand's row
visibilitynumber 0–100rounded to 0.1
share_of_voicenumber 0–100deprecated, frozen — percentage, rounded to 0.1
share_of_voice_fractionnumber 0–1additive — same number on the fraction rule, rounded to 0.001
citation_sharenumber 0–1 | nullunrounded
sentiment_indexnumber 0–100 | null
avg_positionnumber | nullmean 1-based rank

Every metric here, share of voice included, blends the eight search-grounded engines (see Conventions).

GET /api/v1/sources

{ brand, sources: [...] } — the top 20 cited domains, by citations descending.

FieldTypeMeaning
domainstringcited domain
classificationstringowned / competitor / editorial / ugc / … — the effective label; a workspace reclassification (Sources → Type) is already merged in
overriddenbooltrue when classification is a workspace reclassification rather than the automatic label
citationsinttotal citations to this domain in the window
brand_presentboolbrand mentioned in ≥1 run citing this domain

GET /api/v1/urls

{ brand, urls: [...] } — the top 50 cited URLs across all domains (content keys: YouTube watch?v= spellings and Reddit permalink variants merge; elsewhere only fragments and tracking parameters — utm_*/ref/fbclid/gclid — are stripped, so query-keyed content like item?id= threads stays distinct), by peak-window citations.

FieldTypeMeaning
urlstringcanonical cited URL
domainstringits domain
titlestring | nullpage title when an engine reported one
classificationstringthe domain's effective classification (reclassifications merged)
overriddenbooltrue when the classification is a workspace reclassification
citationsintcitations in the current 7-day window (0 = cited in the prior window only)
prior_citationsint | nullcitations in the prior 7 days; null = no prior window observed yet, not zero
answersintdistinct answers citing it in the current window
enginesstring[]engines citing it
brand_presentboolbrand mentioned in ≥1 answer citing this URL
page_typestring | nulldeterministic page type from the URL path + stored title: listicle / comparison / howto / review / docs / forum / news / product; null = no recognizable signal (unclassified, never a guessed default)

GET /api/v1/answers

{ brand, answers: [...] } — up to 200 runs, newest first. Includes all engines (not just blended).

FieldTypeMeaning
idstringrun id
datestring YYYY-MM-DDcollection date
regionstringe.g. us
enginestringone of the ten engine ids
modelstringmodel version ("" if unknown)
prompt_idstringsource prompt
brand_presentboolbrand mentioned in this run
mentions[{name,order,sentiment}]order 1-based; sentiment positive/neutral/negative/null
citations[{domain,url}]cited sources

GET /api/v1/metrics/history

Daily time series for one metric — the same series the dashboard charts, additive (W5). Unlike the resources above, it takes window parameters.

Query params:

ParamValuesMeaning
brandbrand idoptional; defaults to your first brand
metricvisibility | share_of_voice_fraction | citation_share | sentiment_indexrequired
days7 | 14 | 28 | 56preset window (default 28), anchored at the newest collected day; 56 is API-only (the dashboard range control tops out at 28) and spans the whole live run horizon, so its trend is always null
from, toYYYY-MM-DDcustom inclusive range (instead of days; max 366 days)
engineengine idsingle-engine series; default blends the search-grounded engines (see Conventions)
competitorstrueinclude tracked-competitor series alongside the brand

Response envelope:

FieldTypeMeaning
brand{id,name,domain}
metric / unitstringunit is score_0_100, fraction_0_1, or index_0_100
window{start,end,days}resolved bounds; start/end null when nothing was ever collected in scope
enginestring | nullecho of the filter
engine_scopestringblended_search_grounded or single_engine
sourcestringruns (live-computed, last 56 days — the dashboard's exact code path) or daily_stats (nightly aggregate, older custom ranges)
metrics_versionintcurrent metric-formula version
seriesarrayone entry per entity (below)
notestring | nullhonesty caveats (single-day window, empty scope, trend suppressed at the run horizon, preset axis clamped to the horizon, daily_stats limits)

Each series[] entry:

FieldTypeMeaning
id / name / is_brandentity identity
points[{date,value,sample_size}]one point per calendar day of the window; value null + sample_size 0 = nothing collected that day (null is not zero); value null with sample_size > 0 = collected but unmeasurable (e.g. no run cited any source)
window_valuenumber | nullthe metric over the whole window (same math as the dashboard KPI); null on daily_stats source
window_run_countintruns behind window_value
low_confidenceboolwindow_run_count < 5
trendobject | null{previous_window_value, delta, delta_unit} vs the prior same-length window; null for single-day windows, on daily_stats source, and whenever the prior window would fall outside the 56-day live run horizon (days=56 presets, custom ranges starting less than days after the horizon) — a truncated baseline is never compared, note says so

Scales: visibility and sentiment_index are 0–100 rounded to 0.1; fraction metrics are 0–1 rounded to 0.001. delta_unit is relative_pct for visibility, fraction_points / index_points for the others. On source: daily_stats, blended share_of_voice_fraction/citation_share/sentiment_index are not derivable from the stored per-engine aggregates — the endpoint returns 400 suggesting engine= rather than a fabricated blend.

Also available as the get_metrics_history MCP tool.

GET /api/v1/crawlers

Agent analytics over the API (additive, W5): which AI agents read the brand's own site, from its connected crawler logs, plus each agent's robots.txt verdict — the same data as the /crawlers dashboard and its per-agent drill-down. Plan-gated like that page (Growth and above): a Starter/Trial workspace key gets 403. The operator key is exempt.

Query params: brand (as everywhere) and days7 (default), 14, or 28.

Top level:

FieldTypeMeaning
brand{id,name,domain}
window_daysintecho of days
connectedboolfalse = no crawler logs have EVER been received for this brand
last_event_atstring | nullnewest event on record, unwindowed; null = never connected
robots_fetch_stateok | absent | errorabsent = no robots.txt (a real finding: nothing blocked); error = couldn't check — verdicts are null, not a green light
totals{hits, pages}window totals; null when connected is false (unknown, never a fabricated zero)
agentsarrayevery known AI agent + anything the logs carried (below), by hits descending
notestring | nullhonesty caveats: never-connected, quiet pipe (nothing from ANY agent in 7 days — zero hits may be missing data), robots fetch failure

Each agents[] entry:

FieldTypeMeaning
agentstringstable key (e.g. gptbot)
label / operatorstringdisplay name and the company running it
purposetraining | search_index | user_fetchblocking search_index/user_fetch agents removes you from answers, not just training
enginestring | nullthe product engine this crawler feeds, when it maps cleanly
hits / pagesint | nullwindow activity; null when no logs were ever connected — unknown, not zero
first_seen / last_seenstring | nullwindow-scoped timestamps; null when this agent has no hits
robots{verdict, rule, blocked_path} | nullthis agent's robots.txt verdict (allowed/blocked/unaddressed); null when the fetch failed — "couldn't check", never reassurance

Also available as the get_crawler_analytics MCP tool.

GET /api/v1/shopping

The shopping leaderboard over the 7-day window. Built from the same computation as the dashboard's shopping analytics. Metrics blend every collecting engine; the per-engine split is the channel_coverage block.

Top level:

FieldTypeMeaning
brand{id,name,domain}
window_daysint7
totals.carouselsintdistinct carousels captured
totals.productsintdistinct products
totals.own_appearancesintown-product placements
totals.own_avg_positionnumber | nullnull = own never appeared
productsarrayup to 30 (below)
channel_coveragearrayper-engine shopping capability (additive; below)
notestring | nullabsence explainer (see Conventions)

Each channel_coverage[] entry describes one shopping-capable engine:

FieldTypeMeaning
enginestringengine id (chatgpt, aio, aimode, copilot, perplexity, gemini)
statusstringparses-today — the engine's payloads carry parseable product data; awaiting-payload — extraction is wired but the data vendor delivers no product fields yet
sampledboolthe engine collected runs in this window
carouselsint | nullcarousels observed this window; null exactly when unobservable — the engine wasn't sampled, or the payload is awaiting the vendor. Null is not zero: a 0 means a sampled, parsing engine showed no carousel

Engines with no shopping surface (Claude, DeepSeek, Grok, Mistral) are not listed. See Engine coverage for the product-side view of the same map.

Each products[] entry:

FieldTypeMeaning
productstringproduct name
merchant_domainstring | nullderived from the product link only
merchantstring | nullverbatim engine merchant text (usually null)
pricestring | nullthe engine's verbatim price text; null when the engine sent none
price_amountnumber | nullexact amount — taken from the payload's structured price fields when the engine provides them, else parsed once from price; null = unknown, never guessed
currencystring | nullcurrency of price_amount; null when the payload didn't pin one
urlstring | nullproduct link
is_ownboolyour own product
appearancesinttotal placements
carousels_featuringintdistinct carousels featuring it
win_ratenumber 0–1share of its appearances in slot #1
visibility_sharenumber 0–1carousels featuring it ÷ window carousels
avg_positionnumbermean slot (1 = front of shelf)

Price fields are independent: some engines send only structured numbers, so price can be null while price_amount and currency are set. When you need a display price, format it from price_amount + currency (that is how the dashboard renders one); a fully-null trio means the engine sent nothing.

GET /api/v1/shopping-trend

{ brand, window_days, days_observed, trend: [...], note }. days_observed counts collected days; days without carousels produce no point.

Each trend[] point:

FieldTypeMeaning
datestring YYYY-MM-DD
carouselsintcarousels captured that day
own_carousel_sharenumber 0–1that day's carousels featuring an own product
own_win_sharenumber 0–1that day's carousels with an own product in slot #1
own_avg_positionnumber | nullnull on days you weren't shelved

note flags a single-day snapshot; check days_observed >= 2 before charting a line.

POST /api/v1/crawler-events

The one write endpoint — ingests AI-crawler logs. Reachable by full and ingest keys (see Crawler ingest).

  • Path: POST /api/v1/crawler-events?brand=<id> (brand optional; defaults to first).
  • Body: raw text — nginx/apache combined log lines, or NDJSON (Cloudflare Logpush / Vercel Log Drains). Up to 10 MB per request. Only recognised AI agents are stored, paths only.
  • Success 200:
{ "inserted": 0, "matched": 0, "totalLines": 0, "unparsed": 0 }
FieldMeaning
insertedrows actually written (duplicates skipped)
matchedrecognised AI-crawler hits
totalLineslines that parsed as requests
unparsedlines that couldn't be parsed