DidYouSEODidYouSEO
Developer Docs

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).

PlanPer minutePer day
Free10100
Starter30500
Pro602,000

Endpoints

All endpoints are prefixed with https://didyouseo.com/api/v1.

Ping

GET/pingno auth

Health check. No authentication required. Useful for testing connectivity from your plugin or script.

Response

{ "ok": true, "version": "1.0" }

Sites

GET/sites

List all websites registered under your account.

Response

{
  "sites": [
    {
      "id": "abc123",
      "url": "https://example.com",
      "label": "My Blog",
      "created_at": "2026-05-11T10:00:00Z"
    }
  ]
}
POST/sites

Register 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

POST/audits

Queue 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"
}
GET/audits/:audit_id

Get 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

GET/sites/:site_id/score

Get 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
    }
  ]
}
GET/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

StatusMeaning
200Success
201Created (new resource)
202Accepted (audit queued)
400Bad request - missing or invalid field
401Unauthorized - missing or invalid API key
402Plan limit reached
404Resource not found
409Conflict - resource already exists
429Rate limit exceeded - check Retry-After header
500Server error

All error responses include a JSON body: { "error": "description" }