REST API · v1
The data behind your dashboard, over HTTP.
Every number on your CMO.ie dashboard is reachable via this API. It's read-only in v1, scoped by Bearer token, paginated, and returns JSON. Use it for Sheets / Looker / internal dashboards, or install the MCP server to query it from Claude in natural language.
Getting started
- Create a key at Settings → REST API keys. Copy it immediately — it's shown exactly once.
- Pick the scopes you need. v1 scopes are all read-only; mint narrower keys for tools that only need a subset.
- Send an
Authorization: Bearer <key>header with every request.
curl -s \ -H 'Authorization: Bearer cmo_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \ https://cmo.ie/api/v1/projects
Pagination + errors
List endpoints accept ?page= (1-indexed) and ?page_size= (default 50, max 200). They respond with a pagination envelope:
{
"data": [ ... ],
"pagination": {
"page": 1,
"page_size": 50,
"total": 212,
"has_more": true
}
}Errors always use the error envelope with a stable code:
{ "error": { "code": "insufficient_scope", "message": "Token missing scope: gaps.read" } }Rate limit: 60 requests per minuteper key. On breach you'll get a 429 with a retry-after header.
Endpoints
- GET
/api/v1/projectsscope: visibility.readList projects owned by the authenticated key's organisation.
Query parameters
page (optional)1-indexed page number.page_size (optional)Items per page (default 50, max 200).
{ "data": [ { "id": "9c7a0e00-...", "name": "Howl.ie", "website_url": "https://howl.ie", "brand_name": "Howl", "brand_display_name": "Howl", "country_codes": ["IE"], "models": ["claude", "chatgpt", "gemini", "perplexity"], "created_at": "2026-04-10T11:12:00Z" } ], "pagination": { "page": 1, "page_size": 50, "total": 1, "has_more": false } } - GET
/api/v1/projects/{id}/metricsscope: visibility.readHeadline metrics over the window: visibility %, SoV %, avg position, sentiment distribution.
Query parameters
from (optional)ISO date (default 30d ago).to (optional)ISO date (default now).
- GET
/api/v1/projects/{id}/promptsscope: prompts.readPaginated prompt list with a rolling-30d visibility % per prompt.
Query parameters
page (optional)1-indexed page number.page_size (optional)Items per page.
- GET
/api/v1/projects/{id}/chatsscope: chats.readPaginated result rows with snippet + mention flags. One row per prompt × model × run.
Query parameters
from (optional)ISO date.to (optional)ISO date.model (optional)Restrict to chatgpt / claude / perplexity / gemini / google_aio.prompt_id (optional)Restrict to one prompt.mentioned (optional)`true` or `false` to filter by brand_mentioned.
- GET
/api/v1/projects/{id}/sourcesscope: sources.readDomain or URL aggregates with retrieval / citation rates and source-type breakdown.
Query parameters
scope (optional)`domains` (default) or `urls`.from (optional)ISO date.to (optional)ISO date.model (optional)Optional model filter.
- GET
/api/v1/projects/{id}/gapsscope: gaps.readRanked Gap Score list — sources where competitors appear and the brand doesn't.
Query parameters
scope (optional)`domains` (default) or `urls`.from (optional)ISO date.to (optional)ISO date.
- GET
/api/v1/projects/{id}/competitorsscope: competitors.readTracked competitors with aliases, domains, colour.