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. |
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.
/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..."
}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.