API keys
An API key lets your own code talk to Publiz: schedule posts from your CMS, pull assets into a build, kick off an AI job from a script. A key belongs to one workspace and carries only the scopes you give it.
Want an AI assistant like Claude to use Publiz for you? The same key works there too, see Connect an AI assistant.
Find them under Developer then API Keys in the sidebar.


Create one
Section titled “Create one”-
Press Create API Key.
-
Give it a name you’ll recognise later. It’s the only way to tell keys apart once they’re created, so “Zapier automation” beats “key 2”.
-
Tick the resources it needs. Anything your plan can’t grant is greyed out with Not on your plan.


-
Optionally set an expiry date. Leave it empty and the key works until you revoke it. Setting one is worth it for anything short-lived, like a migration script. You can move or remove the date later with Edit.
-
Press Create. The key appears once.
Keys look like publiz_sk_ followed by 64 characters. After you create one, the list only shows
the start of it as a prefix, like publiz_sk_7f3a…, which is enough to work out which key a log
line belongs to. Keys made before the new format start with pk_ and keep working as they are.
Scopes
Section titled “Scopes”A key only reaches what you ticked. Give each integration the least it needs — a key that only publishes posts can’t also delete your media.
| Scope | What it covers |
|---|---|
| Posts | Read, create, update, delete, publish and retry posts |
| Tags | List, create, rename and delete your post tags, so you can put them on posts |
| Signatures | List, write, edit and delete signatures, so you can pick one per account or per post |
| Personas | List, create, edit, duplicate and delete personas. Deleting one needs a key the workspace owner made |
| Ideas | Read and manage content ideas, including generating them |
| Media | List, download, upload and delete assets |
| Webhooks | Create, edit, test and delete webhooks, and read or retry their deliveries |
| Usage | Read-only. Plan usage and your AI and X credit balances |
| Accounts | List the connected accounts. Read-only, you can’t connect one over the API |
| Members | List members and invitations, invite someone by email, remove a member |
| Analytics | Read-only. Workspace totals and per-post analytics |
| AI | Run the AI generation tools |
| Insights | Read-only. Best times to post, daily engagement, post ranking and follower history |
| Channels | See each channel’s connection health, change a channel’s settings (which signature, TikTok privacy, X options), disconnect it |
| Automation | Manage comment auto-reply rules and the reply templates they send, and see what they did |
Read-only scopes
Section titled “Read-only scopes”Any scope that can change something can also be given read only. Tick the scope, then switch
on Read only under Access in the create or edit dialog. The key can then list and fetch, but
anything that creates, changes or deletes gets a 403 with the code READ_ONLY_KEY. It’s a good
fit for dashboards and reporting scripts that should never touch your content.
A few things worth knowing:
- Usage, Analytics, Insights and Accounts are read-only anyway, so they don’t get a switch.
- Pricing an AI job with its
/estimatepath counts as a read, so a read-only AI key can still check what something would cost. - The rule is the same on every route, and the scope shows as read only in the key list.
Member endpoints need a key created by the workspace owner, the same rule the dashboard follows.
Channels can’t be connected over the API, and the API can’t make Publiz re-sync posts or refresh stats from a platform. Those stay in the dashboard, so a runaway script can’t burn through the platform’s rate limits for everyone.
Which scopes you can grant depends on your plan’s API access level, the same name you see on the pricing page. Each level includes everything in the one before it.
| Plan | API access | Keys | Scopes available |
|---|---|---|---|
| Free | Posts + usage | 1 | Posts, Usage, Signatures |
| Starter | Standard | 1 | the above, plus Tags, Personas, Ideas, Media and Webhooks |
| Creator | Standard | 1 | the above, plus Tags, Personas, Ideas, Media and Webhooks |
| Pro | Advanced | 3 | the above, plus Accounts, Members and Analytics |
| Agency | Full | 5 | everything, including AI, Insights, Channels and Automation |
If you move to a plan with a lower level, your keys keep working. The scopes the new plan doesn’t include just stop being honoured, and the edit dialog marks them Kept from a previous plan. Move back up and they work again, no need to recreate anything.
Webhooks are part of the paid plans. On Free you can post and manage signatures over the API, but you can’t create webhooks.
A scope opens the door, but the feature’s own plan limits still apply. Tags start on Starter, and each plan caps how many personas and reply templates a workspace can have, so going over answers the same way the dashboard would.
The page shows where you stand, like “2 of 3 keys used”. At the limit, Create API Key is disabled until you revoke one.
Using the key
Section titled “Using the key”Send it as a Bearer token in the Authorization header. The API lives at
https://api.publiz.app/v1.
curl https://api.publiz.app/v1/me \ -H "Authorization: Bearer publiz_sk_your_key_here"An X-API-Key: publiz_sk_your_key_here header works too, if your tool makes that easier. Pick
one, there’s no difference.
/me is the useful first call: it tells you which workspace the key belongs to and which scopes it
holds, without needing any scope itself.
Creating a post looks like this:
curl -X POST https://api.publiz.app/v1/posts \ -H "Authorization: Bearer publiz_sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{"content": "Fresh batch just landed.", "platforms": ["bluesky"]}'The full endpoint list, with the scope each one needs, is in the API reference. Official TypeScript, Go and Dart SDKs are coming soon, so for now you call the REST API directly.
One thing a key can’t do is manage API keys. Creating, resetting and revoking them all need a signed-in user, so a key can never mint another key.
Rate limits
Section titled “Rate limits”There are two kinds of limit, and a request has to fit both.
Daily limit
Section titled “Daily limit”Each plan comes with a number of requests per workspace, per day. Every key in the workspace draws from the same pool, and the count starts again at 00:00 UTC.
| Plan | Requests per day |
|---|---|
| Free | 100 |
| Starter | 200 |
| Creator | 1,000 |
| Pro | 10,000 |
| Agency | 50,000 |
Every response tells you where you stand:
| Header | Meaning |
|---|---|
X-RateLimit-Limit |
Your plan’s daily limit |
X-RateLimit-Remaining |
How many requests are left today |
X-RateLimit-Reset |
When the count resets, as a Unix timestamp |
Go past the limit and you get a 429 with the code DAILY_LIMIT_REACHED, plus a Retry-After
header with the seconds until midnight UTC. GET /v1/workspace/usage also returns the same numbers
under apiRequests, if your key has the Usage scope.
Per-minute limits
Section titled “Per-minute limits”These are per key, per minute, and they differ by the kind of call:
| Kind of request | Per minute |
|---|---|
| Reads, including polling a job | 600 |
| Writes to posts and ideas | 60 |
| Uploads | 30 |
| AI generation | 10 |
Generation is the tight one because every job spends credits and hits a provider. If you’re batching, put a small delay between jobs rather than firing them all at once.
Retrying safely
Section titled “Retrying safely”Networks drop requests. If a POST times out, you can’t tell whether Publiz got it, and sending
it again might create the post twice. An Idempotency-Key header fixes that.
curl -X POST https://api.publiz.app/v1/posts \ -H "Authorization: Bearer publiz_sk_your_key_here" \ -H "Idempotency-Key: 4f1c2b9e-order-1042" \ -H "Content-Type: application/json" \ -d '{"content": "Fresh batch just landed.", "platforms": ["bluesky"]}'Use any unique string up to 255 characters, like a UUID or your own order id. Then:
- The first request runs as normal.
- Sending the same key with the same body within 24 hours gives you the first response back, with
an
Idempotent-Replayed: trueheader, and nothing runs twice. - The same key with a different body gets a
422. A new request needs a new key. - If the first request is still running, a repeat gets a
409. Wait a moment and try again. - Server errors (
5xx) and rate-limit responses aren’t stored, so you can retry those with the same key.
It works on every POST. Estimates ignore it, since they don’t change anything.
Request logs
Section titled “Request logs”The Logs tab on the API Keys page lists every request made with the workspace’s keys, newest first: when it happened, which key, the method and endpoint, the status, how long it took and the IP it came from. Filter by key, status (2xx, 4xx, 5xx), method or date, and a replayed request is marked Replayed.
Only the details of each request are kept, never what was sent or returned, so your content and tokens don’t end up in a log.
How long entries are kept depends on your plan:
| Plan | Kept for |
|---|---|
| Free | 1 day |
| Starter | 3 days |
| Creator | 7 days |
| Pro | 30 days |
| Agency | 90 days |
Rotating and revoking
Section titled “Rotating and revoking”Each row has three actions:
- Edit (pencil) changes which scopes a key holds and when it expires, without changing the key itself. Nothing needs redeploying. Pick No expiry to take the date off.
- Reset (arrow) issues a new key for the same row, keeping the name and scopes. The old key stops working immediately, and the new one is shown once, same as at creation. This is what to use if a key leaks.
- Revoke (bin) deletes the key for good. It can’t be undone.
Reset and revoke only show up for the workspace owner.
The Usage column shows how many requests each key made today and when it was last used. A key that hasn’t been used in months is usually safe to revoke. A key past its expiry date is marked Expired.
When it doesn’t work
Section titled “When it doesn’t work”missing API key: no key reached us. Check the header spelling, that a Bearer token has the
word Bearer and a space in front, and that your client isn’t dropping headers on a redirect.
unauthorized: invalid api key: the key is wrong, or it was reset or revoked. Compare the
prefix in the error with the prefixes on this page.
unauthorized: api key expired: it had an expiry date and that date has passed. Open Edit
and move the date or pick No expiry, and the same key works again.
A 429 with DAILY_LIMIT_REACHED: the workspace has used today’s requests. They come back at
00:00 UTC, and Retry-After tells you how long that is. If you hit it often, the next plan up has a
bigger allowance.
A 403 with READ_ONLY_KEY: the key holds that scope read-only. Edit the key and switch Read
only off for that scope, or use a different key for writes.
A 403 mentioning the resource: the key is valid but doesn’t hold that scope. Edit it and tick
the resource, assuming your plan allows it.
“the … scope is not enabled on your plan”: different problem, and the wording is deliberate. The key does hold that scope, but your plan no longer covers it. This is what you see after a downgrade: the key keeps its scopes on the page, marked Kept from a previous plan, and they stop working until you upgrade again.
A 404 on something you expected to exist: check the
API reference for the exact path.
A 403 on webhooks or ideas on a free plan. Free keys can work with posts and read usage.
Everything else, webhooks included, starts on Starter.