API
API overview
A REST API over HTTPS with JSON in and out. It does what you do in the calendar, and it’s included on every plan.
Base URL
Every endpoint lives under one base URL, and every request must use HTTPS.
https://api.postzora.com/api/public/v1Your first request
- In the app, open Settings, then API keys, and create a key with read and write access.
- Save it as an environment variable called
POSTZORA_API_KEY. - List your channels. You’ll need their ids to schedule posts.
curl https://api.postzora.com/api/public/v1/channels \
-H "Authorization: Bearer $POSTZORA_API_KEY"const response = await fetch('https://api.postzora.com/api/public/v1/channels', {
headers: {
Authorization: `Bearer ${process.env.POSTZORA_API_KEY}`,
},
})
const { data } = await response.json()import os
import requests
response = requests.get(
"https://api.postzora.com/api/public/v1/channels",
headers={"Authorization": f"Bearer {os.environ['POSTZORA_API_KEY']}"},
)
data = response.json()["data"]{
"data": [
{
"id": 12,
"platform": "linkedin",
"name": "Acme",
"username": "acme",
"avatar_url": "https://media.licdn.com/dms/image/acme.png",
"needs_reconnect": false,
"posting_times": ["09:00", "12:30", "17:00"],
"reconnect_by": null,
"created_at": "2026-09-02T10:14:03.000000Z"
},
{
"id": 15,
"platform": "bluesky",
"name": "Acme",
"username": "acme.bsky.social",
"avatar_url": null,
"needs_reconnect": false,
"posting_times": ["10:00", "16:00"],
"reconnect_by": null,
"created_at": "2026-09-02T10:14:03.000000Z"
}
]
}Schedule a post
Send the text once and list the channels it goes to. A channel can have its own text, here a shorter one for Bluesky. Postzora checks every version against that network’s limits before saving, just like the composer does.
curl -X POST https://api.postzora.com/api/public/v1/posts \
-H "Authorization: Bearer $POSTZORA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "New on the blog: how we plan a month of posts in one afternoon.",
"status": "scheduled",
"scheduled_at": "2026-11-02T09:30:00Z",
"targets": [
{ "channel_id": 12 },
{
"channel_id": 15,
"content": "Shorter version for Bluesky."
}
]
}'const response = await fetch('https://api.postzora.com/api/public/v1/posts', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.POSTZORA_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
content: 'New on the blog: how we plan a month of posts in one afternoon.',
status: 'scheduled',
scheduled_at: '2026-11-02T09:30:00Z',
targets: [
{ channel_id: 12 },
{ channel_id: 15, content: 'Shorter version for Bluesky.' },
],
}),
})
const { data } = await response.json()import os
import requests
response = requests.post(
"https://api.postzora.com/api/public/v1/posts",
headers={"Authorization": f"Bearer {os.environ['POSTZORA_API_KEY']}"},
json={
"content": "New on the blog: how we plan a month of posts in one afternoon.",
"status": "scheduled",
"scheduled_at": "2026-11-02T09:30:00Z",
"targets": [
{"channel_id": 12},
{
"channel_id": 15,
"content": "Shorter version for Bluesky.",
},
],
},
)
data = response.json()["data"]The post appears in your calendar straight away, and publishes at 09:30 UTC on 2 November.
pending_approval.Resources
- ChannelsThe accounts connected to your workspace, and the settings each takes.
- PostsDrafts and scheduled posts, with their status on every channel.
- MediaThe images in your media library, ready to attach to posts.
- QueueThe next free posting time for one or more channels.
Conventions
- JSON everywhere. Send
Content-Type: application/jsonwith a body, and expect JSON back. Media uploads are the one exception: they usemultipart/form-data. - Responses are wrapped. The object or list you asked for is under
data. - Times are ISO 8601. Send them with a time zone, like
2026-11-02T09:30:00Zor2026-11-02T10:30:00+01:00. Responses are always in UTC. - Ids are numbers. They are unique within their type, and never reused.
Questions about the API? Email [email protected].