Skip to main content
PublishBuddy supports three publish_type values. Picking the right one depends on the use case. All examples below create one post; the posts array takes up to 10 items (one per target profile), and every post created by the same request shares a group_id.

Publish now

Useful for breaking news, manual triggers, or scripts that run on cron and publish immediately.
The handoff to the social network happens asynchronously. status moves to publishing within seconds, then published.

Schedule for a fixed datetime

When you know exactly when a post should go live, set scheduled_for (ISO 8601):
The post is created with status: waiting. PublishBuddy’s workers pick it up at the scheduled minute, transition it to publishing, and push to the network. Posts created through the API can be scheduled at most 90 days ahead.

How the datetime is interpreted

  1. An explicit offset wins: 2026-12-25T09:00:00Z is 09:00 UTC, 2026-12-25T09:00:00+11:00 is 09:00 Sydney time.
  2. A bare datetime plus a timezone field (IANA name) is interpreted in that timezone:
  1. A bare datetime with no timezone is UTC.
The account’s timezone setting never affects API scheduling, and responses always report scheduled_for in UTC.

Editing, rescheduling, or cancelling

While the post has not been published (draft, needs_approval, waiting, or failed):
  • Change content: PUT /posts/{post} with content, media_ids, and so on.
  • Move the time: PUT /posts/{post} with a new scheduled_for (same interpretation rules).
  • Change the mode: PUT /posts/{post} with publish_type. For example, switch a queued post to a fixed time (include scheduled_for), or push a scheduled post out immediately with "publish_type": "now".
  • Cancel: DELETE /posts/{post}.
Once the worker picks the post up (status publishing), rescheduling returns 422, although you can still cancel it. Once it is published, updates return 403 and deletes return 409 Conflict. See Post state errors.

Queue using timeslots

Every profile has a configurable weekly queue: a set of recurring timeslots like “Tuesday 9am, Thursday 2pm, Saturday 11am”. Posts created with this publish type are slotted into the next available timeslot for the target profile.
queue_side (top or bottom, default bottom) controls whether the post jumps the queue or joins the end. If your script bulk-creates 30 queued posts, they’ll spread across the next 30 timeslots in the profile’s queue.
Timeslots are profile-local. Each profile has its own queue; the profile’s has_queue field tells you whether one is configured. There is no API endpoint to edit timeslots. They are configured in the dashboard under the account’s Queue Publish Times settings. Changing them reschedules the posts already in that queue, so re-read scheduled_for rather than caching it. Creating a queued post for a profile without timeslots returns 422.

Bulk scheduling

A common use case: import a CSV of 50 planned posts and schedule them across the next month. The recipe:
  1. Upload each row’s media with POST /workspaces/{workspace}/media_contents: up to 10 files per request, or one URL per request, and wait until every item is active (see Upload and manage media).
  2. Batch posts into POST /workspaces/{workspace}/posts calls of up to 10 items each.
  3. Track the returned post ids (and group_id per batch) so you can update or cancel later.
Watch out for the limits: 1,000 requests per minute on the posts endpoint group, and a daily cap of 200 create requests on Pro (500 on Business) per rolling 24 hours, counted per request rather than per post (see Rate limits). Batching 10 posts per request keeps you far under both.

Status of all scheduled posts

Use the status filter to slice by lifecycle stage (draft, needs_approval, waiting, publishing, published, failed), or statuses=waiting,draft for several at once; any other value is 422. Narrow further with filters[profile_ids][]=<uuid>, filters[group_id]=<uuid>, or a post type: filters[post_type]=video, or filters[post_types][]=image&filters[post_types][]=video for several. The response’s meta.status_counts gives per-status totals for the current filters. Labels are filtered by name: filters[labels][]=launch&filters[labels][]=evergreen returns the posts that carry at least one of them. Two date filters each take a start and an end date as YYYY-MM-DD, both inclusive: filters[publish_range][] matches the planned publish time and filters[date_range][] the actual one, so the latter only returns published posts. See Filtering by date for how the dates are read. The list comes back in planned publish order, earliest first. filters[sort_by]=published_at orders by actual publish time, newest first, and filters[sort_by] set to likes, comments, shares, reach, or engagements orders by that metric, highest first. For example, the posts published in April 2026, most liked first (-g stops curl from treating the brackets as a pattern):
See Ordering for the full list of sort values.

Common scheduling gotchas

Almost always caused by an expired profile connection. Check the profile in the dashboard. If it shows “needs reconnection”, the user has to complete the OAuth flow again. Once the post ends up failed, reconnect and retry it with PUT /posts/{post}/retry. Retry only accepts posts in the failed state.
Sending a scheduled_for value in the past returns 422. If you want to publish immediately, use publish_type: "now" instead.
publish_type: queue_using_timeslots requires the profile to have at least one timeslot defined. If the queue is empty, the create call returns 422. Add timeslots in the dashboard or switch to a fixed datetime.
PublishBuddy stores everything in UTC, so DST transitions don’t move scheduled posts. A post scheduled with an explicit offset or bare-UTC datetime stays at that instant. If you schedule with a timezone field, the wall-clock you gave is converted to UTC once, at creation. It won’t re-shift if DST changes between creation and publication.