Skip to content

AEOptimiz for developers

REST API

The same reads the assistant and the MCP server use, over plain HTTP: Search Console history, index coverage, opportunities and reviews. Read-only. Nothing here spends credits or changes anything.

Machine-readable: OpenAPI 3.1 document.

Authentication

Create a key under Settings, then Developer. It is shown once. Send it with every request, either way:

Authorization: Bearer ikey_...
x-api-key: ikey_...

A key reads the workspace it was created in, and stops working when it is revoked or when the person who created it leaves the workspace.

Limits and errors

  • 60 requests a minute per key. Over that, 429 with a Retry-After header.
  • Request bodies up to 32 KB, as a JSON object.
  • Errors are JSON with an error message: 400 a body that is not a JSON object, 401 a missing or invalid key, 404 an endpoint that does not exist, 405 the wrong method, 413 a body too large, 422 a tool that could not answer.

Endpoints

GET /api/v1/sites

The sites in the workspace, by domain. Pass one as site to the tool endpoints.

curl https://aeoptimiz.com/api/v1/sites \
  -H "Authorization: Bearer $AEOPTIMIZ_API_KEY"

POST /api/v1/tools/get_site_profile

The site's own context: locale, market, tech stack, positioning, tracked competitors, writing voice, and how much data has been mirrored. Call this first when you need to tailor an answer to the business rather than give generic SEO advice.

FieldTypeAbout
sitestringThe site to ask about, as its domain (for example example.com). Needed only when the workspace has more than one site; the first is used otherwise.
curl https://aeoptimiz.com/api/v1/tools/get_site_profile \
  -H "Authorization: Bearer $AEOPTIMIZ_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'

POST /api/v1/tools/list_opportunities

The ranked list of what to fix, already scored by expected incremental monthly clicks. This is the engine's output: do NOT re-rank it, and do not invent findings that are not in it. Each row carries the evidence it was derived from, which is what you should quote. Where the site's GA4 property is joined, a row also carries `valuePerMonth`, already formatted with its currency: quote it exactly as given and never convert, scale or estimate it. Rows without one have no revenue data for that page, which is not the same as being worth nothing. A row carrying `availableAction` names a tool that fixes that kind of finding: offer it, and do not offer it for rows without one.

FieldTypeAbout
detectorone of "striking", "cannibal", "decay", "ctr_gap", "demand_gap", "coverage"Filter to one kind of finding. Omit for all.
limitintegerDefault 20.
sitestringThe site to ask about, as its domain (for example example.com). Needed only when the workspace has more than one site; the first is used otherwise.
curl https://aeoptimiz.com/api/v1/tools/list_opportunities \
  -H "Authorization: Bearer $AEOPTIMIZ_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'

POST /api/v1/tools/get_page_performance

Clicks, impressions and average position for one page over time, plus its top queries. Use it to check a claim before making it.

FieldTypeAbout
url*stringFull URL as it appears in Search Console.
daysintegerDefault 90.
sitestringThe site to ask about, as its domain (for example example.com). Needed only when the workspace has more than one site; the first is used otherwise.
curl https://aeoptimiz.com/api/v1/tools/get_page_performance \
  -H "Authorization: Bearer $AEOPTIMIZ_API_KEY" \
  -H "content-type: application/json" \
  -d '{"url":"<url>"}'

POST /api/v1/tools/get_query_performance

How the site performs for one search query, and which of its pages compete for it. Use it to confirm a cannibalisation finding before describing it.

FieldTypeAbout
query*string
daysintegerDefault 90.
sitestringThe site to ask about, as its domain (for example example.com). Needed only when the workspace has more than one site; the first is used otherwise.
curl https://aeoptimiz.com/api/v1/tools/get_query_performance \
  -H "Authorization: Bearer $AEOPTIMIZ_API_KEY" \
  -H "content-type: application/json" \
  -d '{"query":"<query>"}'

POST /api/v1/tools/get_coverage

Index coverage: how many submitted URLs Google has actually indexed, broken down by reason. This is the thing competing tools cannot answer, because a site: query returns a boolean with no coverage state, no crawl date and no Google-chosen canonical. Each class has a different fix.

FieldTypeAbout
coverageClassstringDrill into one class, e.g. not_discovered, discovered_not_crawled, crawled_not_indexed, duplicate_google_chose_other, excluded_noindex, soft_404, fetch_error.
limitintegerDefault 25.
sitestringThe site to ask about, as its domain (for example example.com). Needed only when the workspace has more than one site; the first is used otherwise.
curl https://aeoptimiz.com/api/v1/tools/get_coverage \
  -H "Authorization: Bearer $AEOPTIMIZ_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'

POST /api/v1/tools/check_existing_coverage

Whether this site ALREADY has a page about a topic. Call this BEFORE proposing any new page or article. It searches the site's own sitemap and every page that has earned an impression, and returns a verdict: duplicate (improve the existing page instead), related (write it, and link to the closest one), or clear. Proposing a page the customer published two years ago is the single most common complaint about tools in this category, and it happens because they cannot see the corpus.

FieldTypeAbout
topic*stringThe proposed page title or target keyword, not a single word.
limitintegerDefault 5.
sitestringThe site to ask about, as its domain (for example example.com). Needed only when the workspace has more than one site; the first is used otherwise.
curl https://aeoptimiz.com/api/v1/tools/check_existing_coverage \
  -H "Authorization: Bearer $AEOPTIMIZ_API_KEY" \
  -H "content-type: application/json" \
  -d '{"topic":"<topic>"}'

POST /api/v1/tools/did_it_work

Compare one page against ITSELF, before and after a change was made. Use it whenever someone asks whether something they changed helped. Returns a verdict and both windows, and refuses to conclude anything when the data is too thin or too recent, which is the honest answer far more often than people expect.

FieldTypeAbout
url*stringFull URL as it appears in Search Console.
actionedAt*stringThe date the change was made, as YYYY-MM-DD.
sitestringThe site to ask about, as its domain (for example example.com). Needed only when the workspace has more than one site; the first is used otherwise.
curl https://aeoptimiz.com/api/v1/tools/did_it_work \
  -H "Authorization: Bearer $AEOPTIMIZ_API_KEY" \
  -H "content-type: application/json" \
  -d '{"url":"<url>","actionedAt":"<actionedAt>"}'

POST /api/v1/tools/get_ai_visibility

How often AI assistants with web search cite this site, from the monthly probe: citation rate (the site's domain was among the sources the assistant's search retrieved), mention rate (the answer named the site), overall and per assistant (byAssistant: Claude, ChatGPT, Perplexity, Gemini, whichever were asked), both against the previous month, the hosts retrieved instead, and the questions where the site was not cited. Report citation and mention as two numbers, never as one score. Free: it reads stored results and runs nothing.

FieldTypeAbout
sitestringThe site to ask about, as its domain (for example example.com). Needed only when the workspace has more than one site; the first is used otherwise.
curl https://aeoptimiz.com/api/v1/tools/get_ai_visibility \
  -H "Authorization: Bearer $AEOPTIMIZ_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'

Prefer an AI app to a script? Connect it over MCP.