Citality API
A REST API for your AI search visibility data. Read your brands, prompts, results, citations and visibility metrics, and run prompt tests programmatically. Available on the Agency plan.
All requests are made over HTTPS to:
https://citality.ai/api/v1
Responses are JSON. Successful responses wrap the payload in a data field;
list responses add a pagination object. Everything is scoped to the account
that owns the API key, so you never pass an account id.
Authentication
Create a key in Settings → API access. Keys look like
ctl_live_… and are shown only once, so store the value securely. Pass it as a
Bearer token on every request:
curl https://citality.ai/api/v1/clients \ -H "Authorization: Bearer ctl_live_your_key_here"
Scopes
Each key carries one or both scopes:
| Scope | Grants |
|---|---|
read | All GET endpoints. |
write | Running tests (POST /tests), which debits plan checks. |
Keys are revocable at any time (revocation is immediate) and stop working if the plan
lapses. A missing or revoked key returns 401; a key without the required
scope returns 403.
Rate limits
Requests are limited to 60 per minute per key. Every response includes standard headers so you can back off gracefully:
RateLimit-Limit: 60 RateLimit-Remaining: 58 RateLimit-Reset: 60
Exceeding the limit returns 429 with a rate_limited error.
Errors
Errors use standard HTTP status codes and a consistent JSON body:
{ "error": { "code": "not_found", "message": "Client not found." } }| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | Missing or malformed parameters. |
| 401 | unauthorized | Missing, invalid or revoked key. |
| 402 | insufficient_checks | Not enough checks left this period. |
| 403 | insufficient_scope / feature_unavailable | Key lacks the scope, or plan lacks API access. |
| 404 | not_found | No such resource or endpoint. |
| 429 | rate_limited | Too many requests. |
| 502 | upstream_error | All model runs failed upstream. |
Pagination
List endpoints that can return many rows (currently results) accept limit
(1–200, default 50) and an opaque cursor. Each response's
pagination.next is the cursor for the following page, or null at the end.
curl "https://citality.ai/api/v1/clients/CLIENT_ID/results?limit=50&cursor=NEXT_CURSOR" \ -H "Authorization: Bearer ctl_live_…"
Endpoints
/clientsList the brands/clients in the account.
Response
{
"data": [
{
"clientId": "01KNN…",
"name": "Acme Training Company",
"industry": "Online Training",
"url": "https://acmetrainingcompany.com",
"description": "…",
"createdAt": "2026-04-08T06:31:21.180Z"
}
]
}/clients/{clientId}Retrieve a single client by id. Returns 404 if it isn't in your account.
Response
{ "data": { "clientId": "01KNN…", "name": "Acme Training Company", … } }/clients/{clientId}/promptsList the tracked prompts for a client.
Response
{
"data": [
{ "promptId": "01KNN…", "clientId": "01KNN…",
"prompt": "best online training courses australia",
"intent": "Commercial", "location": "", "createdAt": "…" }
]
}/clients/{clientId}/resultsList a client's results, most recent first. Paginated.
Query parameters
| Param | Description |
|---|---|
from | Only results on/after this date (YYYY-MM-DD). |
to | Only results on/before this date (YYYY-MM-DD). |
platform | ChatGPT, Claude, Gemini or Perplexity. |
promptId | Restrict to one prompt. |
limit, cursor | Pagination (see above). |
Response
{
"data": [
{ "resultId": "01KNN…", "clientId": "01KNN…", "promptId": "01KNN…",
"date": "2026-09-11", "platform": "Perplexity",
"brandMentioned": true, "position": "Cited",
"competitors": "Coursera, Udemy",
"yourUrl": "https://acmetrainingcompany.com",
"competitorUrls": "https://…, https://…",
"confidence": null, "needsReview": false, "createdAt": "…" }
],
"pagination": { "limit": 50, "cursor": null, "next": "Mg==" }
}/clients/{clientId}/metrics
Visibility metrics for a client, matching the figures that power the dashboard and reports.
Accepts optional from / to dates to scope the window.
Response
{
"data": {
"totalPrompts": 3, "brandPercentage": 100, "totalResults": 46,
"topPlatform": "Perplexity", "topCompetitor": "Coursera",
"byPlatform": [ { "platform": "ChatGPT", "total": 12, "mentioned": 4, "percentage": 33 } ],
"byIntent": [ { "intent": "Commercial", "total": 40, "mentioned": 38, "percentage": 95 } ],
"topCompetitors": [ { "name": "Coursera", "count": 21 } ],
"topUrls": [ { "url": "https://…", "count": 9 } ]
},
"meta": { "from": null, "to": null }
}/prompts/{promptId}Retrieve a single prompt by id.
/results/{resultId}Retrieve a single result by id.
/tests
Run a prompt against the AI models and log the results. Requires the
write scope and debits plan checks (one check per model; Perplexity
counts as three; the high-accuracy judge doubles the cost). Failed model runs aren't charged.
Body
| Field | Description |
|---|---|
promptId required | The prompt to test. |
models | Array of model ids. Defaults to all enabled models, capped to the plan's models-per-test. |
opusJudge | Use the high-accuracy judge (Growth+; doubles checks). |
Request
curl -X POST https://citality.ai/api/v1/tests \
-H "Authorization: Bearer ctl_live_…" \
-H "Content-Type: application/json" \
-d '{ "promptId": "01KNN…" }'Response 201
{
"data": {
"testGroupId": "01M29…",
"logged": 4,
"billedChecks": 6,
"results": [ { "resultId": "…", "platform": "ChatGPT", "brandMentioned": false,
"position": "Not Present", "confidence": 0.95, … } ],
"notices": [],
"errors": []
},
"meta": { "checksRemaining": 6959 }
}
If you're out of checks the call returns 402 insufficient_checks. If every
model fails upstream it returns 502 upstream_error with details.
Objects
Client
clientId, name, industry, url, description, createdAt.
Prompt
promptId, clientId, prompt, intent (Commercial / Informational), location, createdAt.
Result
resultId, clientId, promptId, date,
platform, brandMentioned (boolean),
position (Cited / Mentioned / Not Present), competitors,
yourUrl, competitorUrls,
confidence (0–1, or null for citation/manual results),
needsReview, createdAt.
Metrics
totalPrompts, brandPercentage, totalResults,
topPlatform, topCompetitor, and the arrays
byPlatform, byIntent, topCompetitors, topUrls.
MCP server
Citality hosts a Model Context Protocol server so AI assistants (Claude and others) can query your data and run tests directly. It uses the same API keys, and every tool is enforced server-side.
Connect by URL (recommended)
{
"mcpServers": {
"citality": {
"type": "http",
"url": "https://citality.ai/mcp",
"headers": { "Authorization": "Bearer ctl_live_…" }
}
}
}Tools
| Tool | Scope | Description |
|---|---|---|
list_clients | read | List brands/clients. |
get_client | read | Get one client. |
list_prompts | read | List a client's prompts. |
list_results | read | List results with filters. |
get_visibility | read | Visibility metrics for a client. |
run_prompt_test | write | Run a prompt and log results. |
export_results | read | Export a client's results as CSV. |
OpenAPI
A machine-readable OpenAPI 3.1 description is available for client generation and tooling:
