Skip to content

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.

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.

Janela do terminal
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"]
}
}

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.
Janela do terminal
# 1. read what is there
curl -s https://api.canverly.com/v1/posts/01J8ZQ7X4K2N5M9P0R3T6V8W1Y/seo \
-H "Authorization: Bearer ck_live_xxx"
# 2. send the FULL object back, with your change applied
curl -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.

The site-wide defaults, stored under the site’s settings.

Janela do terminal
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"
}
}

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.

Janela do terminal
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": { … } }.

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.
  • Site settings — everything about the site that is not SEO. seo is deliberately excluded from that route; it lives here.
  • Update a post — note that sending category_slugs, tag_slugs or excerpt there writes through the same SEO block.