SEO API
REST endpoints for the SEO module — projects, keywords, audits, backlinks, recommendations.
Endpoints
| Method | Path | Purpose |
|---|---|---|
| GET | /api/seo/projects | List projects. |
| POST | /api/seo/projects | Create. |
| GET/PATCH/DELETE | /api/seo/projects/{id} | CRUD. |
| GET | /api/seo/projects/{id}/keywords | List tracked keywords. |
| POST | /api/seo/projects/{id}/keywords | Add a keyword. |
| GET | /api/seo/projects/{id}/audits | List audits ordered by date desc. |
| POST | /api/seo/projects/{id}/audits/run | Trigger a fresh audit (PageSpeed + crawl). |
| GET | /api/seo/projects/{id}/backlinks | List backlinks. Filter: status. |
| POST | /api/seo/projects/{id}/backlinks | Add a backlink (manual import). |
| GET | /api/seo/projects/{id}/recommendations | List open recommendations. |
| PATCH | /api/seo/projects/{id}/recommendations/{recId} | Update status (open/in_progress/done/dismissed). |
| GET | /api/seo/keywords/{id}/history | Rank history sparkline data. |
Key entity shapes
Keyword
{
"id": "uuid",
"project_id": "uuid",
"keyword": "string",
"search_volume": 0,
"current_rank": 0,
"previous_rank": 0,
"status": "tracking | paused | lost"
}
Audit
{
"id": "uuid",
"project_id": "uuid",
"status": "running | completed | failed",
"pagespeed_mobile": 0,
"pagespeed_desktop": 0,
"issues": [{ "type": "string", "severity": "string", "details": "string" }],
"ran_at": "iso-8601"
}
Permissions
Seeded slugs (per the RBAC seed): seo.audits.read|create,
seo.keywords.read|create|update|delete. Project / backlink /
recommendation surfaces gate on seo.audits.* + seo.keywords.*
overlays; there are no separate seeded slugs for them.
Events
The SEO module does not currently publish to the platform event bus. Audit completions, rank changes, and backlink-status changes surface in the in-app activity feed but are not emitted as webhook events. (Tracked as roadmap — webhook coverage will land in a later phase.)
Crons (platform-side)
Three crons keep SEO data fresh:
seo-keyword-ranks— daily, SerpApi-backed (SERPAPI_API_KEY), per-tenant cap. Returnsprovider: 'unconfigured'and silently skips when the key isn't set.seo-audit-rerun— weekly, Google PageSpeed Insights-backed (PSI_API_KEYfor higher quota, optional). 20 projects/run.seo-backlinks-verify— weekly, plain HTML fetch + anchor check. No third-party key required.
Honor the per-cron disable flags (CRON_SEO_RANKS_ENABLED=false,
etc.) to opt out individually.
OpenAPI
Full schema at /api/openapi.json.