Content Delivery API Reference

Beta

Anonymous, edge-cached read and form-submission API served directly from each Kanorio site (https://{site}/api/v1, including permanent system hosts https://s-xxx.kanorio.com/api/v1). Generated directly from packages/shared/openapi/delivery.json.

Before you start

  • No API key is required for published content. Browser fetch is supported via CORS allowed origins (max 20 https origins; localhost and 127.0.0.1 are always allowed).
  • Every response carries weak ETag, X-Kanorio-Cache-Tags, X-Request-Id, and X-RateLimit-Limit / X-RateLimit-Remaining / X-RateLimit-Reset headers. Unknown query parameters are stripped before cache-key normalization (max 40 query parameters, each value <= 512 chars).
  • Rate limits apply only on cache misses: 20 req/s per site (burst 100) and 10 req/s per IP (burst 30); draft preview: 2 req/s per IP (burst 20); Customer Bearer (knr_cat_) gated reads: 5 req/s (burst 30). Edge cache hits are unlimited.
  • To read unpublished drafts on your server, send X-Kanorio-Preview: knr_pv_<token> (or ?preview=1 with a server-held PAT) or mount @kanorio/sdk/preview (createPreviewRouteHandler).
  • Collections items and pages return a top-level assets map with CDN URL (replace trailing /public with /w=400|800|1200|1600,fit=scale-down,q=85), width, height, ALT, and 16px WebP LQIP placeholder (trashed, missing, or private-bucket assets are automatically filtered out).
  • Guardrails: max 100 collections/site, 50 fields and 20,000 items/collection, 256 KB fieldData/item, 2 MB response payload, limit <= 100, <= 10 filter conditions, <= 3 sort fields, <= 100 values in in/nin, max 1 match filter (<= 8 words), relative dates now[+-]N<s|m|h|d|w|M|y>, and total=false to skip COUNT(*).
https://{site}/api/v1

Endpoint descriptions match what AI assistants read.

Site Meta, Search & OpenAPI

GET/openapi.json

delivery_openapi

BetaRead-only

OpenAPI 3.1 specification for this site's Content Delivery API

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited
curl
curl -X GET "https://{site}/api/v1/openapi.json"
GET/site

delivery_site

BetaRead-only

Public site meta

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited
curl
curl -X GET "https://{site}/api/v1/site"

Header & Footer Globals

GET/globals/headers/{key}

delivery_globals_header_get

BetaRead-only

Global header navigation by key ('primary' or secondary header key) with siteSettings and assets

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited

Path parameters

  • keystringRequired

Query parameters

  • localestring
curl
curl -X GET "https://{site}/api/v1/globals/headers/{key}"

Pages & Redirects

GET/redirects

delivery_redirects

BetaRead-only

Enabled redirect rules (owner-defined and system 301s from page renames) for the external front end to apply

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited
curl
curl -X GET "https://{site}/api/v1/redirects"
GET/pages

delivery_pages_list

BetaRead-only

Published pages and hierarchy tree (paths, parentSlug, breadcrumbs, SEO)

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited

Query parameters

  • localestring
curl
curl -X GET "https://{site}/api/v1/pages"
GET/pages/{slug}

delivery_page_get

BetaRead-only

One published page by slug (supports 2-level path and 'home' alias) with sections and assets map

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited

Path parameters

  • slugstringRequired

Query parameters

  • localestring
curl
curl -X GET "https://{site}/api/v1/pages/{slug}"
GET/seo/llms.txt

delivery_seo_llms_txt

BetaRead-only

AI / LLM site index (llms.txt) using externalStorefrontUrl as the base URL

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited
curl
curl -X GET "https://{site}/api/v1/seo/llms.txt"
GET/seo/llms-full.txt

delivery_seo_llms_full_txt

BetaRead-only

Full site Markdown content (llms-full.txt) using externalStorefrontUrl as the base URL

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited
curl
curl -X GET "https://{site}/api/v1/seo/llms-full.txt"
GET/seo/sitemap.xml

delivery_seo_sitemap_xml

BetaRead-only

XML sitemap using externalStorefrontUrl as the base URL

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited
curl
curl -X GET "https://{site}/api/v1/seo/sitemap.xml"
GET/seo/robots.txt

delivery_seo_robots_txt

BetaRead-only

robots.txt including AI crawler block rules and Sitemap pointing to externalStorefrontUrl

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited
curl
curl -X GET "https://{site}/api/v1/seo/robots.txt"
GET/seo/indexnow-key.txt

delivery_seo_indexnow_key

BetaRead-only

IndexNow key verification file content; external frontends rewrite `/<key>.txt` to this endpoint so Bing/Naver/Seznam/Yandex can verify ownership of the external storefront domain

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited
curl
curl -X GET "https://{site}/api/v1/seo/indexnow-key.txt"
GET/seo/jsonld

delivery_seo_jsonld

BetaRead-only

Schema.org JSON-LD @graph for a given page path

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited

Query parameters

  • pathstring

    Relative page path starting with / (e.g. /, /about, /blog, /blog/my-post)

curl
curl -X GET "https://{site}/api/v1/seo/jsonld"

Blog

GET/feed

delivery_blog_feed_rss

BetaRead-only

Blog RSS 2.0 feed (respects blog collection accessPolicy)

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited

Query parameters

  • tagstring

    Filter by blog_tags slug

  • authorstring

    Filter by blog_authors slug

  • limitinteger
curl
curl -X GET "https://{site}/api/v1/feed"
GET/feed.atom

delivery_blog_feed_atom

BetaRead-only

Blog Atom 1.0 feed (respects blog collection accessPolicy)

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited

Query parameters

  • tagstring

    Filter by blog_tags slug

  • authorstring

    Filter by blog_authors slug

  • limitinteger
curl
curl -X GET "https://{site}/api/v1/feed.atom"

Collections & Blog Items

GET/collections

delivery_collections_list

BetaRead-only

Collections and their fields (self-describing). System collections also list virtualFields (projection-only fields such as blog segments/toc/contentHtml and taxonomy counts) with scope item|fieldData and a TypeScript type literal.

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited
curl
curl -X GET "https://{site}/api/v1/collections"
GET/collections/{key}/items

delivery_collection_items

BetaRead-only

Published items of a collection

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited

Path parameters

  • keystringRequired

Query parameters

  • filterobject

    Bracket-notation filter conditions (up to 10 conditions combined with AND, plus 2-level OR groups via filter[any][0][field][op]=value). Shorthand filter[field]=value equals filter[field][eq]=value. 15 operators by field type: eq, ne, in, nin (up to 100 comma-separated values), gt, gte, lt, lte, contains, ncontains, match (multi-word substring match on text/rich_text, max 1 per query and 8 words), exists (true|false), has (multi_select/multi_reference), eq_slug (reference), has_slug (multi_reference). Item-level attributes slug, publishedAt, createdAt, updatedAt are also filterable. Date/datetime fields accept relative expressions: now or now[+-]<N><s|m|h|d|w|M|y> (e.g. filter[publishedAt][gte]=now-30d).

  • sortstring

    Comma-separated sort fields (up to 3 fields), prefix with `-` for descending (e.g. `sort=-publishedAt,title`), or bracket form `sort[field]=asc|desc`. Sortable: scalar fields and item attributes (`slug`, `publishedAt`, `createdAt`, `updatedAt`); `rich_text`, `json`, `image`, `gallery`, `file`, and `multi_*` are not sortable.

  • limitinteger

    Page size (1–100, default 20).

  • offsetinteger

    Offset pagination index (prefer `cursor` for deep pagination).

  • cursorstring

    Opaque `nextCursor` token from the previous response.

  • totalboolean

    Pass `total=false` to skip the `COUNT(*)` query and return `total: null` for faster cursor pagination.

  • fieldsstring

    Comma-separated `fieldData` field IDs to project (top-level item metadata `id`, `slug`, `publishedAt`, `updatedAt` are always returned).

  • expandstring

    Comma-separated `reference` or `multi_reference` field IDs (plus `primary_author`, `primary_category` on `blog`) to expand 1 layer into `item.expanded[fieldId]`. Referenced media assets are automatically included in the top-level `assets` map (trashed, missing, or private-bucket assets are filtered out).

  • formatsstring

    Comma-separated blog content formats: markdown, html, plaintext

  • localestring

    Optional locale assertion; must match the site's configured `Website.locale` or returns 400 `UNSUPPORTED_LOCALE`.

  • previewstring

    Pass `preview=1` with `X-Kanorio-Preview: knr_pv_<token>` (or a server-held PAT with `collections:view`) to read unpublished draft `fieldData` (`Cache-Control: no-store`).

curl
curl -X GET "https://{site}/api/v1/collections/{key}/items"
GET/collections/{key}/items/{slug}

delivery_collection_item

BetaRead-only

One published item by slug

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited

Path parameters

  • keystringRequired
  • slugstringRequired

Query parameters

  • fieldsstring
  • expandstring
  • formatsstring

    Comma-separated blog content formats: markdown, html, plaintext

  • previewstring
curl
curl -X GET "https://{site}/api/v1/collections/{key}/items/{slug}"

Forms & Anti-Abuse Challenge

GET/forms/{id}

delivery_form_get

BetaRead-only

Public online form definition (fields, availability, consent)

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited

Path parameters

  • idstringRequired
curl
curl -X GET "https://{site}/api/v1/forms/{id}"
GET/forms/{id}/challenge

delivery_form_challenge

BetaRead-only

Fetch a server-signed anti-spam challenge for form submission

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited

Path parameters

  • idstringRequired
curl
curl -X GET "https://{site}/api/v1/forms/{id}/challenge"
POST/forms/{id}/upload-request

delivery_form_upload_request

Beta

Request a signed upload ticket for a form file attachment field

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited

Path parameters

  • idstringRequired
curl
curl -X POST "https://{site}/api/v1/forms/{id}/upload-request" \
  -H "Content-Type: application/json" \
  -d '{"fieldId":"string","fileName":"string","mimeType":"string","fileSize":1,"_hp":"string","_wp":"string","_co":"string","_ts":1}'
POST/forms/{id}/submit

delivery_form_submit

Beta

Submit answers to an online form

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited

Path parameters

  • idstringRequired
curl
curl -X POST "https://{site}/api/v1/forms/{id}/submit" \
  -H "Content-Type: application/json" \
  -d '{"answers":null,"consent":true,"consentTextId":"string","locale":"string","pagePath":"string","_hp":"string","_challenge":{"nonce":"string","ts":1,"sig":"string"}}'

Products & Catalog

GET/products

delivery_products_list

BetaRead-only

Published products listed in the shop (no deliverable outline)

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited

Query parameters

  • pageinteger
  • limitinteger
  • categorystring
  • localestring
curl
curl -X GET "https://{site}/api/v1/products"
GET/products/{slug}

delivery_products_get

BetaRead-only

One product, including the public outline of its deliverables

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited

Path parameters

  • slugstringRequired

Query parameters

  • localestring
curl
curl -X GET "https://{site}/api/v1/products/{slug}"

Free Preview Units

POST/units/{id}/open

delivery_units_open_preview

Beta

Open a free-preview unit anonymously (isPreview only)

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited

Path parameters

  • idstringRequired
curl
curl -X POST "https://{site}/api/v1/units/{id}/open" \
  -H "Content-Type: application/json" \
  -d '{"mode":"read"}'

Analytics

POST/analytics/pageview

delivery_analytics_pageview

Beta

Record an anonymous pageview from an external storefront (per-IP rate-limited)

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited
curl
curl -X POST "https://{site}/api/v1/analytics/pageview" \
  -H "Content-Type: application/json" \
  -d '{"path":"string","referrer":"string","title":"string","uaCategory":"desktop"}'
POST/analytics/bot-hit

delivery_analytics_bot_hit

Beta

Record an AI crawler visit from an external storefront edge middleware (per-IP rate-limited)

Permission
none
Rate limit
20 misses/s per site (burst 100), 10/s per IP (burst 30); preview 2/s (burst 20); customer token 5/s (burst 30); cache hits unlimited
curl
curl -X POST "https://{site}/api/v1/analytics/bot-hit" \
  -H "Content-Type: application/json" \
  -d '{"botToken":"string","path":"string","userAgent":"string"}'

Error codes

Failures always return an error object with code, message and details. Branch on code; the message wording may change. The table lists every error code and its HTTP status.
Example: the page changed after you read it (409)
{
  "error": {
    "code": "ITEM_NOT_FOUND",
    "message": "Item not found."
  }
}
BAD_REQUEST / QUERY_INVALID / INVALID_BODY / INVALID_PATH / UNKNOWN_BOT
400
UNAUTHORIZED / PREVIEW_UNAUTHORIZED
401
FORBIDDEN / NO_ENTITLEMENT / NOT_YET_RELEASED
403
NOT_FOUND / COLLECTION_NOT_FOUND / ITEM_NOT_FOUND
404
RATE_LIMITED
429
UPSTREAM_UNAVAILABLE
503