API Reference
The DidYouSEO REST API lets you trigger audits, register sites, and pull SEO scores from any app or script. Base URL: https://didyouseo.com/api/v1
Getting Started
Generate an API key from your dashboard under Settings → API Keys. Keys start with dys_ and are shown only once on creation - store it somewhere safe.
Then make your first request:
curl https://didyouseo.com/api/v1/ping \ -H "Authorization: Bearer dys_your_key_here"
Authentication
Pass your API key in the Authorization header as a Bearer token on every request (except /ping):
Authorization: Bearer dys_a1b2c3d4e5f6...
Invalid or missing keys return 401 Unauthorized.
Rate Limits
Rate limits are applied per API key. When you exceed a limit the API returns 429 Too Many Requests with a Retry-After header (seconds to wait).
| Plan | Per minute | Per day |
|---|---|---|
| Free | 10 | 100 |
| Starter | 30 | 500 |
| Pro | 60 | 2,000 |
Endpoints
All endpoints are prefixed with https://didyouseo.com/api/v1.
Ping
/pingno authHealth check. No authentication required. Useful for testing connectivity from your plugin or script.
Response
{ "ok": true, "version": "1.0" }Sites
/sitesList all websites registered under your account.
Response
{
"sites": [
{
"id": "abc123",
"url": "https://example.com",
"label": "My Blog",
"created_at": "2026-05-11T10:00:00Z"
}
]
}/sitesRegister a new website. Returns 409 if the URL is already registered to your account.
Request body
{
"url": "https://example.com",
"label": "My Blog"
}Response
{
"id": "abc123",
"url": "https://example.com",
"label": "My Blog",
"created_at": "2026-05-11T10:00:00Z"
}Audits
/auditsQueue a single-page SEO audit. Responds immediately with a queued status. The audit runs in the background - poll GET /audits/:id or supply a webhook_url to receive the result automatically.
Request body
{
"url": "https://example.com/blog/my-post",
"site_id": "abc123",
"webhook_url": "https://example.com/wp-json/didyouseo/v1/webhook"
}Response
{
"audit_id": "audit_a1b2c3d4",
"status": "queued",
"url": "https://example.com/blog/my-post"
}/audits/:audit_idGet the status and results of a specific audit. Status is one of: queued, running, complete, failed.
Response
// While running:
{ "audit_id": "audit_a1b2c3d4", "status": "running", "url": "..." }
// When complete:
{
"audit_id": "audit_a1b2c3d4",
"status": "complete",
"url": "https://example.com/blog/my-post",
"score": 74,
"critical_count": 3,
"warning_count": 5,
"good_count": 12,
"completed_at": "2026-05-11T10:05:00Z",
"checks": [ ... ]
}Scores
/sites/:site_id/scoreGet the latest overall score and top issues for a registered site. Useful for dashboard widgets.
Response
{
"site_id": "abc123",
"url": "https://example.com",
"score": 81,
"total_pages": 24,
"total_criticals": 8,
"total_warnings": 15,
"last_audited_at": "2026-05-11T08:00:00Z",
"top_issues": [
{
"check_name": "Missing meta description",
"status": "critical",
"affected_pages": 6
}
]
}/pages/score?url=...&site_id=...Get the audit score for a specific page URL. Returns 404 if the page has not been audited yet. Useful for showing per-post scores in a CMS.
Response
{
"url": "https://example.com/blog/my-post",
"score": 68,
"critical_count": 2,
"warning_count": 3,
"last_audited_at": "2026-05-11T08:00:00Z",
"report_url": "https://didyouseo.com/dashboard/site-audit"
}Webhooks
Supply a webhook_url when triggering an audit and we will POST the result to your endpoint once complete - no polling needed.
Payload
{
"event": "audit.complete",
"audit_id": "audit_a1b2c3d4",
"url": "https://example.com/blog/my-post",
"score": 74,
"critical_count": 3,
"warning_count": 5,
"good_count": 12,
"completed_at": "2026-05-11T10:05:00Z"
}Signature verification
Every webhook includes an X-DidYouSEO-Signature header. Verify it to confirm the payload came from us:
// PHP example
$body = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $body, YOUR_API_KEY_PREFIX);
$received = $_SERVER['HTTP_X_DIDYOUSEO_SIGNATURE'];
if (!hash_equals($expected, $received)) {
http_response_code(401);
exit;
}Error Codes
| Status | Meaning |
|---|---|
| 200 | Success |
| 201 | Created (new resource) |
| 202 | Accepted (audit queued) |
| 400 | Bad request - missing or invalid field |
| 401 | Unauthorized - missing or invalid API key |
| 402 | Plan limit reached |
| 404 | Resource not found |
| 409 | Conflict - resource already exists |
| 429 | Rate limit exceeded - check Retry-After header |
| 500 | Server error |
All error responses include a JSON body: { "error": "description" }