Reference

Data Export API

Pull your CiteMetrix data - citations, ModelScore trends, competitors, hallucinations, and more - into Looker Studio, Tableau, or any BI tool via a read-only REST API.

A read-only REST interface for pulling your account’s AI-visibility data — citations, ModelScore™ trends, per-platform breakdowns, competitors, hallucinations, and annotations — into Looker Studio, Tableau, a data warehouse, or any tool that speaks HTTP. Every response is available as JSON or CSV.

Before you begin

  • Plan requirement. API access is included on the Professional and Enterprise plans (Business) and the Agency Large and Agency Global plans (Agency). If the Data Export API screen doesn’t appear in your dashboard, your current plan doesn’t include it.
  • Read-only. The API never changes your data. Every key is scoped to a single CiteMetrix account and can see only that account’s domains.
  • You’ll need an API key (created in the dashboard) and any HTTP client — Looker Studio, Tableau, Python, curl, or a browser.

Create an API key

  1. Sign in to your CiteMetrix dashboard.
  2. Open the account menu (top right) and choose Data Export API.
  3. Enter a label describing where the key will be used (e.g. “Looker Studio”) and click Create key.
  4. Copy the key immediately — for security it is shown only once. It looks like cmx_live_…. Store it in your secrets manager.
  5. To retire a key, click Revoke. Revocation takes effect immediately; any integration using that key stops working.
One key per integration. Create a separate key for each tool and label it clearly, so you can revoke one integration without disrupting the others.

Authentication

Send your key as a Bearer token in the Authorization header on every request:

Authorization: Bearer cmx_live_your_key_here

A missing, malformed, or revoked token returns 401 Unauthorized. A valid key on a plan without API access returns 403 Forbidden.

Base URL

https://citemetrix.com/wp-json/citemetrix/v1/export/

All endpoints below are relative to this base. Every request is GET over HTTPS.

Common request parameters

Every data endpoint accepts the same pagination, filtering, and format controls.

Parameter Type Description
page integer 1-based page number. Default 1.
per_page integer Rows per page. Default 100, maximum 1000.
domain integer Restrict to one of your domain IDs (see /export/whoami). A domain not in your account returns 403 domain_out_of_scope. Omit to include all your domains.
from, to date YYYY-MM-DD range on each endpoint’s primary date column. to is inclusive to end-of-day.
format string json (default) or csv.

Response format

JSON responses use a consistent envelope:

{
  "data": [ { …row objects… } ],
  "meta": { "page": 1, "per_page": 100, "total": 9273, "total_pages": 93 }
}

These headers accompany every list response, so a connector can paginate without parsing the body:

Header Meaning
X-Total-Count Total matching rows across all pages.
X-Page Current page.
X-Per-Page Rows per page.

CSV (?format=csv) returns the current page as a downloadable text/csv file with a header row; X-Total-Count is still sent so you can loop through pages. JSON object columns (in /export/scores) are emitted as embedded JSON strings in CSV.

Rate limits

Each key may make up to 120 requests per minute. Exceeding this returns 429 Too Many Requests — wait a few seconds and retry. For large extracts, page through with per_page=1000 rather than issuing many small requests.

Endpoints

GET/export/whoami

Returns the account the key belongs to and the domain IDs it can access. Call this first to discover your domain IDs for the domain filter.

curl -H "Authorization: Bearer cmx_live_…" \
  https://citemetrix.com/wp-json/citemetrix/v1/export/whoami

{
  "api_version": "v1",
  "account": { "user_id": 42, "email": "you@example.com", "tier": "agency" },
  "domains": { "count": 3, "ids": [15, 22, 27] }
}
GET/export/citations

The citation fact table: one row per (query × AI platform) result. Date filter applies to checked_at. Extra filters: platform, is_cited (0/1).

Columns: id, domain_id, keyword_id, platform, query_text, is_cited, is_recommended, is_compared, is_only_option, citation_url, citation_type, has_link, position_in_response, total_sources, mention_count, sentiment, sentiment_score, intent_category, business_driver, brand_position, total_brands_mentioned, checked_at.

GET /export/citations?domain=15&platform=chatgpt&is_cited=1&from=2026-05-01&to=2026-05-31&per_page=1000
GET/export/scores

Daily ModelScore™ and sub-scores per domain. Date filter applies to score_date.

Columns: id, domain_id, score_date, model_score, citation_score, brand_demand_score, authority_score, technical_score, total_citations, total_queries, citation_rate, plus JSON objects platform_breakdown, sentiment_breakdown, gsc_data, ga4_data, and created_at. The four breakdown fields are nested objects in JSON, JSON strings in CSV.

GET/export/platforms

Per-platform breakdown, aggregated across your citations by domain and platform. Date filter applies to checked_at. Extra filter: platform.

Columns: domain_id, platform, total_queries, total_citations, citation_rate (percent), recommended, avg_sentiment_score.

GET/export/competitors

Competitors you track per domain. Extra filter: status (active, paused, deleted).

Columns: id, domain_id, competitor_domain, competitor_name, status, created_at, updated_at.

GET/export/hallucinations

Detected AI hallucinations about your brand. Date filter applies to detected_at. Extra filters: platform, severity (minor, moderate, critical), status, hallucination_type.

Columns: id, domain_id, citation_id, fact_id, platform, severity, hallucination_type, status, claim_text, fact_text, ai_explanation, query_text, remediation_actions, cluster_id, cluster_label, reviewed_at, detected_at, created_at.

GET/export/annotations

Timeline annotations your team has added. Date filter applies to annotation_date. Extra filter: annotation_type (content_update, competitor, algorithm, campaign, other).

Columns: id, domain_id, user_id, annotation_date, note, annotation_type, created_at.

Platform slugs

The platform field and filter use these lowercase slugs:

chatgpt   gemini   perplexity   claude   copilot   grok   google_aio   mistral   deepseek

Error responses

HTTP code Meaning
401 missing_token / bad_token / invalid_token No, malformed, or revoked key.
403 api_not_entitled Valid key, but the plan doesn’t include API access.
403 domain_out_of_scope The requested domain is not in your account.
429 rate_limited More than 120 requests in a minute.

Errors use the standard WordPress REST shape: { "code": "…", "message": "…", "data": { "status": 403 } }.

Connect your BI tool

Looker Studio

Use a JSON/REST community connector. Set the request URL to an endpoint such as …/export/citations?per_page=1000, add the request header Authorization: Bearer cmx_live_…, and point the connector at the data array. Paginate by incrementing page until page > total_pages.

Tableau (Web Data Connector)

Point a Web Data Connector at the endpoint, add the Authorization: Bearer header, and map the JSON data array to your table schema using the column lists above. Drive pagination from X-Total-Count / meta.total_pages in the connector’s getData loop.

Google Sheets (Apps Script)

Because IMPORTDATA can’t send headers, use Apps Script’s UrlFetchApp:

function loadCitations() {
  var res = UrlFetchApp.fetch(
    'https://citemetrix.com/wp-json/citemetrix/v1/export/citations?format=csv&per_page=1000',
    { headers: { Authorization: 'Bearer cmx_live_…' } });
  var rows = Utilities.parseCsv(res.getContentText());
  SpreadsheetApp.getActiveSheet()
    .getRange(1, 1, rows.length, rows[0].length).setValues(rows);
}

Python

import requests

BASE = "https://citemetrix.com/wp-json/citemetrix/v1/export"
HEADERS = {"Authorization": "Bearer cmx_live_…"}

def fetch_all(endpoint, **params):
    params.setdefault("per_page", 1000)
    page, rows = 1, []
    while True:
        params["page"] = page
        r = requests.get(f"{BASE}/{endpoint}", headers=HEADERS,
                         params=params, timeout=60)
        r.raise_for_status()
        body = r.json()
        rows += body["data"]
        if page >= body["meta"]["total_pages"]:
            break
        page += 1
    return rows

citations = fetch_all("citations", domain=15, is_cited=1)
print(len(citations), "rows")

Good practices

  • One key per integration; label them clearly; revoke unused keys.
  • Page with per_page=1000 and stop when page >= total_pages.
  • For incremental syncs, store the last checked_at / score_date you pulled and pass it as from next run.
  • Treat keys like passwords: store them in a secrets manager, never commit them to source control.
Last updated: July 29, 2026 Suggest an edit ›