Publish & edit
POST /v1/posts creates and publishes; PATCH /v1/posts/{id} edits or re-dates. See the publishing guide.
The Canverly Public API lets any external system read and manage a Canverly site over plain HTTPS + JSON: publish and edit posts, upload media, set SEO, change site settings, read audience numbers and form submissions, and move content in and out in bulk. No-code autoposters, custom CMS bridges, CI pipelines and AI agents all use the same surface. Chimpee is simply the first integration we documented; nothing here is specific to it.
https://api.canverly.comAuthorization: Bearer ck_… (one key per site); the public archive
also reads with a browser-safe pk_… key — see keys & scopes/openapi.json · llms.txtPublish & edit
POST /v1/posts creates and publishes; PATCH /v1/posts/{id} edits or re-dates. See the publishing guide.
Read the archive
GET /v1/posts is a keyset-paginated public feed with filters and since for incremental sync.
Media, SEO & settings
Upload assets, set SEO per post and site, change site settings.
Audience & bulk
Read analytics and leads; export or import in NDJSON/CSV.
Twenty-two operations. Full detail — parameters, bodies, responses, errors — in the API reference.
| Area | Operations | Scopes |
|---|---|---|
| Posts | GET /v1/posts · POST /v1/posts · GET /v1/posts/{reference} · PATCH /v1/posts/{id} |
posts:read, posts:write |
| Post types | GET /v1/post-types |
post_types:read |
| SEO | GET/PATCH /v1/posts/{reference}/seo · GET/PATCH /v1/sites/me/seo |
seo:read, seo:write |
| Media | GET/POST /v1/media · GET/DELETE /v1/media/{id} |
media:read, media:write |
| Site | GET /v1/sites/me · GET/PATCH /v1/sites/me/settings |
sites:read, site:read, site:write |
| Analytics | GET /v1/analytics/summary · GET /v1/analytics/posts |
analytics:read |
| Leads | GET /v1/leads · GET /v1/leads/{id} |
leads:read |
| Bulk | GET /v1/export · POST /v1/import |
export, import |
1. Get a key. In the admin, open Configurações → Integrações API and generate a key for your site. Copy the ck_… secret — it is shown once. See Authentication.
2. Publish a post. Send one POST /v1/posts with a title, a block body, and status: "published":
curl -X POST https://api.canverly.com/v1/posts \ -H "Authorization: Bearer ck_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "title": "Hello from the API", "status": "published", "category_slugs": ["news"], "blocks_json": { "v": 1, "blocks": [ { "id": "p1", "type": "paragraph", "text": "Published straight from the <strong>Canverly API</strong>." } ] } }'import requests
r = requests.post( "https://api.canverly.com/v1/posts", headers={"Authorization": "Bearer ck_live_xxx"}, json={ "title": "Hello from the API", "status": "published", "category_slugs": ["news"], "blocks_json": { "v": 1, "blocks": [ {"id": "p1", "type": "paragraph", "text": "Published straight from the <strong>Canverly API</strong>."}, ], }, }, timeout=20,)r.raise_for_status()print(r.json()) # {'id': '01K…', 'slug': 'hello-from-the-api', 'status': 'published', ...}const res = await fetch("https://api.canverly.com/v1/posts", { method: "POST", headers: { Authorization: "Bearer ck_live_xxx", "Content-Type": "application/json", }, body: JSON.stringify({ title: "Hello from the API", status: "published", category_slugs: ["news"], blocks_json: { v: 1, blocks: [ { id: "p1", type: "paragraph", text: "Published straight from the <strong>Canverly API</strong>." }, ], }, }),});if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);console.log(await res.json());<?php$ch = curl_init("https://api.canverly.com/v1/posts");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer ck_live_xxx", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "title" => "Hello from the API", "status" => "published", "category_slugs" => ["news"], "blocks_json" => [ "v" => 1, "blocks" => [ ["id" => "p1", "type" => "paragraph", "text" => "Published straight from the <strong>Canverly API</strong>."], ], ], ]),]);$body = curl_exec($ch);echo $body, "\n";require "net/http"require "json"
uri = URI("https://api.canverly.com/v1/posts")res = Net::HTTP.post( uri, { title: "Hello from the API", status: "published", category_slugs: ["news"], blocks_json: { v: 1, blocks: [ { id: "p1", type: "paragraph", text: "Published straight from the <strong>Canverly API</strong>." }, ], }, }.to_json, "Authorization" => "Bearer ck_live_xxx", "Content-Type" => "application/json",)puts res.bodypackage main
import ( "bytes" "fmt" "io" "net/http")
func main() { body := []byte(`{ "title": "Hello from the API", "status": "published", "category_slugs": ["news"], "blocks_json": {"v":1,"blocks":[ {"id":"p1","type":"paragraph","text":"Published straight from the <strong>Canverly API</strong>."} ]} }`) req, _ := http.NewRequest("POST", "https://api.canverly.com/v1/posts", bytes.NewReader(body)) req.Header.Set("Authorization", "Bearer ck_live_xxx") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.Status, string(out))}3. You get back the post. A 201 Created with the id, slug and status:
{ "id": "01K…", "slug": "hello-from-the-api", "status": "published", "public_url": null }The post is now live at https://<your-site-domain>/<slug>.