For developers and AI

Scan for AI visibility from code, or from Claude.

Everything the web scanner does, two ways: a small REST API for your own scripts and products, and an MCP server so assistants can scan a page while you are talking to them.

Example call

One POST.
One structured verdict.

The shape of a call, not a live response. A scan is asynchronous: you post a URL, get an id, poll until it is done, then fetch the report as JSON.

$curl -X POST lantad.co/api/v1/scan \
-H "Authorization: Bearer lantad_KEY" \
-d '{"url":"acme-store.com"}'
{
"status": "queued",
"report_key": "noYTkhb0v3cTrpBPo7hpK",
"result_url": "/api/v1/report/noYT..."
}
# poll result_url, then read verdict.scores

The request, the header and the response fields are the real v1 shape. The report key shown is an example value, not a live one.

How it works

Key. Scan.
Poll. Fetch.

Four calls, no SDK required. The whole surface is small enough to read in one sitting and stable enough to build on.

  1. Get a key

    Any paid plan that includes the API issues one on your dashboard the moment you subscribe. Send it as an Authorization: Bearer header, never in a URL.

  2. Start a scan

    POST a URL to /api/v1/scan. A bare domain works. You get a scan id, a report key, and the two URLs to poll and to fetch.

  3. Poll for completion

    There is no completion webhook. Poll the status_url until the status leaves queued or running; scans usually finish in under a minute.

  4. Fetch the report

    GET the result_url for the complete structured verdict. The report key is the capability, so that endpoint needs no key at all.

What you get

Four surfaces on one key

The same entitlement covers all four, so there is no separate MCP plan and no separate billing to reconcile.

REST API

Start a scan, poll it, and fetch the complete structured verdict. JSON in, JSON out, with the same verdict shape the web report renders.

MCP server

Point Claude, ChatGPT or another assistant at the server and it can scan a page mid-conversation, then read the verdict back to you.

Monitoring hooks

One call when a deploy lands re-checks the pages you already monitor, instead of waiting a week for the next scheduled run.

Brand metrics

Your prompt-tracking history as data, from the key-gated brand-metrics endpoint: naming, sentiment, citation share and the competitors models reached for instead, with unmeasured days left null rather than zeroed.

Reference

REST API

Get an API key

Access uses a key (a Bearer token that looks like lantad_…) with a daily scan cap, because every scan runs a real browser render. Any paid plan that includes the API issues your key automatically: it is on your account dashboard the moment you subscribe (Starter from $59/mo, Pro from $199/mo, Business from $399/mo). Send the key as an Authorization: Bearer header; never put it in a URL or commit it to a repo. Need higher limits or a custom arrangement? Email hi@lantad.co.

Start a scan

curl -X POST https://lantad.co/api/v1/scan \
  -H "Authorization: Bearer lantad_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "yoursite.com"}'

A bare domain works; no scheme needed. Returns 202 with a scan_id, a report_key, and the URLs to poll and to fetch the result:

{
  "scan_id": "1c2fecfa-…",
  "report_key": "noYTkhb0v3cTrpBPo7hpK",
  "status": "queued",
  "report_url": "https://lantad.co/r/noYTkhb0v3cTrpBPo7hpK",
  "status_url": "https://lantad.co/api/v1/scan/1c2fecfa-…",
  "result_url": "https://lantad.co/api/v1/report/noYTkhb0v3cTrpBPo7hpK"
}

Check status

curl https://lantad.co/api/v1/scan/SCAN_ID

Returns { "status": "queued" | "running" | "done" | … } plus the sub-scores once the grade lands. Scans usually finish in under a minute.

Get the full report

curl https://lantad.co/api/v1/report/REPORT_KEY

Returns the complete structured verdict: the AI Visibility Score and grade, Prose Parity and the other sub-scores, the per-bot access matrix for all checked crawlers, the ranked defects with their fixes, and the evidence behind each. The report_key is the capability, so this endpoint needs no key, exactly like the shareable web report.

A complete poll loop

There is no completion webhook: start the scan, poll the status_url it returns until the status leaves the queue, then fetch the result_url. In shell, with jq:

# 1. Start the scan and keep the URLs it returns
resp=$(curl -s -X POST https://lantad.co/api/v1/scan \
  -H "Authorization: Bearer lantad_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "yoursite.com"}')
status_url=$(echo "$resp" | jq -r .status_url)
result_url=$(echo "$resp" | jq -r .result_url)

# 2. Poll every few seconds until the scan settles
while :; do
  status=$(curl -s "$status_url" | jq -r .status)
  if [ "$status" != "queued" ] && [ "$status" != "running" ]; then break; fi
  sleep 3
done

# 3. Fetch the structured verdict
curl -s "$result_url" | jq .verdict.scores

queued and running are in flight; everything else is terminal. done usually carries a graded report, but it can also carry an honest ungraded verdict with a null composite when the page could not be measured fairly (a bot challenge, a login wall, or too little readable text); read the verdict state, not just the status. The other honest endings: render_blocked (the render was refused or timed out, so you get a partial verdict without a grade), deferred_budget (our daily render budget or the monthly spend ceiling had no room, so the browser view was deferred and re-queued), not_html, opted_out, and failed. When a scan ends without producing a report, the result endpoint returns ready: false with a message saying so; it never invents numbers for a scan that was not measured.

Errors, precisely

Every error response is JSON with an error field describing what to change. The status codes the v1 routes actually return:

CodeRouteMeaning
200 POST /api/v1/scan The same URL was scanned within the last 24 hours: the existing report is returned with deduplicated: true and nothing is charged to your cap.
202 POST /api/v1/scan New scan accepted and queued.
400 all The body is not JSON, url is missing, or a report key or bulk id is malformed (report keys are 16+ letters and digits).
401 POST /api/v1/scan, POST /api/v1/bulk, POST /api/v1/deploy Key missing, invalid, or expired. On /api/v1/scan and MCP, every request also re-checks the live plan behind the key, so a cancelled or past-due subscription answers 401 there; on /api/v1/bulk a lapsed plan instead answers 422 with a plan message.
403 POST /api/v1/deploy The key is valid but was not minted by a paid plan, so it belongs to no account whose pages could be re-checked.
404 GET routes Unknown scan id, report key, or bulk job. The bulk CSV also answers 404 until the job finishes.
422 POST /api/v1/scan, POST /api/v1/bulk Refused before scanning: safety-guard rejections (private or reserved targets, non-HTTP schemes, a domain that has opted out), a plan without audit allowance, an exhausted audit allowance, or an empty or unfetchable sitemap. The body says which.
429 POST /api/v1/scan The key's daily scan cap is used up. The message names the cap; it resets at 00:00 UTC.
429 POST /api/v1/deploy Either the call came inside the 10-minute cooldown, in which case the body carries retry_after_seconds, or the account has used its 200 pages for the day. Nothing is queued either way, so it is safe to call on every deploy and ignore.

Limits and behaviour

  • A repeat scan of the same URL inside 24 hours returns the existing report and does not use budget.
  • Each key has a daily scan cap, resetting at 00:00 UTC: 200 scans a day on Starter, 500 on Pro, 1,000 on Business. The counter is a soft limit: it lives in an eventually consistent store, so a burst right at the boundary may land a request or two either side of the cap.
  • The cap follows your live plan, not the key: upgrade and the higher cap applies on the next request, no re-minting. The same re-check cuts a key off the moment its plan stops being active.
  • Keys from one-time purchases expire with the bundled monitoring months (3 for Deep Audit, 6 for Fix Sprint); subscription keys carry no expiry of their own because the entitlement re-check governs them.
  • The same SSRF and safety guards as the web form apply: private, reserved, and non-HTTP targets are refused.

Bulk sitemap audit (paid plans)

curl -X POST https://lantad.co/api/v1/bulk \
  -H "Authorization: Bearer lantad_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"sitemap": "https://yoursite.com/sitemap.xml"}'

Audits every same-site page in the sitemap with the raw-HTML checks (no browser render): readability verdict, structure facts, structured data, and per-crawler robots access. Poll GET /api/v1/bulk/ID for progress; results include a shareable page and a per-page CSV. Included with Starter (up to 2,000 pages per job, 30,000 per month), Pro (up to 4,000 pages per job, 100,000 per month) and Business (up to 6,000 pages per job, 200,000 per month). The one-time Deep Audit covers 10,000 pages, granted once rather than monthly.

Deploy hook (paid plans)

Monitoring re-checks your pages weekly, which leaves a week-wide hole: a deploy that puts your content back behind JavaScript is invisible until the next run. Call this when a deploy lands and we re-check the pages you monitor straight away.

curl -X POST https://lantad.co/api/v1/deploy \
  -H "Authorization: Bearer lantad_YOUR_KEY"

No body. The key identifies the account, and we re-check the pages already monitored on it, least-recently-checked first. Results arrive the usual way: each page gets a report, and your existing alerts fire if something broke.

{
  "queued": 12,
  "skipped": 1,
  "budget_stopped": false,
  "remaining_today": 188
}

skipped counts pages we could not scan right now, usually a domain that has since opted out. budget_stopped is true when the platform's spend ceiling was reached partway through, so you can tell a short run from a complete one instead of guessing from the count.

Limits. One call per account per 10 minutes, and up to 200 pages re-checked per account per day across every call. A call inside the cooldown answers 429 with retry_after_seconds; one past the day's allowance answers 429 and queues nothing. Deploying more often than the cooldown is fine, the extra calls are simply refused, so you can wire it to every deploy without branching on the result.

What it is not. This re-checks pages you already monitor; it does not add pages, and a page that is not monitored is not scanned. It is available on every paid plan with the API (Starter, Pro and Business), using the same key as the rest of this API, so revoking the key revokes the hook.

Brand metrics (paid plans)

Your prompt-tracking history as data: one row per UTC day on which at least one of your verified prompt runs finished, covering the last 365 days. The same rows the dashboard's brand-visibility trends draw, so a chart you saw and a number you fetched can never disagree.

curl https://lantad.co/api/v1/brand-metrics \
  -H "Authorization: Bearer lantad_YOUR_KEY"

Read the nulls honestly. A null metric means it was not measured that day, never zero: a day with no graded answers has sentiment_net: null, a day where no engine returned attributable sources has citation_share: null, and a day where no answer named the brand has avg_position: null (positions are 1-based, lower is better; 0 is not a possible value). A real 0 is a measured claim and is returned as 0. A realistic response, nulls included:

{
  "window_days": 365,
  "note": "One row per UTC day with at least one finished verified prompt run. A null metric means it was not measured that day, never zero.",
  "rows": [
    {
      "day": "2026-08-03",
      "mention_rate": 40,
      "sentiment_net": 0.25,
      "citation_share": 12,
      "avg_position": 2.3,
      "runs": 2,
      "sov": [{ "name": "Rival Co", "count": 3, "isPinned": true }]
    },
    {
      "day": "2026-08-06",
      "mention_rate": 35,
      "sentiment_net": null,
      "citation_share": null,
      "avg_position": null,
      "runs": 1,
      "sov": []
    }
  ]
}

Requires a plan-issued key: the key identifies the account whose history is returned, so a key not minted by a plan answers 403. The same rows are downloadable from the dashboard as CSV or JSON without touching the API.

Reference

MCP server, for Claude, ChatGPT and other assistants

Lantad runs an MCP server so an assistant can scan a site for you and read the result back. It speaks Streamable HTTP JSON-RPC at:

https://lantad.co/api/mcp

Authenticate with the same Bearer key. Two tools are exposed:

  • scan_website(url) runs a scan and waits up to about 22 seconds for the graded result, polling every 2 seconds. A bare domain works. The result is a text summary (score, grade, sub-scores, and the top issues worst first, each with its fix) plus the complete verdict as structured content, so the assistant can quote exact numbers rather than paraphrase. If the site has a report from the last 24 hours, that report is reused and your daily cap is untouched.
  • get_scan_report(report_key) fetches a prior report, useful when a slow site did not finish inside the wait window: scan_website then hands back the report_key and says to call this.

Protocol details, for client authors: POST only (GET answers 405; there is no server-initiated stream), and the server handles initialize, ping, tools/list, and tools/call. A missing or dead key gets a 401 with JSON-RPC error code -32001. Tool-level problems (a refused URL, an unknown report key, a spent daily cap) come back as tool results flagged isError with the same plain-language messages as the REST API, so the assistant can read the reason and tell you.

Connect it

In an MCP client that supports remote HTTP servers with headers (for example Claude Desktop or Cursor), add:

{
  "mcpServers": {
    "lantad": {
      "url": "https://lantad.co/api/mcp",
      "headers": { "Authorization": "Bearer lantad_YOUR_KEY" }
    }
  }
}

In Claude or ChatGPT's custom connector settings, add a connector pointing to https://lantad.co/api/mcp and set the Authorization header to your key. Then ask the assistant to scan a site, for example: "Scan stripe.com for AI visibility and summarise the top fixes."

Who it is for

Three people this is built for

Rate limits are per key, per day: 200 calls on Starter, 500 on Pro, and 1,000 on Business.

01

Platform engineers

You want a parity check in CI, so a deploy that moves content behind JavaScript fails the build rather than the quarter.

  • A scan step in CI, failing the build on a score drop
  • The deploy hook, so a bad deploy is caught the same hour
  • The bulk sitemap audit for whole-site sweeps
02

Product teams

You are building AI-visibility features into your own product and need the grading rather than the interface.

  • Client dashboards built on the report JSON
  • Scheduled sweeps across a portfolio of sites
  • Brand metrics exported as rows, nulls left honest
03

Assistant users

MCP lets Claude or ChatGPT scan a site mid-conversation and reason about the result with the exact figures in hand.

  • Scanning a site without leaving the chat
  • Quoting exact sub-scores instead of paraphrasing
  • Fetching a prior report by its key

Just the crawler-eye view of one URL, or a robots.txt question? The free What GPTBot sees and robots.txt tester tools need no key. Questions, higher limits, or a bug: hi@lantad.co.

One key, four surfaces, and the same grading code the website runs. There is no separate API scoring path: a scan started from curl, from the MCP server, or from the scan box on the homepage runs the same checks and returns the same verdict shape. What differs is how you start it and where the result lands.

A key works as long as the plan behind it is active: every request re-checks the live entitlement, so a lapsed plan stops the key on the next call.

FAQ

Common questions

Do I need an API key to read a report?

No. The report_key is the capability: GET /api/v1/report/<key> needs no Authorization header, exactly like the shareable web report at /r/<key>. Keys gate starting scans, not reading results.

Does re-scanning the same URL burn my daily budget?

No. A repeat scan of the same URL inside 24 hours returns the existing report with deduplicated: true, and the dedupe check runs before the daily cap is counted, so it costs nothing.

Is the API scored differently from the website?

No. There is one grading path. A scan started from the API, from the MCP server, or from the scan box on the homepage runs the same checks and returns the same verdict, including the same treatment of anything that could not be measured.

What are the rate limits?

Per key, per day: 200 calls on Starter, 500 on Pro, and 1,000 on Business. A one-time Deep Audit carries 200 a day for the bundled months. The cap resets at 00:00 UTC, and limits are enforced from the same configuration the plans page quotes.

What is the MCP server for?

It lets an assistant run a scan as a tool while you are in the conversation. You ask Claude about a page, it scans it, and it reads the verdict back with the evidence rather than guessing from the URL. If a slow site outlasts the tool's wait window, the assistant gets the report key and fetches the finished report with a second tool call.

Is there a webhook when a scan finishes?

No. Poll the status_url the scan response gives you; scans usually finish in under a minute. The status endpoint needs no key because scan ids are unguessable UUIDs.

How long does my API key last?

A subscription key works as long as the plan is active: every request re-checks the live entitlement, so a cancelled or past-due plan stops the key. Keys from one-time purchases expire with the bundled monitoring months: 3 months for a Deep Audit, 6 for a Fix Sprint.

Is there a free tier for the API?

No. The scan itself is free from the website, without an account, but programmatic access comes with a paid plan because it is the surface that costs real compute at volume.

How long can I fetch a report after scanning?

Scan artifacts are kept for up to 18 months, after which they are deleted or made inaccessible. Render screenshots expire after 24 hours, but they are cosmetic; the structured verdict is what the API returns.