REST API

The REST API exposes the same features as the MCP server over plain HTTPS and JSON: list sites, read reports, run audits, check pages and draft llms.txt files.

By · Updated

How do I authenticate?#

Create a key in Settings → API and MCP and send it as a bearer token. Every request is scoped to the key's workspace.

header
Authorization: Bearer sfc_your_key

Responses are JSON: { "data": … } on success, { "error": { "code", "message" } } otherwise. Base URL: https://getsurfaced.in/api/v1

Endpoints#

MethodPathWhat it doesCredits
GET/sitesSites in the workspace with latest scores0
GET/sites/:site/report?section=Latest report. :site is an id or domain. section: summary, issues, fixes, competitors, full0
POST/auditsStart a full audit for a url or existing site1 + 1 per 3 AI questions
GET/audits/:idAudit status, progress and scores0
POST/checkSingle-page SEO and AEO check from a url or raw html0 (300/day)
POST/llms-txtDraft an llms.txt from a site's pages0 (30/day)
GET/creditsBalance, pricing and each site's report cost0

Check a page

terminal
curl https://getsurfaced.in/api/v1/check \
  -H "Authorization: Bearer $SURFACED_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com"}'

Pass html instead of url to check markup that isn't published yet, with an optional pageUrl for canonical checks.

Run an audit and wait for it

node
const headers = { Authorization: `Bearer ${process.env.SURFACED_KEY}`, "Content-Type": "application/json" };

const { data: run } = await fetch("https://getsurfaced.in/api/v1/audits", {
  method: "POST",
  headers,
  body: JSON.stringify({ url: "https://example.com", competitors: ["https://competitor.com"] }),
}).then((r) => r.json());

let audit;
do {
  await new Promise((r) => setTimeout(r, 5000));
  ({ data: audit } = await fetch(`https://getsurfaced.in/api/v1/audits/${run.auditId}`, { headers }).then((r) => r.json()));
} while (audit.status === "queued" || audit.status === "running");

const { data: fixes } = await fetch(`https://getsurfaced.in/api/v1/sites/${run.site.id}/report?section=fixes`, { headers }).then((r) => r.json());
console.log(fixes.fixes);

Errors and limits#

StatusMeaning
400Invalid input (bad URL, missing field)
401Missing, invalid or revoked API key
402Not enough credits for an audit
404Site, report or audit not found in this workspace
429Rate limited: 60 requests a minute per endpoint, 10 audits a minute