Your first request
# 1. Start the full audit: 202 with an id (a refusal is printed and ends the script)
curl -sS -X POST 'https://fukamo.com/api/v1/audits' \
-H "Authorization: Bearer $FUKAMO_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"urls": ["https://example.com/"]}' > start.json
ID=$(jq -r '.id // empty' start.json)
[ -n "$ID" ] || { cat start.json; exit 1; }
# 2. Poll every 5 seconds until it is complete (typically 1–4 minutes, at most 15)
for i in $(seq 180); do
curl -sS "https://fukamo.com/api/v1/audits/$ID" -H "Authorization: Bearer $FUKAMO_API_KEY" > audit.json
[ "$(jq -r .status audit.json)" = complete ] && break
jq -e '.code and .code != "rate_limited"' audit.json > /dev/null && { cat audit.json; exit 1; }
sleep 5
done
jq '.pages[0].result.score' audit.jsonTest your API key
The key goes only to our server and is stored nowhere. The test runs no audit and spends no quota.
HTTP/1.1 404 Not Found
{
"error": "No such batch for this key.",
"code": "not_found"
}Start, ask, collect
An audit takes minutes, so the API returns an id at once and you collect the result when it is ready.
- POST /api/v1/audits1. Start the audit
Send the URL. The server answers at once with 202 and an
id. - GET /api/v1/audits/{id}2. Ask for the status
Every 5 seconds until
statusiscomplete. Costs no quota. - GET /api/v1/audits/{id}?format=pdf3. Collect the result
pages[].resultcarries the scores and findings;?format=pdfreturns the PDF.
queuedwaiting in the queuerunningthe audit runs, usually 1–4 minutescompletethe result is readyKey and limits
Send the key with every call: Authorization: Bearer fk_… or X-Api-Key. Never put it in a URL or public JavaScript.
| Limit | Value |
|---|---|
| Audits per key | 60 per hour by default |
| A full audit of one page | 5 audits of the quota — 12 full audits an hour by default |
| Pages of one website | 1 a day per key unless the operator gave the key more (up to 50); the same page can be audited again |
| Pages in progress per key | At most 10 at once, queued or running |
| Status polls | 120 per minute, outside the quota |
| PDF reports | A quarter of the quota, 15 an hour by default, apart from audits |
Get a key at support@fukamo.com — say what you will use it for.
POST /api/v1/audits
Send url. Optionally add query: the search phrase the page is measured against (one phrase, max 32 words).
- No
query: we use the website’s first keyword suggestion. Its findings come back asreviewand do not lower the score. - With
query: scored exactly as on the website. A page that barely covers the phrase gets a cappedscore.fukamo. locale: the report’s language —en(default),skores— for the JSON and the PDF.
curl -X POST "https://fukamo.com/api/v1/audits" \
-H "Authorization: Bearer $FUKAMO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "urls": ["https://example.com/"], "query": "seo audit" }'You get 202 with id and statusUrl at once. Quota is spent only when the page answers — an unreachable page returns 502 unreachable and costs nothing.
{
"version": 1,
"id": "c547d6c0-15b5-4ba9-badc-c6fc392e0d7b",
"status": "queued",
"pages": 1,
"queries": [
{ "url": "https://example.com/", "query": { "primary": "seo audit", "source": "input" } }
],
"statusUrl": "/api/v1/audits/c547d6c0-15b5-4ba9-badc-c6fc392e0d7b"
}curl "https://fukamo.com/api/v1/audits/<id>" -H "Authorization: Bearer $FUKAMO_API_KEY"{
"version": 1,
"id": "c547d6c0-15b5-4ba9-badc-c6fc392e0d7b",
"status": "complete",
"createdAt": "2026-09-24T08:12:03.000Z",
"queue": { "position": 0, "pagesAhead": 0, "etaSeconds": 0, "samples": 12 },
"pages": [
{
"url": "https://example.com/",
"status": "done",
"result": {
"version": 1,
"url": "https://example.com/",
"scannedAt": "2026-09-24T08:14:41.000Z",
"score": {
"seo": 71,
"geo": 64,
"fukamo": 68,
"parts": { "content": 66, "google": 71, "ai": 64, "technical": 76,
"experience": 61 },
"html": 69
},
"summary": { "failed": 1, "review": 0, "extras": 0, "passed": 311,
"notApplicable": 424, "unavailable": 6, "total": 742, "checksEvaluated": 760 },
"findings": [
{ "id": "meta-description", "label": "Meta description is missing", "status": "fail",
"level": "high", "category": "on-page",
"evidence": "<head> does not contain <meta name=\"description\">",
"fix": "Add a specific description of the page content." }
],
"performance": {
"mobile": { "performance": 0.62, "accessibility": 0.91, "seo": 1 },
"desktop": { "performance": 0.88, "accessibility": 0.91, "seo": 1 }
},
"query": { "primary": "seo audit", "source": "input", "score": 62,
"suggestions": ["seo audit", "website audit", "technical seo check"] },
"unmeasured": ["JavaScript audit"],
"reportUrl": "https://fukamo.com/?url=https%3A%2F%2Fexample.com%2F",
"page": {
"title": "Example — SEO audit", "description": "…", "language": "en",
"canonical": "https://example.com/", "responseTimeMs": 312,
"wordCount": { "main": 144, "total": 881, "pageType": "homepage" }
}
}
}
],
"site": { "addresses": 1, "uniquePages": 1, "meanScore": 68, "score": 68, "cappedBy": null,
"issues": [] }
}| Field | Meaning |
|---|---|
score.fukamo | The main score, as on the website and in the PDF. A weighted mean of score.parts: content weighs most, and the score stands at most 15 points above it. |
score.parts.content | Content: depth, structure, phrase match, freshness and trust. Points are earned, not only lost. |
score.seo, score.geo | HTML scores for SEO and GEO. |
score.html | Overall HTML score, measurements included. |
findings | Only fail and review, worst first. |
summary | The website's counts, each problem once: failed, review, extras (technical extras), passed, notApplicable and unavailable add up to total. checksEvaluated is every check that ran, before duplicates were merged. An HTML rule and the same Lighthouse audit (for example image-alt) are one root cause: when both fail, failed counts them once. |
query | The measured phrase and its source (input, suggested, none). query.score is the match, query.suggestions the website’s suggestions. |
page | Title, description, language, canonical, responseTimeMs, wordCount. |
performance | Lighthouse for mobile and desktop, 0–1. Absent if PageSpeed failed. |
unmeasured | What did not finish. That score part is null — never 0. |
result.url | The address after redirects. pages[].url is the one you sent. |
site.score | The website score. Lower than site.meanScore when a site-wide issue caps it (site.cappedBy). |
- Texts are in English, phrase findings (
query-*) included: a Slovak phrase is matched with Slovak rules and described in English. Quoted page content keeps its language. - Match on
id,statusandlevel— they are never translated. - New fields may appear. Existing ones change only with a new
version.
?format=pdf returns the same report as a PDF, in English.
- Not finished yet:
409 not_ready. - Limit: a quarter of the key’s quota (15 an hour by default), apart from audits.
- Over the limit:
429withretryAfterin seconds.
curl -H "Authorization: Bearer $FUKAMO_API_KEY" \
"https://fukamo.com/api/v1/audits/<id>?format=pdf" \
-o report.pdfErrors
Errors are JSON with error and code. Branch on code, not on the text.
429: waitretryAfterseconds.502/503: retry a few times with growing pauses.
| HTTP | code | When |
|---|---|---|
| 400 | invalid_request | The body is not JSON with a non-empty urls list of public http(s) addresses. |
| 400 | invalid_query | query is not a string or has more than 32 words. |
| 400 | invalid_locale | locale is not en, sk or es. Without it the report is in English. |
| 400 | bad_format | ?format= is something other than json or pdf. |
| 400 | bad_page | With ?format=pdf, ?page= is not the index of a page in this audit. |
| 401 | unauthorized | Missing or wrong key — the same answer on purpose. |
| 404 | not_found | No audit has this id, or it belongs to another key. |
| 409 | not_ready | The PDF was asked for before the page finished; carries status. Wait for complete. |
| 410 | use_async | The old synchronous GET /api/v1/audit is retired. Use POST /api/v1/audits, and update @fukamo/cli and @fukamo/mcp to 0.3.0 or later. |
| 429 | rate_limited | Quota spent, too many PDFs, too many unreachable targets (10 an hour), or the status polled too often; retryAfter in seconds. |
| 429 | queue_full | The key already has 10 pages queued or running; retryAfter in seconds. Collect their results first. |
| 429 | domain_limit | This key already audited as many pages of this website today as it may (1 by default; the owner can raise it in the admin panel); availableUrl is a page it can audit again, pagesPerSite its daily number. |
| 502 | report_failed | The PDF could not be rendered; the JSON result is unaffected. |
| 502 | unreachable | The target cannot be audited (does not exist, refuses, is not public). Carries host. |
| 503 | unavailable | The audit could not be queued because of a fault on our side; retryAfter in seconds. |
| 503 | not_configured | No key is issued on this server. |
Postman
Every call on this page, ready to import. In Postman: Import → both files → environment “Fukamo — produkcia” → your key in apiKey. The files contain no key.
CLI — @fukamo/cli
export FUKAMO_API_KEY=fk_… # from the environment, never as an argument
npx @fukamo/cli audit https://example.com/
npx @fukamo/cli audit https://example.com/ --query "seo audit" --json
npx @fukamo/cli audit https://example.com/ --pdf report.pdf- Runs the full audit and waits (1–4 minutes). Prints the score and the 10 worst findings;
--jsonprints the exactresult. --querysets the phrase.--pdf report.pdfsaves the PDF.- Exit code:
2= failing checks,0= none,1= the call failed. Sofukamo audit "$URL" && deployworks in CI. - With
--pdfthe exit code is0once the file is written — gate CI on--json. fukamo result <id>picks up a running audit without starting a new one.- Needs Node 20+.
FUKAMO_API_URLswitches the server. - Versions below 0.3.0 get
410 use_async— update.
MCP — @fukamo/mcp
audit_website(url, query?)runs the audit and returns the score and the failing checks, each with a fix.- Not done within 45 s? You get the audit id, and
get_audit_result(audit_id)waits for it for free. fukamo_report_pdf(path, url | audit_id, query?)saves the PDF.
{
"mcpServers": {
"fukamo": {
"command": "npx",
"args": ["-y", "@fukamo/mcp"],
"env": { "FUKAMO_API_KEY": "fk_…" }
}
}
}The key goes in env, never into a prompt.
What we keep
- Per audit, for 90 days: key label, time, the audited page address (without “?…”), audit id, status and duration. Never the key, only its fingerprint.
- The result stays on the server under its id. It is not deleted automatically yet.
- Only public http(s) addresses; internal networks are refused.