Headers
X-API-Key: wa_your_api_key_here
Query Parameters
| Parameter | Required | Description |
|---|---|---|
| businessUrl | Required | Website URL to audit — example.com or https://example.com |
| businessName | Optional | Name of the business, e.g. Example Inc. Omit it and the audit will detect the name from the site and flag it when it could not be verified. Send one only when you actually know it — a supplied name overrides detection and is treated as confirmed. |
| businessCity | Optional | City for local search analysis, e.g. Seattle, WA. Omit it and the audit will detect the location; when none can be found the questions widen to the country or drop the place entirely, which suits a national or global business but not a local one. |
Example URL
GET /api/audit?businessUrl=example.com&businessName=Example+Inc&businessCity=Seattle
The call is synchronous: it triggers the audit, polls until it finishes, and returns the finished report in one response. Expect it to take a while — the poll runs for up to 3 minutes before giving up with a 504.
Response — 200 OK
{
"success": true,
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"run_id": "8ae42039-b30",
"timestamp": "2026-03-31T12:00:00.000Z",
"duration_ms": 15230,
"audit": {
"run_id": "8ae42039-b30",
"base_url": "https://example.com",
"environment": "production",
"started_at": "2026-03-31T11:59:44.770Z",
"finished_at": "2026-03-31T12:00:00.000Z",
"duration_s": 15.2,
"status": "completed",
"summary": {
"total": 163,
"passed": 154,
"failed": 3,
"warnings": 6,
"errors": 0,
"pass_rate": 94.5
},
"results": [ ... ],
"performance_metrics": { ... },
"ai_visibility": { ... }
}
}audit is the complete report document — the same JSON served at /report/{run_id}/json — not a link to it. results, performance_metrics and ai_visibility are abridged above because they are large; their real contents are described below. platform, total_annual_impact, total_annual_impact_range and total_pages_audited appear only when the audit produced them. total_annual_impact_range is {"low", "high"} with high equal to total_annual_impact — the same estimate published as a category range (the AI-visibility term spans a stated 0.1%–1% influence band).
Shape of an audit.results[] entry
{
"test_id": "f7013b72",
"module": "base",
"name": "AI Visibility Score",
"description": "Business appears in 0/32 AI recommendations (0% visibility)",
"status": "failed",
"severity": "high",
"url": "https://example.com",
"details": "Tested across 4 AI platforms with 8 queries each.",
"recommendation": "Improve your online presence, reviews, and structured data.",
"screenshot": null,
"duration_ms": 0,
"timestamp": "2026-03-31T11:59:45.730Z",
"logs": [],
"has_fix": true,
"impact_pages": 1,
"impact_estimate": 5500
}status is one of passed, failed, warning, skipped, error; severity is one of critical, high, medium, low, info. The has_fix, impact_* and build_time_* keys appear only on findings that carry them.
Fix content is not included in the API payload. has_fix tells you a fix exists, but fix_snippet, fix_filename and fix_instructions are stripped from every result on this path — retrieve the full generated fix files via GET /api/fixes, read them from the signed-in report at report_url, or get ranked advice via GET /api/recommendations.
Shape of an audit.ai_visibility.sources[] entry
{
"domain": "forbes.com",
"answers": 6,
"platforms": ["ChatGPT", "Perplexity", "Claude"],
"ownership": "third_party",
"url": "https://www.forbes.com/advisor/best-password-managers/",
"title": "Best Password Managers Of 2026"
}audit.ai_visibility.sources is the ranked list the report page renders as Where the assistants looked: the cited documents, deduplicated by domain, ordered by how many platforms independently read the domain first (cross-engine agreement), then by how many answers cited it, capped at the top ten. answers counts answers, not citation entries — one answer citing three pages of a site counts once. platforms is listed in a fixed platform order (ChatGPT, Perplexity, Claude, Gemini); any other engine name follows them alphabetically. ownership is one of yours, competitor, third_party; treat competitor rows as context, not as placement targets. url and title name one representative page. url is null when no citation for the domain named a directly linkable page (Gemini often reaches pages through a grounding redirect, which does not count), and title is always empty in that case; a linkable page can also arrive with an empty title when its citation carried none. sources itself is null when no recorded citation named an attributable domain — including answers whose only citations were grounding redirects with no identifiable site — and the key is omitted entirely when the audit holds no readable citation records: AI visibility not scored, deferred by the daily budget, an audit predating citation capture — or, rarely, a server-side ranking failure (logged, never a 5xx). The raw per-answer citations remain available in ai_visibility.all_results[].citations.
Response — 400 Validation Error
{
"success": false,
"error": "Validation failed",
"details": [
"businessUrl is required. Provide the website URL to audit."
]
}Response — 401 Invalid API Key
{
"success": false,
"error": "Invalid API key. Check that your key is correct.",
"reason": "unknown_key"
}A 401 covers four different problems, and reason says which without your having to read error: missing_key (no X-API-Key header), malformed_key (doesn't start with wa_), unknown_key (well-formed, no such key) and revoked_key (the key existed and was turned off). Branch on reason, not on error — the wording is free to change, these values are not. Only revoked_key means access you previously had was withdrawn; the first two mean the key never reached us intact.
Response — 403 Subscription Required
{
"success": false,
"error": "This endpoint requires a Website Auditor Pro subscription.",
"tier": "free"
}Response — 429 Rate Limited
{
"success": false,
"error": "Rate limit exceeded. You can make 10 requests per day.",
"rate_limit": {
"limit": 10,
"remaining": 0,
"resets_at": "2026-03-31T23:59:59.999Z"
}
}Response — 502 Upstream Rejected
{
"success": false,
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"error": "The audit service rejected the request.",
"upstream_status": 500,
"details": "...first 500 characters of the upstream body...",
"duration_ms": 812
}Only upstream 5xx responses become a 502; an upstream 4xx is passed through with its own status. If the audit service accepts the request but returns no run ID, the status is 502 with The audit service did not return a run ID.
Response — 503 Rate Limiting Unavailable
{
"success": false,
"error": "Rate limiting is temporarily unavailable, so requests are paused. Please try again shortly."
}The daily cap fails closed: if the counter can't be read, the audit is refused rather than run uncapped. Sent with a Retry-After: 60 header.
Response — 504 Audit Timed Out
{
"success": false,
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"run_id": "8ae42039-b30",
"error": "The audit did not complete within 3 minutes. The site may be very large. The audit may still be running on website-auditor.io.",
"duration_ms": 180000
}Response — 500 Audit Failed
{
"success": false,
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"run_id": "8ae42039-b30",
"error": "The audit failed. Check website-auditor.io for details.",
"status": "failed",
"duration_ms": 42310
}An unhandled proxy error also returns 500, with An internal error occurred while processing the audit request. and no run_id.