Skip to content

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.

The API Keys page listing two keys with their prefix, scopes, expiry and last used date.The API Keys page listing two keys with their prefix, scopes, expiry and last used date.
  1. Press Create API Key.

  2. 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”.

  3. Tick the resources it needs. Anything your plan can’t grant is greyed out with Not on your plan.

    The Create API Key dialog with a name field, resource checkboxes, and an optional expiry date.The Create API Key dialog with a name field, resource checkboxes, and an optional expiry date.
  4. 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.

  5. 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.

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

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 /estimate path 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.

Send it as a Bearer token in the Authorization header. The API lives at https://api.publiz.app/v1.

Terminal window
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:

Terminal window
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.

There are two kinds of limit, and a request has to fit both.

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.

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.

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.

Terminal window
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: true header, 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.

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

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.

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.