Run AI-powered website audits programmatically. The Webmatik API gives your tools (Claude Code, Cursor, custom apps) access to the same audit engine that powers webmatik.ai.
https://webmatik.ai/api/v1X-API-Key headerAll requests require an API key. Generate one in your Account settings. Send it in the X-API-Key header.
curl https://webmatik.ai/api/v1/audit \
-H "X-API-Key: wmk_your_key_here" \
-H "Content-Type: application/json" \
-d '{"url": "example.com"}'⚠️ Treat your API key like a password. Never commit it to source control or expose it in client-side code.
/api/v1/audit— Start a new audit| Field | Type | Required | Description |
|---|---|---|---|
| url | string | required | URL to audit. Protocol optional (auto-prefixed with https://). |
| webhookUrl | string | optional | HTTPS URL to receive a POST when the audit fully completes (including background tasks). See Webhooks section. |
| options | object | optional | Advanced options. Available only to whitelisted accounts. See "Advanced options" below. |
curl -X POST https://webmatik.ai/api/v1/audit \
-H "X-API-Key: wmk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"url": "stripe.com",
"webhookUrl": "https://your-app.com/webhooks/webmatik"
}'{
"auditId": "afb1f027-7bcb-4b1c-abe8-c2a09d246c77",
"status": "processing",
"redirectTo": "/audit/afb1f027-7bcb-4b1c-abe8-c2a09d246c77"
}Save the auditId — you'll need it to fetch results.
If your account is whitelisted, you can pass options to skip parts of the audit for faster, cheaper runs. Useful when you only need core scoring or have your own broken-link checker. Non-whitelisted accounts get a 403 if they try to use these flags.
{
"url": "client.com",
"options": {
"skipChecks": ["broken-links", "geo", "vision"],
"skipInnerPages": true
}
}| Field | Type | Required | Description |
|---|---|---|---|
| options.skipChecks | string[] | optional | Array of check IDs to skip. Allowed: "broken-links", "geo", "vision". |
| options.skipInnerPages | boolean | optional | If true, only the homepage is analyzed (no inner pages). |
Skipped checks won't appear in the response and won't affect the category score (treated as not applicable).
/api/v1/audit/{id}— Fetch audit status and resultAfter starting an audit, poll this endpoint every 5–10 seconds until status === "completed". Audits typically finish in 60–180 seconds. Or use a webhook instead.
processing — audit running. Continue polling.completed — audit finished. Use the result.failed — audit failed (timeout, network error, invalid URL).curl https://webmatik.ai/api/v1/audit/afb1f027-7bcb-4b1c-abe8-c2a09d246c77 \
-H "X-API-Key: wmk_your_key_here"{
"status": "completed",
"auditId": "afb1f027-7bcb-4b1c-abe8-c2a09d246c77",
"url": "https://stripe.com",
"score": 87.6,
"grade": "Excellent",
"siteType": "saas",
"language": "en",
"reportUrl": "https://webmatik.ai/audit/afb1f027-...",
"timestamp": "2026-04-26T12:34:56.000Z",
"summary": {
"conversion": { "score": 92, "passed": 8, "issues": 1, "topIssue": "..." },
"retention": { "score": 78, "passed": 5, "issues": 2, "topIssue": "..." },
"performance":{ "score": 85, "passed": 6, "issues": 2, "topIssue": "..." },
"uiux": { "score": 95, "passed": 10, "issues": 0, "topIssue": null },
"seo": { "score": 88, "passed": 18, "issues": 3, "topIssue": "..." },
"geo": { "score": 75, "passed": 4, "issues": 2, "topIssue": "..." },
"accessibility": { "score": 90, "passed": 9, "issues": 1, "topIssue": "..." },
"security": { "score": 80, "passed": 7, "issues": 2, "topIssue": "..." }
},
"actionPlan": {
"total": 13,
"critical": [
{
"category": "performance",
"title": "Reduce mobile LCP",
"description": "Largest Contentful Paint is 4.2s on mobile",
"howToFix": "Compress hero images and enable lazy loading...",
"effort": "medium",
"impact": "high"
}
],
"important": [...],
"niceToHave": [...]
},
"seo": {
"estimatedTraffic": 12500000,
"totalKeywords": 89500,
"topKeywords": [...]
},
"geoVisibility": {
"score": 75,
"promptsChecked": 16,
"mentionedIn": 12,
"citedIn": 8,
"providers": [...],
"queries": [...]
},
"innerPages": [...]
}{
"status": "processing",
"auditId": "afb1f027-...",
"url": "https://stripe.com",
"geoStatus": "pending",
"visionStatus": "complete",
"partialScore": 82.4,
"message": "Audit is finalizing background analysis..."
}/api/v1/audit/{id}/pdf— Download audit report as PDFReturns the same audit report as a PDF binary. Useful for sending reports to clients, archiving, or attaching to tickets. Available on all paid plans.
Content-Type: application/pdfContent-Disposition: attachment; filename="webmatik-{domain}-{grade}-{score}.pdf"curl -o report.pdf https://webmatik.ai/api/v1/audit/{id}/pdf \
-H "X-API-Key: $WEBMATIK_API_KEY"Instead of polling, pass a webhookUrl when starting an audit. We'll POST the full result to that URL when the audit completes (including background tasks like AI vision and GEO).
| Field | Type | Required | Description |
|---|---|---|---|
| X-Webmatik-Event | string | optional | Event type. Currently always "audit.completed". |
| X-Webmatik-Signature | string | optional | HMAC-SHA256 signature: "sha256=<hex>". Verify using your API key hash as secret. |
| Content-Type | string | optional | application/json |
import crypto from 'crypto';
function verifySignature(rawBody, signatureHeader, apiKeyHash) {
const expected = crypto
.createHmac('sha256', apiKeyHash)
.update(rawBody)
.digest('hex');
return signatureHeader === `sha256=${expected}`;
}Same structure as the GET endpoint, with an additional event field.
{
"event": "audit.completed",
"auditId": "afb1f027-...",
"url": "https://stripe.com",
"score": 87.6,
"grade": "Excellent",
"summary": { ... },
"actionPlan": { ... }
}| Field | Type | Required | Description |
|---|---|---|---|
| 400 | Bad Request | optional | Invalid URL, malformed JSON, or invalid webhookUrl |
| 401 | Unauthorized | optional | Missing or invalid X-API-Key header |
| 403 | Forbidden | optional | API access requires Starter or Growth plan |
| 404 | Not Found | optional | Audit ID does not exist or belongs to different user |
| 429 | Too Many Requests | optional | Monthly audit limit reached for your plan |
| 500 | Server Error | optional | Unexpected server error — retry with exponential backoff |
{ "error": "Description of what went wrong" }There are no per-second rate limits, but the total number of audits per month is capped by your plan:
When you exceed the monthly limit, requests return 429 until the next billing cycle.
const API_KEY = process.env.WEBMATIK_API_KEY;
const BASE = 'https://webmatik.ai/api/v1';
async function audit(url) {
// Start audit
const start = await fetch(`${BASE}/audit`, {
method: 'POST',
headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ url }),
});
const { auditId } = await start.json();
// Poll until complete
while (true) {
await new Promise(r => setTimeout(r, 8000));
const res = await fetch(`${BASE}/audit/${auditId}`, {
headers: { 'X-API-Key': API_KEY },
});
const result = await res.json();
if (result.status === 'completed') return result;
if (result.status === 'failed') throw new Error('Audit failed');
}
}
const result = await audit('stripe.com');
console.log(`Growth Score: ${result.score}/100 (${result.grade})`);
console.log(`Critical issues: ${result.actionPlan.critical.length}`);import os, time, requests
API_KEY = os.environ['WEBMATIK_API_KEY']
BASE = 'https://webmatik.ai/api/v1'
H = {'X-API-Key': API_KEY}
def audit(url):
r = requests.post(f'{BASE}/audit', headers={**H, 'Content-Type': 'application/json'}, json={'url': url})
audit_id = r.json()['auditId']
while True:
time.sleep(8)
res = requests.get(f'{BASE}/audit/{audit_id}', headers=H).json()
if res['status'] == 'completed':
return res
if res['status'] == 'failed':
raise Exception('Audit failed')
result = audit('stripe.com')
print(f"Score: {result['score']}/100 ({result['grade']})")curl -X POST https://webmatik.ai/api/v1/audit \
-H "X-API-Key: $WEBMATIK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "stripe.com",
"webhookUrl": "https://your-app.com/webhooks/webmatik"
}'Need help? Contact us or check the step-by-step tutorial for using the API with AI coding tools.