Developer Guide
API keys, the MCP server and how to query AIEO.ee data programmatically.
Everything in the dashboard is available programmatically: a REST API for scripts and integrations, and an MCP server for AI coding agents.
API keys
Create keys in Settings → MCP (dashboard). Keys are org-scoped and
start with aieo_:
readscope — every GET endpoint.read+writescope — mutating endpoints (POST/PATCH/DELETE). A read-only key gets403on any write.
Organization management (billing, keys, members) is session-only — API keys cannot touch it.
REST API
Base URL: https://app.aieo.ee/api (self-hosted: your API origin).
Authenticate with Authorization: Bearer aieo_....
The full route inventory is generated from the running app and lives at API Route Inventory. Hand-written request/response examples for the core workflows are below.
Core workflow examples
All calls: Authorization: Bearer aieo_..., base https://app.aieo.ee/api.
API keys act on their own organization — the x-org-id header is a
session-auth feature (dashboard users with multiple memberships) and does not
switch an API key's org. With session auth, an x-org-id (or ?orgId=) naming
an organization you are not a member of is rejected with 403
(code: "org_not_member") rather than silently falling back to another
organization. Mutating calls need a read+write key.
List projects
curl -H "Authorization: Bearer aieo_YOUR_KEY" \
https://app.aieo.ee/api/projects[
{ "id": "e312a3b6-...", "name": "acmecloud.io", "domain": "acmecloud.io" }
]Add a tracked keyword
One keyword per call (keyword, 2–120 chars):
curl -X POST -H "Authorization: Bearer aieo_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"keyword":"acmecloud vs hetzner"}' \
https://app.aieo.ee/api/projects/PROJECT_ID/seo/keywordsRun all GEO prompts now
curl -X POST -H "Authorization: Bearer aieo_YOUR_KEY" \
https://app.aieo.ee/api/projects/PROJECT_ID/geo/run-allRuns every active prompt on its engines as one request (at most 50 runs).
Each run is billed at its actual cost; same-day re-runs replace the day's
result. Send an idempotencyKey in the JSON body so a retried request is not
run twice. Errors carry machine-readable codes: no subscription returns 402
with code: "subscription_required"; a balance that cannot cover the held
maximum returns 402 with code: "insufficient_balance" (plus
neededUnits / availableUnits, 1 unit = $0.0001); more than 50 runs in one
request or 2,000 a day returns 429 with code: "too_many_runs".
Fetch AI visibility summary
curl -H "Authorization: Bearer aieo_YOUR_KEY" \
"https://app.aieo.ee/api/projects/PROJECT_ID/geo/summary?days=14"{ "runs": 80, "mentions": 44, "mentionRate": 55, "citationRate": 12,
"prompts": 6, "sov": [{ "name": "Hetzner", "mentions": 30 }] }Generate a white-label report
curl -H "Authorization: Bearer aieo_YOUR_KEY" \
https://app.aieo.ee/api/projects/PROJECT_ID/overview/report \
-o report.htmlErrors
Errors are JSON with an error message and, where applicable, a machine
code (subscription_required, insufficient_balance, limit_reached); the dashboard
maps these codes to localized copy. Watch the difference between truncation
caps and pagination: the keywords and GEO-runs lists accept a limit
(keywords default 100 / max 200, runs default 25 / max 100) that caps how
many rows return — there is no offset, so those endpoints are
single-response views; GSC rows are the one that truly paginates
(limit + offset).
MCP server
AIEO.ee exposes your SEO + GEO data over the Model Context Protocol — streamable HTTP, POST-only:
POST https://app.aieo.ee/mcp
Authorization: Bearer aieo_...Claude Code
claude mcp add --transport http aieo https://app.aieo.ee/mcp \
--header "Authorization: Bearer aieo_YOUR_KEY"Cursor
Add to .cursor/mcp.json:
{
"mcpServers": {
"aieo": {
"url": "https://app.aieo.ee/mcp",
"headers": { "Authorization": "Bearer aieo_YOUR_KEY" }
}
}
}Codex CLI
Export the key first, then reference it by env-var name:
export AIEO_API_KEY=aieo_YOUR_KEYAdd to ~/.codex/config.toml:
[mcp_servers.aieo]
url = "https://app.aieo.ee/mcp"
bearer_token_env_var = "AIEO_API_KEY"OpenCode
Export the key, then add a remote server to opencode.json (project root or
~/.config/opencode/):
export AIEO_API_KEY=aieo_YOUR_KEY{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"aieo": {
"type": "remote",
"url": "https://app.aieo.ee/mcp",
"headers": { "Authorization": "Bearer {env:AIEO_API_KEY}" }
}
}
}Then ask your agent things like "why did our AI visibility drop this week?"
— it will call query_ai_visibility, analyze_citations and friends.
Skills + MCP
MCP gives your agent the data and actions; a Skill gives it the
playbook. Skills are authored on the client side (for Claude Code, Codex
and OpenCode that is a SKILL.md file) and simply call the MCP tools above.
A minimal weekly review:
---
name: weekly-geo-review
description: Weekly AI-visibility review for a project, using the AIEO.ee MCP tools.
---
1. Call query_ai_visibility (days: 14); note per-engine changes.
2. Call analyze_citations; list the domains cited instead of us.
3. Call find_content_gap; pick the single biggest gap.
4. Call generate_content_brief for it (needs a write-scope key).
5. Reply: what changed, why, what to publish this week.Clients that cannot attach a custom MCP server can run the same playbook against the REST API with the same Bearer key.
Every tool, its real input schema and scope requirements are generated from the server's own definitions: MCP Tools Reference.
Rate limits
MCP requests are rate-limited per organization (60 requests/minute at the time
of writing). Exceeding it returns 429 with a Retry-After header.