Build with CiteFit
Query brand visibility data, trigger scans, and integrate CiteFit into your own workflows — via the REST API or the MCP server for AI agent use.
API Keys
All programmatic access uses a Bearer API key. Keys carry the prefix bp_ and are shown only once at creation.
Generate a key
- 1Go to Dashboard → Account → API Keys.
- 2Click New API key, give it a label, and copy the full key immediately — it is shown only once.
- 3You may hold up to 10 active keys per account. Revoke unused keys to stay within the limit.
Using the key
Pass the key as a Bearer token in every request:
Authorization: Bearer bp_<your-key>Requests without a valid key receive 401 Unauthorized.
REST API
Standard JSON over HTTPS. All request and response bodies use Content-Type: application/json.
Base URL
https://www.citefit.comBrands
| Method | Path | Description |
|---|---|---|
GET | /api/brands | List all tracked brands |
POST | /api/brands | Create a brand |
GET | /api/brands/:id | Get a brand |
PATCH | /api/brands/:id | Update a brand |
DELETE | /api/brands/:id | Delete a brand |
curl https://www.citefit.com/api/brands \
-H "Authorization: Bearer bp_<your-key>"Scans
| Method | Path | Description |
|---|---|---|
POST | /api/run-scan | Trigger a manual scan |
GET | /api/scan-status/:jobId | Poll scan progress |
# Trigger a scan
curl -X POST https://www.citefit.com/api/run-scan \
-H "Authorization: Bearer bp_<your-key>" \
-H "Content-Type: application/json" \
-d '{"brand_id": "<uuid>"}'
# → {"job_id": "f9e8d7c6-...", "status": "pending"}# Poll for completion
curl https://www.citefit.com/api/scan-status/f9e8d7c6-... \
-H "Authorization: Bearer bp_<your-key>"
# → {"status": "running", "progress": {"current": 4, "total": 12, "step": "chatgpt"}}status progresses: pending → running → complete or error. The endpoint honours If-None-Match / ETag for efficient polling.
Visibility & insights
| Method | Path | Description |
|---|---|---|
GET | /api/alerts/:brandId | Unseen visibility alerts |
GET | /api/recommendations/:brandId | AI-generated recommendations |
GET | /api/competitive-delta/:brandId | Competitive comparison |
GET | /api/seo-health/:brandId | SEO health snapshot |
Plan limits
| Plan | Scan interval | On-demand cap |
|---|---|---|
| Pro | Every 3 days per brand | — |
| Ultra | Unlimited | 25 scans / day across all brands |
Exceeding limits returns 429 with a descriptive error message.
MCP Server
CiteFit implements the MCP 2025-03-26 spec over HTTP + SSE. Connect any MCP-compatible AI client directly to your brand data.
Endpoint
https://www.citefit.com/api/mcp
Protocol
MCP 2025-03-26 (Streamable HTTP)
POST
JSON-RPC 2.0 request / response
GET
SSE keep-alive stream
Connect a client
Add CiteFit to your MCP client config (Claude Desktop, Cursor, Continue, etc.):
// mcp-config.json
{
"mcpServers": {
"citefit": {
"url": "https://www.citefit.com/api/mcp",
"headers": {
"Authorization": "Bearer bp_<your-key>"
}
}
}
}The server advertises its capabilities during the initialize handshake and lists its tools via tools/list. Machine-readable metadata is available at /.well-known/mcp/server-card.json.
SSE connection limits
The GET /api/mcp endpoint accepts up to 2 concurrent SSE connections per API key. Exceeding this returns 429 with Retry-After: 60. The server sends a keep-alive ping every 25 seconds and releases the connection slot automatically on client disconnect.
Tool reference
All tools return a content array with a single text item containing JSON. On failure, isError: true is set and text contains the error message.
list_brandsno arguments requiredList all brands tracked by the authenticated user.
// request
{"name": "list_brands", "arguments": {}}
// response text (JSON)
[
{
"id": "a1b2c3d4-...",
"name": "Acme Corp",
"domain": "acme.com",
"competitors": ["Globex"],
"created_at": "2025-03-01T12:00:00Z"
}
]get_brandGet a single brand including all its prompts.
| Argument | Type | Required | Description |
|---|---|---|---|
brand_id | string (UUID) | yes | Brand identifier |
get_visibilityGet daily visibility snapshots (mention rate, average position) for a brand. Returns up to 90 days × 3 platforms by default.
| Argument | Type | Required | Description |
|---|---|---|---|
brand_id | string (UUID) | yes | Brand identifier |
platform | string | no | chatgpt | perplexity | google_aio |
from | ISO date | no | Lower bound e.g. 2025-01-01 |
to | ISO date | no | Upper bound |
// response text sample
[
{
"date": "2025-03-15",
"platform": "chatgpt",
"mention_rate": 0.62,
"avg_position": 2.1,
"total_prompts": 8,
"mentioned_count": 5,
"scan_count": 1
}
]get_recent_resultsMost recent scan results for a brand across all platforms.
| Argument | Type | Required | Description |
|---|---|---|---|
brand_id | string (UUID) | yes | Brand identifier |
limit | integer 1–100 | no | Max results (default 20) |
get_recommendationsAI-generated recommendations for improving brand visibility.
| Argument | Type | Required | Description |
|---|---|---|---|
brand_id | string (UUID) | yes | Brand identifier |
include_dismissed | boolean | no | Include dismissed items (default false) |
get_alertsUnseen visibility alerts for a brand.
| Argument | Type | Required | Description |
|---|---|---|---|
brand_id | string (UUID) | yes | Brand identifier |
trigger_scanTrigger a manual brand scan. Runs asynchronously — poll the returned poll_url to track progress. The same plan limits as the REST API apply.
| Argument | Type | Required | Description |
|---|---|---|---|
brand_id | string (UUID) | yes | Brand to scan |
// response text
{
"job_id": "f9e8d7c6-...",
"status": "pending",
"poll_url": "/api/scan-status/f9e8d7c6-..."
}Error reference
HTTP status codes
| Status | Meaning |
|---|---|
400 | Malformed JSON or invalid JSON-RPC request |
401 | Missing or invalid API key |
402 | Free trial ended — upgrade required |
429 | Rate limit or too many SSE connections |
500 | Internal server error |
JSON-RPC error codes (MCP)
| Code | Meaning |
|---|---|
-32700 | Parse error |
-32600 | Invalid request |
-32601 | Method not found |
-32602 | Invalid params |
-32603 | Internal error |
-32001 | Unauthorized (application-level) |
Code examples
cURL — list brands
curl https://www.citefit.com/api/brands \
-H "Authorization: Bearer bp_<your-key>"Python — MCP client
import httpx, json
API_KEY = "bp_<your-key>"
BASE = "https://www.citefit.com/api/mcp"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
def rpc(method, params=None, *, id=1):
r = httpx.post(BASE, headers=HEADERS, json={
"jsonrpc": "2.0", "id": id, "method": method, "params": params or {}
})
r.raise_for_status()
return r.json()
# Handshake
rpc("initialize", {
"protocolVersion": "2025-03-26",
"clientInfo": {"name": "my-script", "version": "1.0.0"},
"capabilities": {},
})
# Fetch brands
result = rpc("tools/call", {"name": "list_brands", "arguments": {}}, id=2)
brands = json.loads(result["result"]["content"][0]["text"])
# Visibility for the first brand
vis = rpc("tools/call", {
"name": "get_visibility",
"arguments": {"brand_id": brands[0]["id"], "platform": "chatgpt"},
}, id=3)
print(json.loads(vis["result"]["content"][0]["text"]))TypeScript — trigger scan and poll
const API_KEY = "bp_<your-key>";
const MCP_URL = "https://www.citefit.com/api/mcp";
async function rpc(method: string, params = {}, id = 1) {
const res = await fetch(MCP_URL, {
method: "POST",
headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ jsonrpc: "2.0", id, method, params }),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
}
// Handshake
await rpc("initialize", {
protocolVersion: "2025-03-26",
clientInfo: { name: "my-agent", version: "1.0.0" },
capabilities: {},
});
// Trigger a scan
const { result } = await rpc("tools/call", {
name: "trigger_scan",
arguments: { brand_id: "<uuid>" },
}, 2);
const { job_id, poll_url } = JSON.parse(result.content[0].text);
// Poll until done
while (true) {
const r = await fetch(`https://www.citefit.com${poll_url}`, {
headers: { Authorization: `Bearer ${API_KEY}` },
});
const job = await r.json();
if (job.status === "complete" || job.status === "error") { console.log(job); break; }
await new Promise((res) => setTimeout(res, 3_000));
}Ready to integrate?
Generate your first API key from the account settings page.
Go to Account Settings