Developer API
Automate website audits
Authenticate with your API key, submit a URL, get results via webhook or polling. Available on the Agency plan.
Base URL
https://seolint.dev/api/v1Authentication
Pass your API key as a Bearer token in every request. Generate your key from the API dashboard.
curl -X POST https://seolint.dev/api/v1/scan \
-H "Authorization: Bearer sl_your_api_key" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com"}'/scan
Submit a URL for scanning. Returns immediately. Results arrive via webhook or polling.
curl -X POST https://seolint.dev/api/v1/scan \
-H "Authorization: Bearer sl_your_api_key" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com"}'Response
{
"scanId": "3f2a1b4c-...",
"pollUrl": "https://seolint.dev/api/v1/scan/3f2a1b4c-...",
"reportUrl": "https://seolint.dev/report/3f2a1b4c-..."
}/scan/:id
Poll until status is complete. Recommended interval: 3 seconds.
curl https://seolint.dev/api/v1/scan/3f2a1b4c-...Response (complete)
{
"status": "complete",
"issues": [
{
"id": "missing-title",
"category": "seo",
"severity": "critical",
"title": "Missing page title",
"description": "The page has no <title> tag...",
"fix": "Add a unique <title> tag under 60 characters."
}
],
"markdown": "# Website Audit: https://example.com\n\n..."
}/scan/:id/markdown
Returns the report as a raw .md file. Ideal for piping into AI agents, Claude, or Cursor.
curl https://seolint.dev/api/v1/scan/3f2a1b4c-.../markdownWebhooks
Set a webhook URL in your API dashboard and we'll POST results to your endpoint when each scan finishes. No polling needed.
Events
scan.completedScan finished successfullyscan.failedScan encountered an errorPayload
{
"event": "scan.completed",
"scan_id": "3f2a1b4c-...",
"url": "https://example.com",
"status": "complete",
"issue_count": 8,
"critical_count": 2,
"report_url": "https://seolint.dev/report/3f2a1b4c-...",
"timestamp": "2026-03-30T12:00:00.000Z"
}Verifying signatures
Every request includes an X-SEOLint-Signature header. Verify it with your signing secret (shown in the API dashboard) to confirm the request came from us.
// Node.js verification example
const crypto = require('crypto')
function isValidWebhook(rawBody, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody) // rawBody must be the raw string, not parsed JSON
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
)
}
// Express
app.post('/webhooks/scanner', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-seolint-signature']
if (!isValidWebhook(req.body, sig, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature')
}
const event = JSON.parse(req.body)
console.log(event.event, event.url, event.critical_count)
res.sendStatus(200)
})Important: Always read the raw request body before parsing JSON. Parsing then re-stringifying changes the byte sequence and breaks the HMAC check.