API Documentation

The AEO Villa API lets agencies and larger teams pull scores, rankings, citation data, and recommendations into their own dashboards and tools — and trigger new scans and site audits directly. API access is available on the agency plan. Every example below is ready to copy — replace aeo_your_key_here with your own key and any SCAN_ID style placeholder with a real id from the matching list endpoint.

Authentication

Create an API key in your dashboard under Account Settings → API Keys. The full key is shown exactly once at creation. Send it on every request as a Bearer token:

Authenticate a request
curl "https://aeovilla.com/api/v1/scans" \
  -H "Authorization: Bearer aeo_your_key_here"

Requests without a valid key return 401. Revoked keys stop working immediately, and keys stop working if the account leaves the agency plan (403).

Rate limits and versioning

Each key is limited to 60 requests per minute (the limit is set per plan — every plan with API access shares this limit today); exceeding it returns 429. All endpoints live under /api/v1. Most are read only; scans and site audits can also be triggered with POST. Backward incompatible changes will ship under a new version prefix.

Endpoints

GET/api/v1/scans

Lists your page scans, newest first. Paginated with ?page= (20 per page).

Request
curl "https://aeovilla.com/api/v1/scans?page=1" \
  -H "Authorization: Bearer aeo_your_key_here"
Response
{
  "data": [
    {
      "id": "6a43b1a77c1e0abb32d7c598",
      "url": "https://example.com",
      "score": 83,
      "grade": "Excellent",
      "createdAt": "2026-06-30T12:08:07.538Z"
    }
  ],
  "page": 1,
  "pageSize": 20,
  "total": 42
}
POST/api/v1/scans

Runs a new page scan for a URL and returns its score, subject to the same monthly scan quota and SSRF safety checks as the dashboard's single scan tool.

Request
curl -X POST "https://aeovilla.com/api/v1/scans" \
  -H "Authorization: Bearer aeo_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com"}'
Response
{
  "data": {
    "id": "6a43b1a77c1e0abb32d7c598",
    "url": "https://example.com",
    "score": 83,
    "grade": "Excellent",
    "createdAt": "2026-06-30T12:08:07.538Z"
  }
}
GET/api/v1/scans/:id

Full scan detail: overall score, per category scores, and every flagged issue with its recommendation.

Request
curl "https://aeovilla.com/api/v1/scans/SCAN_ID" \
  -H "Authorization: Bearer aeo_your_key_here"
Response
{
  "data": {
    "id": "6a43b1a77c1e0abb32d7c598",
    "url": "https://example.com",
    "score": 53,
    "grade": "Average",
    "categories": {
      "schema": 30,
      "metadata": 40,
      "clarity": 70,
      "trust": 15,
      "aiReadiness": 50,
      "performance": 100,
      "headings": 100,
      "eeat": 25
    },
    "issues": [
      {
        "severity": "high",
        "category": "Schema",
        "title": "Structured Data Missing",
        "message": "No JSON-LD structured data found on the page.",
        "recommendation": "Add Organization and WebSite schema to the page head."
      }
    ],
    "createdAt": "2026-06-30T12:08:07.538Z"
  }
}
GET/api/v1/audits

Lists your full site crawl audits, newest first. Paginated with ?page= (20 per page).

Request
curl "https://aeovilla.com/api/v1/audits?page=1" \
  -H "Authorization: Bearer aeo_your_key_here"
Response
{
  "data": [
    {
      "id": "6a3fa1264b68b30d382013e0",
      "domain": "example.com",
      "averageScore": 79,
      "pagesScanned": 11,
      "createdAt": "2026-06-27T10:08:38.998Z"
    }
  ],
  "page": 1,
  "pageSize": 20,
  "total": 3
}
GET/api/v1/audits/:id

Full audit detail: every crawled page with its score, the sitewide issue rollup, a keyword cannibalization report, and a diff against the previous crawl of the same domain (when one exists).

Request
curl "https://aeovilla.com/api/v1/audits/AUDIT_ID" \
  -H "Authorization: Bearer aeo_your_key_here"
Response
{
  "data": {
    "id": "6a3fa1264b68b30d382013e0",
    "domain": "example.com",
    "averageScore": 79,
    "pagesScanned": 11,
    "pages": [
      {
        "url": "https://example.com/about",
        "score": 84,
        "grade": "Excellent",
        "criticalIssuesCount": 0
      }
    ],
    "siteWideIssues": [
      {
        "category": "Schema",
        "count": 4,
        "recommendation": "Add FAQPage schema to question and answer pages."
      }
    ],
    "previousAuditId": "6a3f72226f364bd3a7ea9401",
    "diffSummary": {
      "newPages": ["https://example.com/new-guide"],
      "removedPages": [],
      "changedPages": [
        {
          "url": "https://example.com/pricing",
          "scoreBefore": 72,
          "scoreAfter": 84,
          "titleChanged": true,
          "schemaChanged": false
        }
      ]
    },
    "cannibalization": [
      {
        "pages": ["https://example.com/blog/seo-tips", "https://example.com/blog/seo-guide"],
        "similarity": 0.82,
        "sharedTerms": ["seo", "tips", "guide"],
        "recommendation": "These pages appear to target the same topic (seo, tips, guide). Consolidate them into one authoritative page, or differentiate titles and headings so each targets a distinct query."
      }
    ],
    "createdAt": "2026-06-27T10:08:38.998Z"
  }
}
POST/api/v1/audits

Kicks off a new multi page site audit for a domain. The crawl runs in the background exactly like the dashboard's audit trigger — poll GET /api/v1/audits/:id for progress once status moves from running to completed.

Request
curl -X POST "https://aeovilla.com/api/v1/audits" \
  -H "Authorization: Bearer aeo_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com"}'
Response
{
  "data": {
    "id": "6a3fa1264b68b30d382013e0",
    "status": "running"
  }
}
GET/api/v1/monitors

Your monitored URLs with the complete score trend history — every rescan entry includes the overall score and all seven category scores, ready to chart.

Request
curl "https://aeovilla.com/api/v1/monitors" \
  -H "Authorization: Bearer aeo_your_key_here"
Response
{
  "data": [
    {
      "id": "6a3f8d2e8f7837d921eeab10",
      "url": "https://example.com",
      "schedule": "weekly",
      "isActive": true,
      "lastScanDate": "2026-06-27T08:39:33.065Z",
      "nextScanDate": "2026-07-04T08:39:33.065Z",
      "history": [
        {
          "date": "2026-06-27T08:39:33.065Z",
          "score": 93,
          "categories": {
            "schema": 95,
            "metadata": 90,
            "clarity": 88,
            "performance": 100,
            "trust": 85,
            "aiReadiness": 96,
            "headings": 100,
            "eeat": 80
          }
        }
      ],
      "decayFlag": {
        "isDecaying": false,
        "delta": 0,
        "consecutiveDeclines": 0,
        "detectedAt": null
      },
      "createdAt": "2026-06-20T08:39:33.065Z"
    }
  ]
}
GET/api/v1/citations

Your tracked AI citation prompts with full check history: citation rate, cited source domains, and competitor hit rates per check, newest last.

Request
curl "https://aeovilla.com/api/v1/citations" \
  -H "Authorization: Bearer aeo_your_key_here"
Response
{
  "data": [
    {
      "id": "6a3f95118f7837d921eead02",
      "prompt": "best project management tools for agencies",
      "targetDomain": "example.com",
      "competitors": ["competitor.com"],
      "schedule": "weekly",
      "lastRunAt": "2026-06-27T09:00:00.000Z",
      "nextRunAt": "2026-07-04T09:00:00.000Z",
      "history": [
        {
          "date": "2026-06-27T09:00:00.000Z",
          "citationRate": 66,
          "citedDomains": ["example.com", "review-site.com"],
          "competitorHits": { "competitor.com": 33 }
        }
      ],
      "createdAt": "2026-06-20T09:00:00.000Z"
    }
  ]
}
GET/api/v1/visibility

Workspace level AI visibility (share of voice) snapshots — brand rate vs named competitor rates, plus average position and sentiment, aggregated across your tracked prompts. Filter with ?period=week or ?period=day.

Request
curl "https://aeovilla.com/api/v1/visibility?period=week" \
  -H "Authorization: Bearer aeo_your_key_here"
Response
{
  "data": [
    {
      "week": "2026-W27",
      "period": "week",
      "brandRate": 58,
      "competitorRates": { "competitor.com": 33 },
      "avgPosition": 1.4,
      "avgSentimentScore": 42,
      "sampleSize": 6,
      "computedAt": "2026-07-04T08:00:00.000Z"
    }
  ]
}
GET/api/v1/prompts

Your saved Prompt Library, newest first. Paginated with ?page= (20 per page).

Request
curl "https://aeovilla.com/api/v1/prompts?page=1" \
  -H "Authorization: Bearer aeo_your_key_here"
Response
{
  "data": [
    {
      "id": "6a3f95118f7837d921eead02",
      "text": "best project management tools for agencies",
      "type": "geo_prompt",
      "journeyStage": "consideration",
      "tags": ["comparison"],
      "source": "user",
      "createdAt": "2026-06-20T09:00:00.000Z"
    }
  ],
  "page": 1,
  "pageSize": 20,
  "total": 12
}
GET/api/v1/recommendations

Your recommendations queue, highest priority first. Filter with ?status= (open, in_progress, done, or dismissed). Paginated with ?page= (20 per page).

Request
curl "https://aeovilla.com/api/v1/recommendations?status=open" \
  -H "Authorization: Bearer aeo_your_key_here"
Response
{
  "data": [
    {
      "id": "6a3fb2118f7837d921eeafa1",
      "type": "on_page_fix",
      "source": "scan_issue",
      "title": "Structured Data Missing",
      "description": "Add Organization and WebSite schema to the page head.",
      "priorityScore": 90,
      "priorityLabel": "critical",
      "status": "open",
      "assignee": null,
      "createdAt": "2026-06-30T12:08:07.538Z"
    }
  ],
  "page": 1,
  "pageSize": 20,
  "total": 7
}
GET/api/v1/crawlers

Verified AI crawler visit rollups per bot — total visits, unique pages fetched, and last visit time. Part of AI Crawler Analytics, which is still being rolled out; this endpoint returns 404until it's enabled on your account.

Request
curl "https://aeovilla.com/api/v1/crawlers" \
  -H "Authorization: Bearer aeo_your_key_here"
Response
{
  "data": [
    {
      "bot": "gptbot",
      "totalVisits": 14,
      "uniquePaths": 6,
      "lastVisitAt": "2026-07-16T04:12:00.000Z"
    }
  ]
}

CSV exports

Every major data table in the dashboard (scans, site audits, monitors, the Prompt Library, recommendations, and cited domains) has an "Export CSV" download next to its list — session authenticated, not part of the API key surface above. Useful for a quick spreadsheet pull without writing any code against the endpoints documented here.

MCP server

AEO Villa also runs a hosted Model Context Protocol server at https://aeovilla.com/api/mcp, so an AI assistant can query and act on your AEO data directly instead of you copying numbers into a prompt by hand. It speaks Streamable HTTP and uses the exact same API key as the REST endpoints above — configure your MCP client to send Authorization: Bearer aeo_your_key_here as a custom header on every request to that URL.

Available tools:

  • list_scans, run_scan
  • list_audits, run_audit
  • get_visibility
  • list_recommendations
  • get_citations
Claude Desktop config (claude_desktop_config.json)
{
  "mcpServers": {
    "aeo-villa": {
      "url": "https://aeovilla.com/api/mcp",
      "headers": {
        "Authorization": "Bearer aeo_your_key_here"
      }
    }
  }
}

Outbound webhook events

Alongside the read API, AEO Villa can push events to a webhook URL you configure on the AEO Monitoring page. Every event is a JSON POST with a flat payload: { "event": "…", …, "sentAt": "…" }. Point the URL at Zapier, Make, or your own endpoint to turn flagged issues into tasks in any project management or CRM tool — no per tool connector needed.

issue_flagged

Fires when a scan or site audit finds critical issues. Site audits send auditId and domain instead of scanId and url.

Payload
{
  "event": "issue_flagged",
  "scanId": "6a43b1a77c1e0abb32d7c598",
  "url": "https://example.com",
  "score": 53,
  "criticalCount": 2,
  "issues": [
    { "category": "Schema", "title": "Structured Data Missing" }
  ],
  "sentAt": "2026-07-02T10:00:00.000Z"
}

score_drop

Fires when a monitored URL falls 10 or more points between rescans.

Payload
{
  "event": "score_drop",
  "url": "https://example.com",
  "oldScore": 82,
  "newScore": 64,
  "drop": 18,
  "date": "2026-07-02T10:00:00.000Z",
  "sentAt": "2026-07-02T10:00:00.000Z"
}

citation_change

Fires when a tracked prompt's citation rate moves 25 or more points between scheduled checks.

Payload
{
  "event": "citation_change",
  "prompt": "best project management tools for agencies",
  "targetDomain": "example.com",
  "oldRate": 66,
  "newRate": 0,
  "date": "2026-07-02T10:00:00.000Z",
  "sentAt": "2026-07-02T10:00:00.000Z"
}

report_ready

Fires when a scheduled client report is generated for a monitored URL.

Payload
{
  "event": "report_ready",
  "url": "https://example.com",
  "score": 78,
  "reportUrl": "https://aeovilla.com/r/abc123def456",
  "date": "2026-07-02T10:00:00.000Z",
  "sentAt": "2026-07-02T10:00:00.000Z"
}

anomaly_detected

Fires on a statistically unusual score or citation rate movement that doesn't cross the fixed score_drop/citation_change thresholds — a spike, or several consecutive declines in a row.

Payload
{
  "event": "anomaly_detected",
  "url": "https://example.com",
  "kind": "spike",
  "detail": "Score moved to 91, 2.8 standard deviations from its recent average.",
  "date": "2026-07-02T10:00:00.000Z",
  "sentAt": "2026-07-02T10:00:00.000Z"
}

visibility_drop

Fires when your overall AI visibility (the workspace level share of voice across every tracked prompt, not a single prompt) falls 15 or more points day over day.

Payload
{
  "event": "visibility_drop",
  "oldRate": 62,
  "newRate": 40,
  "date": "2026-07-02T10:00:00.000Z",
  "sentAt": "2026-07-02T10:00:00.000Z"
}

new_citation

Fires the first time a tracked prompt goes from never being cited to actually being cited — a meaningful transition even when the move is smaller than citation_change's 25 point threshold.

Payload
{
  "event": "new_citation",
  "prompt": "best project management tools for agencies",
  "targetDomain": "example.com",
  "newRate": 15,
  "date": "2026-07-02T10:00:00.000Z",
  "sentAt": "2026-07-02T10:00:00.000Z"
}

recommendation_status

Fires when a recommendation's status changes (e.g. moved to in_progress or done on the Recommendations board).

Payload
{
  "event": "recommendation_status",
  "recommendationId": "6a3fb2118f7837d921eeafa1",
  "title": "Structured Data Missing",
  "oldStatus": "open",
  "newStatus": "in_progress",
  "sentAt": "2026-07-02T10:00:00.000Z"
}

Errors

Errors return a JSON body with an error message and an appropriate status code: 401 invalid or revoked key, 403 plan does not include API access, 404 resource not found or not yours, 429 rate limit exceeded.

Error response
{
  "error": "Invalid or revoked API key."
}