SEO metadata
Four routes, two levels: per post (title, description, canonical, indexing, schema type) and site-wide (the defaults and templates every page inherits).
| Method | Path | Scope |
|---|---|---|
GET |
/v1/posts/{reference}/seo |
seo:read |
PATCH |
/v1/posts/{reference}/seo |
seo:write |
GET |
/v1/sites/me/seo |
seo:read |
PATCH |
/v1/sites/me/seo |
seo:write |
Read and write are separate scopes: a key that can read SEO cannot write it. Rate limit: 60 req/min per key.
Body shape
Section titled “Body shape”Both PATCH routes accept either form — the wrapper is optional:
{ "seo": { "title": "…", "description": "…" } }{ "title": "…", "description": "…" }Both GET routes always answer with the wrapped form, { "seo": { … } }, and
an empty object when nothing has been set yet. Keys outside the allowlist for
that level are dropped in silence; a body with no allowlisted key at all is
a 400 whose message lists the accepted keys.
GET /v1/posts/{reference}/seo
Section titled “GET /v1/posts/{reference}/seo”curl -s https://api.canverly.com/v1/posts/01J8ZQ7X4K2N5M9P0R3T6V8W1Y/seo \ -H "Authorization: Bearer ck_live_xxx"{ "seo": { "title": "How to do X — a practical guide", "description": "Step by step, with the pitfalls.", "og_image": "https://cdn.example.com/x-og.png", "twitter_card": "summary_large_image", "no_index": false, "canonical_url": "https://blog.example.com/how-to-do-x", "schema_type": "BlogPosting", "category_slugs": ["guides"], "tag_slugs": ["x"] }}PATCH /v1/posts/{reference}/seo
Section titled “PATCH /v1/posts/{reference}/seo”Allowlisted keys:
| Key | Type | Notes |
|---|---|---|
title |
string | Meta/OG title for this post. |
description |
string | Meta description. |
og_image |
string (URI) | Open Graph image. |
twitter_card |
string | e.g. summary_large_image. |
no_index |
boolean | true keeps the post out of search indexes. |
canonical_url |
string (URI) | Overrides the canonical link. |
schema_type |
string | Article, BlogPosting or NewsArticle. |
category_slugs |
string[] | Drives the category archives. |
tag_slugs |
string[] | Drives the tag archives. |
# 1. read what is therecurl -s https://api.canverly.com/v1/posts/01J8ZQ7X4K2N5M9P0R3T6V8W1Y/seo \ -H "Authorization: Bearer ck_live_xxx"
# 2. send the FULL object back, with your change appliedcurl -X PATCH https://api.canverly.com/v1/posts/01J8ZQ7X4K2N5M9P0R3T6V8W1Y/seo \ -H "Authorization: Bearer ck_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "seo": { "title": "How to do X — a practical guide", "description": "Step by step, with the pitfalls.", "schema_type": "BlogPosting", "category_slugs": ["guides"], "tag_slugs": ["x"], "no_index": true } }'200 OK returns the stored object in the same wrapped shape.
GET /v1/sites/me/seo
Section titled “GET /v1/sites/me/seo”The site-wide defaults, stored under the site’s settings.
curl -s https://api.canverly.com/v1/sites/me/seo \ -H "Authorization: Bearer ck_live_xxx"{ "seo": { "title_template": "%s — Example Blog", "default_title": "Example Blog", "default_description": "Notes on doing X well.", "default_og_image": "https://cdn.example.com/og-default.png", "twitter_card": "summary_large_image", "twitter_handle": "@exampleblog", "robots": "index,follow" }}PATCH /v1/sites/me/seo
Section titled “PATCH /v1/sites/me/seo”Allowlisted keys: title_template, default_title, default_description,
default_og_image, twitter_card, twitter_handle, robots.
This one is merged into the stored site SEO — keys you omit keep their current value, and the rest of the site’s settings blob is untouched.
curl -X PATCH https://api.canverly.com/v1/sites/me/seo \ -H "Authorization: Bearer ck_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "default_description": "Notes on doing X well.", "robots": "index,follow" }'200 OK returns the merged object, wrapped as { "seo": { … } }.
Errors
Section titled “Errors”| Status | error.code |
When |
|---|---|---|
400 |
bad_request |
Body is not a JSON object, or carries no allowlisted key (no updatable SEO fields provided (allowed: …)). |
401 |
unauthorized |
Missing/invalid/revoked key. |
403 |
forbidden |
Key lacks seo:read / seo:write. |
404 |
not_found |
Post routes: no such post on this site (or a slug was passed instead of an id). |
429 |
rate_limited |
Over 60 req/min — honour Retry-After. |
See also
Section titled “See also”- Site settings — everything about the site that is
not SEO.
seois deliberately excluded from that route; it lives here. - Update a post — note that sending
category_slugs,tag_slugsorexcerptthere writes through the same SEO block.