Back to CMO.ie

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

  1. Create a key at Settings → REST API keys. Copy it immediately — it's shown exactly once.
  2. Pick the scopes you need. v1 scopes are all read-only; mint narrower keys for tools that only need a subset.
  3. 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.read

    List 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.read

    Headline 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.read

    Paginated 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.read

    Paginated 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.read

    Domain 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.read

    Ranked 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.read

    Tracked competitors with aliases, domains, colour.