Trigger measurement cycles and read visibility, trends, crawler activity, the sources the answers cite and the individual answers behind them.
Paths on this page are relative to https://shruwd.io/api/v1.
| Method | Path | Scope | Role |
|---|---|---|---|
POST | /brands/{brandId}/cycles | write | editor |
GET | /brands/{brandId}/cycles | read | viewer |
GET | /brands/{brandId}/visibility | read | viewer |
GET | /brands/{brandId}/visibility/series | read | viewer |
GET | /brands/{brandId}/crawlers | read | viewer |
GET | /brands/{brandId}/crawlers/paths | read | viewer |
GET | /brands/{brandId}/sources | read | viewer |
GET | /brands/{brandId}/answers | read | viewer |
Measurement is asynchronous. Running a cycle schedules work; results arrive over the following hours. Nothing you can call returns a finished measurement, so do not poll in a tight loop and do not report a number before there is one.
Run a cycle
curl -X POST "https://shruwd.io/api/v1/brands/northwind/cycles" \
-H "Authorization: Bearer $SHRUWD_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
Creates a manual cycle due now and returns its id. It draws on the same period allowance as any cycle.
One manual cycle per brand at a time: while one is pending or running, another is
409 cycle_in_progress. On a free plan this is the one snapshot, and is
409 snapshot_taken once used. A workspace paused pending review is 409 workspace_on_hold.
GET /brands/{brandId}/cycles?limit= returns recent cycles with their state and counts.
Read visibility
curl "https://shruwd.io/api/v1/brands/northwind/visibility?engine=google_aio" \
-H "Authorization: Bearer $SHRUWD_API_KEY"
| Parameter | |
|---|---|
engine | google_aio (the default), google_ai_mode or chatgpt |
from to | YYYY-MM-DD. to defaults to today in the brand's timezone |
Returns { engine, range, asOf, nResponses, entities, prompts, intents }. Each entity
carries mentionRate and shareOfVoice, each with its interval or an explicit
insufficient_data, and avgProminence: an average with no interval, near 1 when the
entity is named at the start of the answer, null when it was never named.
firstNamed is how often the entity is the first of your tracked companies an answer
names, with its interval, over the answers share of voice uses. It is computed when a cycle
closes, for 7-, 30- and 90-day ranges; for any other range it is undefined. An untracked
company can come earlier, so read it as a sign of being the first pick, not a recommendation.
intents is prompts pooled by question type, buying first: commercial, comparison,
problem, informational, navigational, each only when a prompt has it. Each row
carries prompts (how many it pools), the summed counts, and selfMentionRate with its
own n. Like prompts it covers from to to, while entities describe asOf, so the
totals can differ by a few days' runs.
If from reaches past your plan's history window it is clamped, and the response says so
in historyFrom rather than failing.
Read the trend
curl "https://shruwd.io/api/v1/brands/northwind/visibility/series?window=30" \
-H "Authorization: Bearer $SHRUWD_API_KEY"
| Parameter | |
|---|---|
window | 7, 30 or 90. Default 30 |
from to | Default is the last ninety days |
Returns points (one per cycle close) and, per entity, a series index-aligned with
it. A point below the display floor is insufficient_data, never a number.
modelChanges lists the moments the provider changed its underlying models. A difference
spanning one of those may be the model rather than you — see
When a number has moved.
Read crawler activity
curl "https://shruwd.io/api/v1/brands/northwind/crawlers" \
-H "Authorization: Bearer $SHRUWD_API_KEY"
Per-bot activity with verified and unverified hits kept separate. Only verified hits count toward anything.
daily carries a per-day series per bot, index-aligned with daily.days.
A day with no log ingest is null, not 0. It means nobody was watching, not that
nothing happened — do not chart it as zero.
Read crawler activity per path
Where a bot's errors came from:
curl "https://shruwd.io/api/v1/brands/northwind/crawlers/paths?bot=oai_searchbot&status=error" \
-H "Authorization: Bearer $SHRUWD_API_KEY"
| Parameter | |
|---|---|
from to | Default is the last thirty days. Per-path detail is kept 90 days |
bot | A botId from the crawlers read, such as oai_searchbot |
status | 2xx, 3xx, 4xx, 5xx or error (4xx or 5xx) |
contentOnly | 1 keeps only pages a person reads |
limit | 1 to 200. Default 50 |
One row per bot and path, verified hits only. worstStatus tells a missing page (404)
from a refusal (403). totalPaths above the rows returned means the list was cut.
contentPage: false marks files and framework data such as _payload.json, and sign-in
and account pages. Some frameworks' data files return 404 to every visitor by design, so
an error there is rarely a lost citation.
Read the sources the answers cite
Which sites and pages the answers cite, and whose they are:
curl "https://shruwd.io/api/v1/brands/northwind/sources?engine=google_aio" \
-H "Authorization: Bearer $SHRUWD_API_KEY"
| Parameter | |
|---|---|
from to | YYYY-MM-DD. Default is the last thirty days |
engine | google_aio, google_ai_mode or chatgpt. Default is every engine |
promptGroupId | Only the answers to this prompt. A finding on a prompt carries its id as subjectRef |
limit | 1 to 100. Default 25 |
One row per site, most-cited first. answers counts the answers citing it, and rate is
the share of all answers in answers.total, with its interval, or an explicit
insufficient_data below ten answers. byEngine gives each engine's count with its own
total. totalSources above the rows returned means the list was cut.
kind says whose a site is: yours or competitor when it is a company you track (named
in owner), otherwise review_directory, forum, wikipedia, video or editorial
(any other site: a blog, a publication, a vendor). yourAnswers counts the answers citing
your own pages there, or videos on one of your
YouTube channels. Each site carries its five
most-cited pages, without tracking parameters; for a video, the page's owner is the
company whose channel published it. findings lists up to three findings about the site
that are still open or being fixed.
answers.citingAny is how many answers cite any source at all. ChatGPT often answers
without citing anything, so read a share against it: a site in 30% of answers matters more
where every answer cites something. It is null when part of the range predates
2026-11-02, when Shruwd started counting it.
Every number here comes from the same daily rollups as the dashboard's Sources tab. Only measurement answers count; a recheck's are left out, as everywhere.
Read individual answers
curl "https://shruwd.io/api/v1/brands/northwind/answers?limit=10" \
-H "Authorization: Bearer $SHRUWD_API_KEY"
limit is 1–50, default 10. Newest first, measured runs only.
promptGroupId narrows the list to one prompt, and engine to one engine. A finding on a
prompt carries that prompt's id as its subjectRef, so this reads the answers behind it:
curl "https://shruwd.io/api/v1/brands/northwind/answers?promptGroupId=$groupId&limit=50" \
-H "Authorization: Bearer $SHRUWD_API_KEY"
Each answer carries prompt, engine, state, executedAt, whether you were
mentioned and at what rank, the surrounding context, the competitors named, and
the citations with isSelf marked: a page on your domains, or a video on one of your
YouTube channels. ownedVia says which, site or
channel.
This is evidence, not a metric. Never compute a rate from these rows — the sample is whatever the last few runs happened to be. It exists so you can read what an answer actually said, which is useful while a brand is still below the display floor.
Next steps
- Findings — the diagnosed causes behind these numbers
- Metric definitions — what each number counts

