# Introduction (/)
Omnia tracks how AI engines (ChatGPT, Perplexity, Google AI Overviews, and others) mention, recommend, and cite brands. This documentation covers the tools available to developers for integrating Omnia data into their own workflows and systems.
***
## What's in these docs [#whats-in-these-docs]
# Async operations (/api/async-operations)
Some operations take longer to complete and run asynchronously. When you call one of these
endpoints, the response returns immediately with a `links.self` URL and a `status` field:
```json
{
"data": {
"id": "task_abc123",
"status": "pending"
},
"links": {
"self": "https://app.useomnia.com/api/v1/insights/generation/generation_abc123"
}
}
```
There are two ways to know when the task finishes: polling and webhooks.
## Polling [#polling]
Poll the `links.self` URL until `status` changes to `"completed"` (or `"failed"`).
While processing, each polling response includes a `Retry-After` header telling you how many
seconds to wait before the next request:
```bash
curl "https://app.useomnia.com/api/v1/insights/generation/generation_abc123" \\
-H "Authorization: Bearer ot_your-token-here"
```
```
HTTP/1.1 200 OK
Retry-After: 5
```
```json
{
"data": {
"id": "task_abc123",
"status": "processing"
},
"links": {
"self": "https://app.useomnia.com/api/v1/insights/generation/generation_abc123"
}
}
```
Once the task completes, the full result is included in the response and no `Retry-After`
header is sent:
```json
{
"data": {
"id": "task_abc123",
"status": "completed",
"results": [ ... ]
}
}
```
**Respect the `Retry-After` header.** Polling more frequently than the header suggests
won't speed things up and will eat into your [rate limit](/docs/api/rate-limiting) budget.
## Webhooks [#webhooks]
Instead of polling, you can register webhooks to receive a `POST` request when an async task
finishes. This is the recommended approach for production integrations because it removes the
need for polling loops and delivers results as soon as they are ready.
You can register and manage webhooks from the [API webhooks page](https://app.useomnia.com/api-webhooks).
### Supported events [#supported-events]
| Event | Fired when |
| ------------------- | ------------------------------------ |
| `insight_generated` | An insight generation task completes |
| `data_exported` | A data export task completes |
### Delivery format [#delivery-format]
When an event fires, Omnia sends a `POST` request to your registered URL with a JSON body:
```json
{
"event": "insight_generated",
"data": {
"generationId": "generation_abc123",
"status": "completed"
},
"webhookId": "wh_def456",
"timestamp": "2026-04-13T14:30:00.000Z"
}
```
| Field | Description |
| ----------- | ------------------------------------------------ |
| `event` | The event type that triggered the delivery |
| `data` | Event-specific payload with the task results |
| `webhookId` | The ID of the webhook registration that matched |
| `timestamp` | ISO 8601 timestamp of when the delivery was sent |
The request includes a `Content-Type: application/json` header.
### Best practices [#best-practices]
* **Return quickly.** Your endpoint should respond with a `2xx` status within a few seconds.
If you need to do heavy processing, accept the webhook, enqueue the work, and return immediately.
* **Be idempotent.** In rare cases (network timeouts, retries), you may receive the same event
more than once. Use the `webhookId` and `timestamp` fields to deduplicate.
* **Handle retries gracefully.** If your endpoint returns a non-`2xx` status or times out,
Omnia retries delivery up to 2 additional times with exponential backoff. After all retries
are exhausted, the delivery is dropped.
* **Monitor your endpoint.** If deliveries consistently fail, consider logging failures on your
side and verifying that your endpoint is reachable.
# Authentication (/api/authentication)
Authenticate by including your API key in every request. You can generate and manage keys from the
[API access page](https://app.useomnia.com/api-access).
Include the key in the `Authorization` header:
```bash
curl https://app.useomnia.com/api/v1/brands \\
-H "Authorization: Bearer ot_your-token-here"
```
**Treat your API keys like passwords.** Never commit them to version control or expose them in
frontend code. If a key leaks, revoke it immediately from your [API access page](https://app.useomnia.com/api-access).
# Overview (/api)
The Omnia API lets you create your scripts and automations that integrate with your Omnia workspace.
It follows [REST](https://en.wikipedia.org/wiki/REST) conventions: resources have their own URLs,
requests and responses use JSON, and HTTP verbs indicate actions (`GET` to read, `POST` to create, etc.).
# Pagination (/api/pagination)
Endpoints that return multiple items are properly paginated. You can control pagination with these
query parameters:
| Parameter | Description | Default |
| ---------- | --------------------------------- | ------- |
| `page` | Which page to fetch (starts at 1) | `1` |
| `pageSize` | Items per page (5–100) | `20` |
```bash
curl "https://app.useomnia.com/api/v1/brands?page=2&pageSize=20" \\
-H "Authorization: Bearer ot_your-token-here"
```
All paginated responses include page and navigation info alongside the results:
```json
{
"data": [
{ "id": "brand_abc", "name": "Brand A" },
{ "id": "brand_def", "name": "Brand B" }
],
"pagination": {
"page": 2,
"pageSize": 20,
"totalItems": 85
},
"links": {
"prev": "https://app.useomnia.com/api/v1/brands?page=1&pageSize=20",
"next": "https://app.useomnia.com/api/v1/brands?page=3&pageSize=20"
}
}
```
Use the URLs in `links` to move between pages rather than building them yourself.
# Rate limiting (/api/rate-limiting)
To prevent API abuse, we limit how many requests can be made by a single user. For rate limiting,
we use a token bucket mechanism: you have a bucket that refills over time, and each request takes
tokens from it. As a general rule, we use the following request costs for API endpoints:
| Operation | Cost |
| -------------------------------- | -------- |
| `GET` requests | 1 token |
| `POST`, `PUT`, `PATCH`, `DELETE` | 5 tokens |
Some resource-intensive endpoints may cost more.
## Checking your limits [#checking-your-limits]
Every response includes the following headers:
| Header | What it tells you |
| ----------------------- | -------------------------------------- |
| `X-RateLimit-Limit` | Your bucket's max capacity |
| `X-RateLimit-Remaining` | Tokens you have left |
| `X-RateLimit-Reset` | Seconds until the bucket is full again |
## When you hit the limit [#when-you-hit-the-limit]
If you run out of tokens, you'll get a `429 Too Many Requests` response with a `Retry-After` header telling you
how long to wait:
```json
{
"error": {
"code": 1007,
"description": "Too many requests"
}
}
```
To avoid hitting limits:
* Check `X-RateLimit-Remaining` to prevent exceeding rate limits.
* Use [exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff) if you exceed a rate limit and receive a `429` error response.
* Cache responses for data that doesn't change often.
# Responses (/api/responses)
We have defined a consistent structure for all API responses.
## Success [#success]
Successful responses wrap the result in a `data` field:
```json
{
"data": {
"id": "brand_abc123",
"name": "My Brand"
}
}
```
## Errors [#errors]
Failed requests return an `error` object with a numeric code (for your scripts to handle) and a
human-readable description:
```json
{
"error": {
"code": 1002,
"description": "Invalid input parameters"
}
}
```
### Error codes reference [#error-codes-reference]
| Code | Description |
| ------ | ------------------------------------------------------------ |
| `1001` | Internal error |
| `1002` | Invalid input parameters |
| `1003` | Authentication configuration error |
| `1004` | Invalid or expired API token |
| `1005` | Missing authorization header. Please provide a Bearer token. |
| `1006` | Item not found |
| `1007` | Too many requests |
| `1008` | Duplicated item |
| `1009` | Monitoring limit exceeded |
| `1010` | Insufficient credits |
| `1011` | The API key is not authorized to perform this action |
| `1012` | This endpoint has been deprecated and is no longer available |
## Streaming responses (NDJSON) [#streaming-responses-ndjson]
A handful of endpoints return potentially very large result sets and stream them back as
[newline-delimited JSON (NDJSON)](https://github.com/ndjson/ndjson-spec) instead of a single JSON
document. They respond with `Content-Type: application/x-ndjson` and a chunked body where each
line is a self-contained JSON object:
```
{"id":"row_1","...":"..."}
{"id":"row_2","...":"..."}
{"id":"row_3","...":"..."}
```
There is no pagination on streaming endpoints. The server keeps the HTTP connection open and writes
rows as they become available, so expect a long-lived response when the filter matches a lot of
data.
Parse the body line by line as it arrives rather than buffering the whole response, that keeps
memory use flat even on very large exports. For example:
```typescript
const response = await fetch(url, { headers: { Authorization: `Bearer ${API_KEY}` } });
const reader = response.body!.pipeThrough(new TextDecoderStream()).getReader();
let buffer = "";
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += value;
const lines = buffer.split("\n");
buffer = lines.pop() ?? "";
for (const line of lines) {
if (line) {
const row = JSON.parse(line);
// handle row
}
}
}
```
If you can't keep an HTTP connection open for the duration of the request (for example for
scheduled jobs, very large date ranges or background pipelines), streaming endpoints usually have
an asynchronous `POST` counterpart that writes the same data to cloud storage (typically as a CSV
file, ready to load into BI tools) and returns a presigned download URL. See
[Async operations](/docs/api/async-operations) for how to poll those.
# Versioning (/api/versioning)
The API version must be included in the URL path (`/api/v1/...`). When we make breaking changes, we'll release
a new version and give you time to migrate before deprecating the old one.
# Connecting to Omnia MCP (/mcp/connecting)
The Omnia MCP server is hosted at:
```
https://app.useomnia.com/mcp
```
Authentication is handled via OAuth, you'll be prompted to log in to your Omnia account the first time you connect.
A single connection gives you both [data and discovery tools](/mcp) — no separate connector required.
***
## Claude.ai [#claudeai]
Go to **Settings** → **Connectors**
Click **Add Connector** and enter the server URL: `https://app.useomnia.com/mcp`
Complete the OAuth flow to connect your Omnia account
Once connected, Omnia tools will be available in any conversation
***
## Claude Code [#claude-code]
Run this command in your terminal:
```bash
claude mcp add --transport http omnia https://app.useomnia.com/mcp
```
Then authenticate:
```bash
claude /mcp
```
Select **omnia** from the list, choose **Authenticate**, and complete the OAuth flow in your browser. You'll see a confirmation that the connection was successful.
***
## Cursor [#cursor]
Open
**Cursor Settings**
→
**MCP**
→
**Add new global MCP server**
Paste the following configuration:
```json
{
"mcpServers": {
"omnia": {
"url": "https://app.useomnia.com/mcp"
}
}
}
```
Save and restart Cursor
When you use an Omnia tool for the first time, complete the OAuth flow to connect your account
**Want to share the config with your team?** Add a `.cursor/mcp.json` file to your project root with the same configuration above.
***
## VS Code [#vs-code]
Open the Command Palette (`Cmd+Shift+P` / `Ctrl+Shift+P`) and run **MCP: Open User Configuration**
Add the following to your `mcp.json` file:
```json
{
"servers": {
"omnia": {
"type": "http",
"url": "https://app.useomnia.com/mcp"
}
}
}
```
Run **MCP: List Servers** from the Command Palette, start the Omnia server, and complete the OAuth flow when prompted
***
## Windsurf [#windsurf]
Open Windsurf Settings and search for **MCP**
Click
**View raw config**
to open
`mcp_config.json`
Add the Omnia server:
```json
{
"mcpServers": {
"omnia": {
"serverUrl": "https://app.useomnia.com/mcp"
}
}
}
```
Save and restart Windsurf. Complete the OAuth flow when prompted.
***
## Microsoft Copilot (VS Code) [#microsoft-copilot-vs-code]
Open the Command Palette (`Cmd+Shift+P` / `Ctrl+Shift+P`) and run **MCP: Open User Configuration**
Add the following to your `mcp.json` file:
```json
{
"servers": {
"omnia": {
"type": "http",
"url": "https://app.useomnia.com/mcp"
}
}
}
```
Run **MCP: List Servers** from the Command Palette, start the Omnia server, and complete the OAuth flow when prompted
***
## ChatGPT [#chatgpt]
Go to [chatgpt.com/#settings/Connectors](https://chatgpt.com/#settings/Connectors)
Click **Add Connector** and enter the server URL: `https://app.useomnia.com/mcp`
Select **OAuth** as the connection mechanism and complete the login flow
***
## Other MCP clients [#other-mcp-clients]
If your client isn't listed above but supports MCP, use the following:
| Setting | Value |
| -------------- | ------------------------------ |
| **Server URL** | `https://app.useomnia.com/mcp` |
| **Transport** | Streamable HTTP |
| **Auth** | OAuth |
***
## Troubleshooting [#troubleshooting]
**My client doesn't support remote MCP servers**
If your client only supports local stdio servers, you can bridge the connection using `mcp-remote`:
```json
{
"mcpServers": {
"omnia": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://app.useomnia.com/mcp"]
}
}
}
```
**Authentication issues**
* Make sure you complete the OAuth flow all the way through
* If you're stuck, try disconnecting and reconnecting via your client's MCP settings
* You must have an active Omnia account to authenticate. [Sign up here](https://app.useomnia.com/sign-up)
**Tools aren't appearing**
* Restart your AI client after adding the server
* Confirm the server URL is exactly `https://app.useomnia.com/mcp` (no trailing slash issues)
* Run the server list command in your client to verify the connection status
# Overview (/mcp)
Connect your AI assistant to your brand's AI visibility data.
## What is the Omnia MCP? [#what-is-the-omnia-mcp]
The Omnia MCP (Model Context Protocol) server lets AI assistants like Claude, ChatGPT, Cursor, and others read and reason over your Omnia data in real time. With MCP, your AI assistant connects directly to Omnia and pulls the context it needs automatically.
[MCP](https://modelcontextprotocol.io/introduction) is an open standard for connecting AI applications to external systems. Think of it like a universal adapter: the Omnia MCP server implements that standard so any compatible AI client can securely access your brand's AI visibility data without ever leaving the conversation.
***
## Tool families [#tool-families]
A single connection to the Omnia MCP exposes two complementary families of tools:
* **Data tools** — Read your brand's AI visibility data: share of voice, citations, sentiment, topics, prompts, insights, trends. These mirror the [public API](/api/overview) endpoints, so anything you can query through the API you can also query through MCP. Use these when you want your AI assistant to *answer questions* about your data.
* **Discovery tools** — Search the Omnia API documentation itself (endpoint specs, schemas, conceptual guides). LLMs are good at writing code against APIs but they hallucinate parameter names, paths, and response shapes, especially for APIs they haven't seen in training. Discovery tools let an assistant query the live spec at runtime and ground its responses in the real schema. Use these when you want your AI assistant to *write code* against the Omnia API.
Both families share the same connection and the same auth. Connecting once gives you everything.
***
## What can you do with the Omnia MCP? [#what-can-you-do-with-the-omnia-mcp]
The Omnia MCP exposes your brand's AI visibility data as a set of tools that AI assistants can call during a conversation. Here's what's possible:
### Monitor your AI visibility [#monitor-your-ai-visibility]
Ask your AI assistant to pull your current visibility scores, share of voice, and citation data across AI engines like ChatGPT, Perplexity, Google AI Overviews, and Google AI Mode. You can filter by topic, date range, or competitor.
**Example prompts:**
* *"How has our share of voice trended over the last 30 days?"*
* *"Which topics are driving the most AI citations for our brand this week?"*
* *"How do we compare to our top three competitors in Google AI Overviews?"*
### Analyze citations and sources [#analyze-citations-and-sources]
Dig into which sources AI engines are citing when they talk about your category, and discover where your brand has gaps.
**Example prompts:**
* *"What third-party sites are being cited most often in our tracked prompts?"*
* *"Show me which of our owned pages are getting picked up as AI citations."*
* *"Which citation sources are our competitors appearing in that we're not?"*
### Explore topics and trends [#explore-topics-and-trends]
Browse your tracked topics, discover emerging questions your customers are asking AI, and identify new opportunities to show up in AI answers.
**Example prompts:**
* *"What are our top-performing branded topics right now?"*
* *"Are there any new high-volume topics we should be tracking?"*
* *"What's the search volume trend for our core non-branded topics?"*
### Get AI-powered analysis [#get-ai-powered-analysis]
Because the Omnia MCP connects your data to an AI assistant's reasoning, you can go beyond dashboards. Ask for interpretations, comparisons, and recommendations, all grounded in real Omnia data.
**Example prompts:**
* *"Summarize where we're winning and where we're losing in AI search this month."*
* *"Based on our citation data, what type of content should we prioritize next?"*
* *"Draft a Slack update for my team on our AI visibility progress this quarter."*
# Get AI answer (/api/ai-answer/get-ai-answer)
Retrieve an AI answer by its id
# List AI answers (/api/ai-answer/list-ai-answers)
Retrieve a paginated list of AI answers for the given prompt. Pagination in this endpoint is not 100% accurate and pages might have fewer items than initially requested. For the same reason, the real total number of items can be lower than the one reported in responses
# Create brand (/api/brand/create-brand)
Create a new brand with the provided details. The brand will be associated with the authenticated organization.
# Delete brand (/api/brand/delete-brand)
Delete a brand and all its associated data including topics and prompts. This action cannot be undone.
# Get brand (/api/brand/get-brand)
Retrieve a brand by its id
# List brands (/api/brand/list-brands)
Retrieve a paginated list of brands. Optionally filter by domain or name using partial, case-insensitive matching.
# Update brand (/api/brand/update-brand)
Update an existing brand. Only the fields provided in the request body will be updated.
# Export performance data (/api/guides/export-performance-data)
Export daily performance data from the Omnia API into flat JSON files that you can load into
Looker Studio, BigQuery, Tableau, or any BI tool.
### How data is organized [#how-data-is-organized]
Omnia tracks how AI engines mention and recommend brands.
The data is organized in a three-level hierarchy:
* **Brand** is the top level. It represents your company or product (e.g. "Acme Corp" / acme.com).
Brand-level aggregates show your overall performance across all topics.
* **Topics** sit under a brand. Each topic is a subject area you want to monitor
(e.g. "project management software", "best CRM tools"). Topic-level aggregates show how brands
perform within that specific subject.
* **Prompts** sit under a topic. Each prompt is a specific question that gets asked to AI engines
(e.g. "What is the best project management tool for remote teams?"). Prompt-level aggregates
show brand performance for that exact question.
At each level, four metrics are available:
| Metric | What it measures | Endpoint suffix |
| ------------------ | ------------------------------------------------------- | ---------------------------- |
| **Share of voice** | How often a brand is mentioned relative to competitors | `/share-of-voice/aggregates` |
| **Visibility** | How prominently a brand appears in AI responses | `/visibility/aggregates` |
| **Citations** | Which sources AI engines cite when mentioning brands | `/citations/aggregates` |
| **Sentiment** | How positively or negatively AI engines describe brands | `/sentiment/aggregates` |
That gives you 3 levels x 4 metrics = 12 aggregate endpoints total. Share of voice and visibility
return the same shape (brand name, mention count, rank). Citations return a different shape
(URL, title, citation count). Sentiment returns a flat list of brand-feature pairs with endorsedMentions/underminedMentions/neutralMentions counts. The code below uses share of voice as the example.
### Prerequisites [#prerequisites]
* An Omnia API key (generate one from your [API access page](https://app.useomnia.com/api-access))
* Node.js 18+ (uses the built-in `fetch` API)
* [tsx](https://github.com/privatenumber/tsx) to run TypeScript directly: `npm install -g tsx`
> Store your API key in an environment variable. Do not hardcode it or commit it to version control.
```bash
export OMNIA_API_KEY="your-api-key-here"
```
### Step 1: API client [#step-1-api-client]
Create a file called `export-sov.ts`. Start with two utility functions: `apiFetch` handles
authentication and retries on `429`, and `buildUrl` constructs an endpoint URL with query params:
```typescript
import fs from "node:fs";
const API_BASE = "https://app.useomnia.com/api/v1";
const API_KEY = process.env.OMNIA_API_KEY;
const MAX_RETRIES = 3;
if (!API_KEY) {
throw new Error("Set the OMNIA_API_KEY environment variable before running this script.");
}
function buildUrl(endpoint: string, params: Record = {}): URL {
const url = new URL(API_BASE + endpoint);
for (const [key, value] of Object.entries(params)) {
url.searchParams.set(key, value);
}
return url;
}
async function apiFetch(url: string | URL, retries = 0): Promise {
const response = await fetch(url, {
headers: { Authorization: `Bearer \${API_KEY}` },
});
if (response.status === 429) {
if (retries >= MAX_RETRIES) {
throw new Error("Rate limit exceeded after " + MAX_RETRIES + " retries");
}
const retryAfter = parseInt(response.headers.get("Retry-After") ?? "5", 10);
await new Promise((resolve) => setTimeout(resolve, retryAfter \* 1000));
return apiFetch(url, retries + 1);
}
if (!response.ok) {
let description = response.statusText;
try {
const body = await response.json();
description = body.error.description;
} catch {}
throw new Error(`API \${response.status}: \${description}`);
}
return response.json();
}
```
### Step 2: Pagination helper [#step-2-pagination-helper]
Aggregate endpoints are paginated. Every paginated response includes a `links` object with
a `next` URL. Follow it until there are no more pages:
```typescript
async function fetchAllPages(
endpoint: string,
dataKey: string,
params: Record = {}
) {
const allItems: Record[] = [];
let nextUrl: string | URL | undefined = buildUrl(endpoint, { ...params, pageSize: "100" });
while (nextUrl) {
const page = await apiFetch(nextUrl);
allItems.push(...(page.data[dataKey] ?? []));
nextUrl = page.links?.next;
}
return allItems;
}
```
### Step 3: Find your brand and export [#step-3-find-your-brand-and-export]
The rest of the script goes inside an `async main()` function. Start by calling `GET /brands`
to find the brand you want to export:
```typescript
async function main() {
const brands = await fetchAllPages("/brands", "brands");
const brand = brands.find((b) => b.name === "Your Brand Name");
if (!brand) {
throw new Error("Brand not found. Available: " + brands.map((b) => b.name).join(", "));
}
const brandId = brand.id as string;
console.log(`Exporting share of voice for \${brand.name} (\${brandId})`);
```
Replace `"Your Brand Name"` with the name of your brand as it appears in Omnia.
### Step 4: Loop over a date range and write to a file [#step-4-loop-over-a-date-range-and-write-to-a-file]
Aggregate endpoints accept `startDate` and `endDate`. To get daily granularity, set both to the
same date and loop one day at a time:
```typescript
const startDate = new Date("2025-06-01T00:00:00Z");
const endDate = new Date("2025-06-07T00:00:00Z");
const rows: Record[] = [];
for (const d = new Date(startDate); d <= endDate; d.setUTCDate(d.getUTCDate() + 1)) {
const date = d.toISOString().slice(0, 10);
console.log(`Fetching \${date}...`);
const aggregates = await fetchAllPages(
`/brands/\${brandId}/share-of-voice/aggregates`,
"aggregates",
{ startDate: date, endDate: date }
);
for (const item of aggregates) {
rows.push({ date, ...item });
}
}
fs.writeFileSync("share-of-voice.json", JSON.stringify(rows, null, 2));
console.log(`Wrote \${rows.length} rows to share-of-voice.json`);
}
main();
```
### Running the script [#running-the-script]
All the code blocks above go into the same file (`export-sov.ts`), in order. Then run it:
```bash
tsx export-sov.ts
```
### Next steps [#next-steps]
The example above covers brand-level share of voice. To export at the topic or prompt level,
replace the endpoint path:
* `/brands/{id}/share-of-voice/aggregates` (brand level)
* `/topics/{id}/share-of-voice/aggregates` (topic level)
* `/prompts/{id}/share-of-voice/aggregates` (prompt level)
Swap `share-of-voice` for `visibility`, `citations`, or `sentiment` to get the other metrics. Note that
citations and sentiment return different response shapes (see the endpoint reference above for details).
To discover all topics and prompts for a brand, add this inside your `main()` function:
```typescript
const topics = await fetchAllPages(`/brands/\${brandId}/topics`, "topics");
for (const topic of topics) {
const prompts = await fetchAllPages(`/topics/\${topic.id}/prompts`, "prompts");
console.log(`\${topic.name}: \${prompts.length} prompts`);
}
```
From there, loop over each topic and prompt the same way the brand example loops over dates.
For a production-ready version that exports all metrics at all levels with concurrency, rate limit
handling, and error recovery, see the
[full export script](https://github.com/useomnia/omnia-examples/tree/main/examples/export-data)
on GitHub.
# Get insight (/api/insight/get-insight)
Retrieve a single insight by its id
# List insights (/api/insight/list-insights)
Retrieve a paginated list of insights
# Update insight (/api/insight/update-insight)
Changes the status of an insight
# Create prompt (/api/prompt/create-prompt)
Creates a new non-monitored prompt that depends on the selected topic.
**Deprecated**: Returns a `410 Gone` error once Topics are disabled for your account due to deprecation — if so, use `POST /api/v1/brands/{brand}/prompts` instead. It does not require a topic ID.
# Create prompts (/api/prompt/create-prompts)
Bulk-create prompts for a brand. Each prompt is grouped under the named topic, or an auto-managed topic for the given location if no topic name is provided. Returns a report detailing created prompts, skipped duplicates, and row-level errors.
# Delete prompt (/api/prompt/delete-prompt)
Delete a prompt. This action cannot be undone.
# Get prompt (/api/prompt/get-prompt)
Retrieve a prompt by its id
# List brand prompts (/api/prompt/list-brand-prompts)
Retrieve a paginated list of all prompts for a brand across all topics. Unlike the topic-scoped prompts endpoint, this response omits the topicId field.
# List prompts (/api/prompt/list-prompts)
Retrieve a paginated list of prompts.
**Deprecated**: Returns a `410 Gone` error once Topics are disabled for your account due to deprecation — if so, use `GET /api/v1/brands/{brand}/prompts` instead. It does not require a topic ID and supports `type`, `countries`, and `tags` filters to narrow the results.
# Toggle prompt monitoring (/api/prompt/toggle-prompt-monitoring)
Changes the monitoring status for a prompt.
Calling this endpoint uses **25 tokens** from your rate limit bucket. Learn more about rate limiting [here](/api/rate-limiting).
# Update prompt (/api/prompt/update-prompt)
Update an existing prompt. Only the fields provided in the request body will be updated.
# Bulk import prompts (/api/topic/bulk-import-prompts)
Bulk-import prompts from parsed CSV rows. Rows missing a topic are grouped via automatic topic discovery. Returns a report detailing created topics, skipped duplicates, and row-level errors.
**Deprecated**: Returns a `410 Gone` error once Topics are disabled for your account due to deprecation — if so, use `POST /api/v1/brands/{brand}/prompts` instead.
# Create topic (/api/topic/create-topic)
Creates a new non-monitored topic with associated prompts. If a topic with the same name and location already exists for this brand, the prompts will be added to the existing topic instead.
**Deprecated**: Returns a `410 Gone` error once Topics are disabled for your account due to deprecation — if so, use `POST /api/v1/brands/{brand}/prompts` instead.
# Delete topic (/api/topic/delete-topic)
Delete a topic and all its associated data including prompts. This action cannot be undone.
**Deprecated**: Returns a `410 Gone` error once Topics are disabled for your account due to deprecation — if so, use `DELETE /api/v1/prompts/{prompt}` to delete individual prompts instead.
# Get topic (/api/topic/get-topic)
Retrieve a topic by its id.
**Deprecated**: Returns a `410 Gone` error once Topics are disabled for your account due to deprecation — if so, use `GET /api/v1/brands/{brand}/prompts` instead. It returns all prompts for the brand and supports `type`, `countries`, and `tags` filters to narrow the results.
# List topics (/api/topic/list-topics)
Retrieve a paginated list of topics.
**Deprecated**: Returns a `410 Gone` error once Topics are disabled for your account due to deprecation — if so, use `GET /api/v1/brands/{brand}/prompts` instead. It returns all prompts for the brand and supports `type`, `countries`, and `tags` filters to narrow the results.
# Toggle topic monitoring (/api/topic/toggle-topic-monitoring)
Changes the monitoring status for a topic and all its prompts.
Calling this endpoint uses **25 tokens** from your rate limit bucket. Learn more about rate limiting [here](/api/rate-limiting).
**Deprecated**: Returns a `410 Gone` error once Topics are disabled for your account due to deprecation — if so, use `PUT /api/v1/prompts/{prompt}/toggle-monitoring` to toggle monitoring for individual prompts instead.
# Update topic (/api/topic/update-topic)
Update an existing topic. Only the fields provided in the request body will be updated.
**Deprecated**: Returns a `410 Gone` error once Topics are disabled for your account due to deprecation — if so, use `PATCH /api/v1/prompts/{prompt}` to update individual prompts instead.
# Accept trend (/api/trend/accept-trend)
Accepts a trend and starts monitoring all the selected prompt ids. This converts the trend into an active topic that can be managed via the topics endpoints.
Calling this endpoint uses **25 tokens** from your rate limit bucket. Learn more about rate limiting [here](/api/rate-limiting).
**Deprecated**: Returns a `410 Gone` error once Topics are disabled for your account due to deprecation — if so, use `POST /api/v1/brands/{brand}/prompts` to create and start monitoring prompts directly instead.
# Import trends (/api/trend/import-trends)
Creates new trends from a list of imported keywords. This method is asynchronous and it requires querying for new trends to see when the import finishes.
Calling this endpoint uses **25 tokens** from your rate limit bucket. Learn more about rate limiting [here](/api/rate-limiting).
**Deprecated**: Returns a `410 Gone` error once Topics are disabled for your account due to deprecation — if so, use `POST /api/v1/brands/{brand}/prompts` to create prompts directly instead.
# List trends (/api/trend/list-trends)
Retrieve a paginated list of trends.
**Deprecated**: Returns a `410 Gone` error once Topics are disabled for your account due to deprecation. No direct replacement — use `GET /api/v1/brands/{brand}/prompts` to list all prompts for a brand instead. It supports `type`, `countries`, and `tags` filters to narrow the results.
# Create view (/api/view/create-view)
Create a view for a brand. The view name must be unique per brand (case-insensitive). Omit `filters` (or pass `{}`) for a view matching all of the brand's prompts.
A view is a named saved filter over a brand's prompts (any combination of prompt type, countries, tags, and status). The prompt set is resolved from the filters at query time, so a view always reflects the brand's current prompts. Views are the recommended grouping primitive for analytics, replacing topic-scoped endpoints.
# Delete view (/api/view/delete-view)
Delete a view. Only the saved filter is removed — the prompts it groups are not affected.
# Get view (/api/view/get-view)
Retrieve a view by its id.
A view is a named saved filter over a brand's prompts (any combination of prompt type, countries, tags, and status). The prompt set is resolved from the filters at query time, so a view always reflects the brand's current prompts. Views are the recommended grouping primitive for analytics, replacing topic-scoped endpoints.
# List view prompts (/api/view/list-view-prompts)
Retrieve the paginated list of prompts the view currently groups, with summary performance metrics per prompt. The prompt set is resolved from the view's saved filters at query time.
# List views (/api/view/list-views)
Retrieve a paginated list of the brand's views.
A view is a named saved filter over a brand's prompts (any combination of prompt type, countries, tags, and status). The prompt set is resolved from the filters at query time, so a view always reflects the brand's current prompts. Views are the recommended grouping primitive for analytics, replacing topic-scoped endpoints.
# Update view (/api/view/update-view)
Update a view's name and/or filters. `filters` replaces the entire filters object — include every criterion the view should keep.
# Create metrics export task (/api/brand/performance/create-metrics-export-task)
Schedule an asynchronous export task that writes the resulting **ZIP archive** to cloud storage and exposes it as a presigned download URL. Use this variant when you don't want to keep an HTTP connection open for the duration of the export (for example, for scheduled jobs, very large date ranges, or background pipelines) or when you need files ready to load into BI tools like Looker Studio, BigQuery or Tableau.
The request body is empty; all parameters are passed as query parameters. The date range and the engine filter behave exactly as on the streaming `GET` variant. Where the `GET` takes a single `entity` and `metric`, this endpoint takes comma-separated `entities` and `metrics` so one task can cover several granularities and metrics at once, plus a `format` selecting how each file is serialized.
The archive holds **one file per requested `entities` × `metrics` combination**, named `_.` — for example `prompt_share-of-voice.ndjson`. If any individual query fails, the archive also contains an `errors.json` listing the combinations that could not be produced, and the task reports `status: "failed"`.
`format` selects how each file is serialized:
* `ndjson` (default) — one JSON object per line, matching the `ExportRow` shape documented on the streaming endpoint. Fields that have no value are omitted from the line.
* `csv` — a header row followed by one row per record. The columns are the `ExportRow` fields for the requested entity and metric, sorted alphabetically and identical on every row; a field with no value is written as an empty cell. Columns are fixed by the schema, so the header is present even when the export contains no rows.
The response is `202 Accepted` with `links.self` pointing at the polling endpoint (`GET /api/v1/exports/{export}`). Poll that URL until the `status` becomes `completed` (or `failed`). When the export completes, the polling response includes a `resultsUrl`, a presigned URL that lets you download the archive. Presigned URLs expire 7 days after generation.
This is a long-running operation. The response includes a `links.self` URL you can use to keep track of the status of the operation. Learn more about async endpoints [here](/api/async-operations).
Calling this endpoint uses **25 tokens** from your rate limit bucket. Learn more about rate limiting [here](/api/rate-limiting).
# Get citations aggregates (/api/brand/performance/get-citations-aggregates)
Retrieve a paginated list of citations aggregates
# Get citations time series (/api/brand/performance/get-citations-time-series)
Retrieve time series data showing citations evolution over time for a brand and its competitors. Returns data points for each brand within the specified date range.
# Get metrics export (/api/brand/performance/get-metrics-export)
Retrieve the status of a metrics export task. When the export has finished successfully, the response includes a presigned URL to download the results as a ZIP archive, holding one file per exported entity and metric in the `format` requested when the task was created. While the task is in progress, the response includes a `Retry-After` header suggesting when to poll again.
# Get sentiment aggregates (/api/brand/performance/get-sentiment-aggregates)
Retrieve a flat, paginated list of sentiment aggregates. Each row represents a unique brand-feature pair with counts of endorsed, undermined, and neutral feature mentions.
# Get sentiment time series (/api/brand/performance/get-sentiment-time-series)
Retrieve daily sentiment distribution over time for the owned brand only (competitors are not included). Returns counts of endorsed, undermined, and neutral feature mentions per day within the specified date range.
# Get share of voice aggregates (/api/brand/performance/get-share-of-voice-aggregates)
Retrieve a paginated list of share of voice aggregates
# Get share of voice time series (/api/brand/performance/get-share-of-voice-time-series)
Retrieve time series data showing share of voice evolution over time for a brand and its competitors. Returns data points for each brand within the specified date range.
# Get visibility aggregates (/api/brand/performance/get-visibility-aggregates)
Retrieve a paginated list of visibility aggregates
# Get visibility time series (/api/brand/performance/get-visibility-time-series)
Retrieve time series data showing visibility evolution over time for a brand and its competitors. Returns data points for each brand within the specified date range.
# Stream metrics export (/api/brand/performance/stream-metrics-export)
Stream every row of the selected metric for the given brand. Changing `entity` cascades entity information onto each row: brand-level exports carry only brand fields, topic-level exports also include the topic, and prompt-level exports also include the prompt.
If you can't keep an HTTP connection open for the duration of the export (for example, for scheduled jobs, very large date ranges, or background pipelines), use `POST /api/v1/brands/{brand}/export` instead and poll for completion.
This endpoint streams its response as **newline-delimited JSON (NDJSON)**. Each line of the response body is a self-contained JSON object matching the schema below. There is no pagination, the full filtered dataset is returned in one long-lived HTTP connection. Learn more about streaming responses [here](/api/responses#streaming-responses-ndjson).
Calling this endpoint uses **25 tokens** from your rate limit bucket. Learn more about rate limiting [here](/api/rate-limiting).
# Generate insights (/api/insight/generation/generate-insights)
Starts a new insights generation task.
This is a long-running operation. The response includes a `links.self` URL you can use to keep track of the status of the operation. Learn more about async endpoints [here](/api/async-operations).
Calling this endpoint uses **25 tokens** from your rate limit bucket. Learn more about rate limiting [here](/api/rate-limiting).
# Get insight generation (/api/insight/generation/get-insight-generation)
Retrieve an insight generation by its id. This method is used to poll ongoing generations.
# Get citations aggregates (/api/prompt/performance/get-citations-aggregates)
Retrieve a paginated list of prompt citations aggregates
# Get citations time series (/api/prompt/performance/get-citations-time-series)
Retrieve prompt time series data showing citations evolution over time for a brand and its competitors. Returns data points for each brand within the specified date range.
# Get sentiment aggregates (/api/prompt/performance/get-sentiment-aggregates)
Retrieve a flat, paginated list of sentiment aggregates for a prompt. Each row represents a unique brand-feature pair with counts of endorsed, undermined, and neutral feature mentions.
# Get sentiment time series (/api/prompt/performance/get-sentiment-time-series)
Retrieve daily sentiment distribution over time for the owned brand within a prompt (competitors are not included). Returns counts of endorsed, undermined, and neutral feature mentions per day within the specified date range.
# Get share of voice aggregates (/api/prompt/performance/get-share-of-voice-aggregates)
Retrieve a paginated list of prompt share of voice aggregates
# Get share of voice time series (/api/prompt/performance/get-share-of-voice-time-series)
Retrieve prompt time series data showing share of voice evolution over time for a brand and its competitors. Returns data points for each brand within the specified date range.
# Get visibility aggregates (/api/prompt/performance/get-visibility-aggregates)
Retrieve a paginated list of prompt visibility aggregates
# Get visibility time series (/api/prompt/performance/get-visibility-time-series)
Retrieve prompt time series data showing visibility evolution over time for a brand and its competitors. Returns data points for each brand within the specified date range.
# Get citations aggregates (/api/topic/performance/get-citations-aggregates)
Retrieve a paginated list of topic citations aggregates.
**Deprecated**: Returns a `410 Gone` error once Topics are disabled for your account due to deprecation — if so, use `GET /api/v1/brands/{brand}/citations/aggregates` with the `promptType` query parameter for a Branded/Non-Branded split, or create a View (`POST /api/v1/brands/{brand}/views`) to replicate this topic's specific prompt grouping and query `GET /api/v1/views/{view}/citations/aggregates` instead.
# Get citations time series (/api/topic/performance/get-citations-time-series)
Retrieve topic time series data showing citations evolution over time for a brand and its competitors. Returns data points for each brand within the specified date range.
**Deprecated**: Returns a `410 Gone` error once Topics are disabled for your account due to deprecation — if so, use `GET /api/v1/brands/{brand}/citations/timeseries` with the `promptType` query parameter for a Branded/Non-Branded split, or create a View (`POST /api/v1/brands/{brand}/views`) to replicate this topic's specific prompt grouping and query `GET /api/v1/views/{view}/citations/timeseries` instead.
# Get sentiment aggregates (/api/topic/performance/get-sentiment-aggregates)
Retrieve a flat, paginated list of sentiment aggregates for a topic. Each row represents a unique brand-feature pair with counts of endorsed, undermined, and neutral feature mentions.
**Deprecated**: Returns a `410 Gone` error once Topics are disabled for your account due to deprecation — if so, use `GET /api/v1/brands/{brand}/sentiment/aggregates` with the `promptType` query parameter for a Branded/Non-Branded split, or create a View (`POST /api/v1/brands/{brand}/views`) to replicate this topic's specific prompt grouping and query `GET /api/v1/views/{view}/sentiment/aggregates` instead.
# Get sentiment time series (/api/topic/performance/get-sentiment-time-series)
Retrieve daily sentiment distribution over time for the owned brand within a topic (competitors are not included). Returns counts of endorsed, undermined, and neutral feature mentions per day within the specified date range.
**Deprecated**: Returns a `410 Gone` error once Topics are disabled for your account due to deprecation — if so, use `GET /api/v1/brands/{brand}/sentiment/timeseries` with the `promptType` query parameter for a Branded/Non-Branded split, or create a View (`POST /api/v1/brands/{brand}/views`) to replicate this topic's specific prompt grouping and query `GET /api/v1/views/{view}/sentiment/timeseries` instead.
# Get share of voice aggregates (/api/topic/performance/get-share-of-voice-aggregates)
Retrieve a paginated list of topic share of voice aggregates.
**Deprecated**: Returns a `410 Gone` error once Topics are disabled for your account due to deprecation — if so, use `GET /api/v1/brands/{brand}/share-of-voice/aggregates` with the `promptType` query parameter for a Branded/Non-Branded split, or create a View (`POST /api/v1/brands/{brand}/views`) to replicate this topic's specific prompt grouping and query `GET /api/v1/views/{view}/share-of-voice/aggregates` instead.
# Get share of voice time series (/api/topic/performance/get-share-of-voice-time-series)
Retrieve topic time series data showing share of voice evolution over time for a brand and its competitors. Returns data points for each brand within the specified date range.
**Deprecated**: Returns a `410 Gone` error once Topics are disabled for your account due to deprecation — if so, use `GET /api/v1/brands/{brand}/share-of-voice/timeseries` with the `promptType` query parameter for a Branded/Non-Branded split, or create a View (`POST /api/v1/brands/{brand}/views`) to replicate this topic's specific prompt grouping and query `GET /api/v1/views/{view}/share-of-voice/timeseries` instead.
# Get visibility aggregates (/api/topic/performance/get-visibility-aggregates)
Retrieve a paginated list of topic visibility aggregates.
**Deprecated**: Returns a `410 Gone` error once Topics are disabled for your account due to deprecation — if so, use `GET /api/v1/brands/{brand}/visibility/aggregates` with the `promptType` query parameter for a Branded/Non-Branded split, or create a View (`POST /api/v1/brands/{brand}/views`) to replicate this topic's specific prompt grouping and query `GET /api/v1/views/{view}/visibility/aggregates` instead.
# Get visibility time series (/api/topic/performance/get-visibility-time-series)
Retrieve topic time series data showing visibility evolution over time for a brand and its competitors. Returns data points for each brand within the specified date range.
**Deprecated**: Returns a `410 Gone` error once Topics are disabled for your account due to deprecation — if so, use `GET /api/v1/brands/{brand}/visibility/timeseries` with the `promptType` query parameter for a Branded/Non-Branded split, or create a View (`POST /api/v1/brands/{brand}/views`) to replicate this topic's specific prompt grouping and query `GET /api/v1/views/{view}/visibility/timeseries` instead.
# Get citation aggregates (/api/view/performance/get-citation-aggregates)
Retrieve a paginated list of citation aggregates over the prompts the view groups. The prompt set comes from the view's saved filters; the query parameters only control dates, engine, sorting, filtering by source, and pagination.
# Get citations time series (/api/view/performance/get-citations-time-series)
Retrieve citations time series over the prompts the view groups. The prompt set comes from the view's saved filters; the query parameters only control dates and engine.
# Get sentiment aggregates (/api/view/performance/get-sentiment-aggregates)
Retrieve a paginated list of sentiment aggregates (one row per brand-feature pair) over the prompts the view groups. The prompt set comes from the view's saved filters; the query parameters only control dates, engine, sorting, filtering by brand or feature, and pagination.
# Get sentiment time series (/api/view/performance/get-sentiment-time-series)
Retrieve daily sentiment feature mention distribution over the prompts the view groups. The prompt set comes from the view's saved filters; the query parameters only control dates and engine.
# Get share of voice aggregates (/api/view/performance/get-share-of-voice-aggregates)
Retrieve a paginated list of share of voice aggregates over the prompts the view groups. The prompt set comes from the view's saved filters; the query parameters only control dates, engine, sorting, and pagination.
# Get share of voice time series (/api/view/performance/get-share-of-voice-time-series)
Retrieve share of voice time series over the prompts the view groups. The prompt set comes from the view's saved filters; the query parameters only control dates and engine.
# Get visibility aggregates (/api/view/performance/get-visibility-aggregates)
Retrieve a paginated list of visibility aggregates over the prompts the view groups. The prompt set comes from the view's saved filters; the query parameters only control dates, engine, sorting, and pagination.
# Get visibility time series (/api/view/performance/get-visibility-time-series)
Retrieve visibility time series over the prompts the view groups. The prompt set comes from the view's saved filters; the query parameters only control dates and engine.