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
- Sign in to your CiteMetrix dashboard.
- Open the account menu (top right) and choose Data Export API.
- Enter a label describing where the key will be used (e.g. “Looker Studio”) and click Create key.
- Copy the key immediately — for security it is shown only once. It looks like
cmx_live_…. Store it in your secrets manager. - To retire a key, click Revoke. Revocation takes effect immediately; any integration using that key stops working.
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
/export/whoamiReturns 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] }
}
/export/citationsThe 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
/export/scoresDaily 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.
/export/platformsPer-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.
/export/competitorsCompetitors you track per domain. Extra filter: status (active, paused, deleted).
Columns: id, domain_id, competitor_domain, competitor_name, status, created_at, updated_at.
/export/hallucinationsDetected 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.
/export/annotationsTimeline 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=1000and stop whenpage >= total_pages. - For incremental syncs, store the last
checked_at/score_dateyou pulled and pass it asfromnext run. - Treat keys like passwords: store them in a secrets manager, never commit them to source control.