Skip to main content

Create post

POST 

/posts

Create and schedule a social media post. Specify content, target accounts, and optionally a schedule time. If no scheduled_for is provided, the post is published immediately.

Use platform_content to customise content per platform — override captions, set Instagram content type (Story, Reels, Post), configure TikTok privacy, set YouTube titles, and more.

Instagram Stories example:

{
"content": "Check this out!",
"account_ids": [123],
"media_ids": ["uuid"],
"platform_content": {
"instagram": { "contentType": "Story" }
}
}
Round-trip safe

Every name in this request body also appears in the response. You can take any post you got from GET /v1/posts/{id} and POST its top-level fields straight back to create a clone — no field renaming required. The fields are: content, account_ids, scheduled_for, timezone, media_ids, platform_content.

Don't confuse scheduled_for with created_at
  • scheduled_for is a future timestamp — when the post will publish. You set this in the request.
  • created_at is a past timestamp — when the post record was created (i.e. when you scheduled it). It appears in the response only.

These are different fields with different meanings. The PostEverywhere API used to have a request field called scheduled_at which carried the same value as scheduled_for — that was a misnomer and it has been renamed. scheduled_at is still accepted as a deprecated alias for scheduled_for, but new integrations should always use scheduled_for.

Don't confuse media_ids (request) with media (response)
  • Request: media_ids is an array of UUIDs (strings) — ["a1b2-...", "c3d4-..."]
  • Response: media is an array of full media objects with url, type, dimensions, etc.

The response also includes media_ids as a convenience array of just the UUIDs, so you can round-trip it directly.

If you send media, media_id, mediaId, or attachments in a request body, the API will return a 400 invalid_field_name with a "did you mean media_ids?" hint.

Request Body Schema

POST /postsContent-Type: application/json

FieldTypeRequiredDescription
contentstringYesThe text content of the post
account_idsinteger[]YesArray of social account IDs (integers) to post to. Get them from GET /accounts.
scheduled_forstring (ISO 8601)NoUTC datetime when the post should publish, e.g. "2026-04-15T14:30:00Z". Always send UTC — convert from local time in your code if needed. Omit to publish immediately.
timezonestringNoIANA timezone (e.g. "UTC", "America/New_York"). Defaults to "UTC". Display metadata only — does not affect when the post fires. The web app uses this to render schedules in the user's preferred local time.
media_idsstring[] (UUID)NoArray of media UUIDs to attach. Get IDs from POST /media/upload-from-url (one-call URL import) or the 3-step POST /media/upload flow.
platform_contentobjectNoPlatform-specific content overrides and settings (see below).
cover_photoobjectNoCover photo / thumbnail for video posts. Applied to all platforms that support it. See Cover Photo below.
scheduled_atstring (ISO 8601)NoDeprecated alias for scheduled_for. Still accepted for back-compat with integrations built before 2026-04-12. New integrations should use scheduled_for.

platform_content structure

Use the platform key as the object key. Each platform object can contain a content override and a settings object.

Supported platform keys: instagram, tiktok, youtube, linkedin, x, facebook, threads

Instagram example with settings:

{
"platform_content": {
"instagram": {
"content": "Instagram-specific caption with #hashtags",
"contentType": "Reels",
"settings": {
"altText": "Image description for accessibility",
"coverPhotoTimestamp": 2.5
}
}
}
}
PlatformSettingsValues
instagramcontentType"Post", "Story", "Reels", "Trial Reel"
instagramaltTextstring (max 100 chars)
instagramcoverPhotoUrlstring — per-platform cover image URL (overrides global cover_photo)
tiktokprivacyLevel"PUBLIC_TO_EVERYONE", "MUTUAL_FOLLOW_FRIENDS", "FOLLOWER_OF_CREATOR", "SELF_ONLY"
tiktokallowComments, allowDuet, allowStitchboolean (default true)
youtubetitlestring (required for YouTube, max 100 chars)
youtubeprivacyStatus"public", "unlisted", "private"
youtubetagsstring[]
youtubecoverPhotoUrlstring — per-platform cover image URL (overrides global cover_photo)
linkedinvisibility"PUBLIC", "CONNECTIONS", "LOGGED_IN_MEMBERS"
linkedincoverPhotoUrlstring — per-platform cover image URL (overrides global cover_photo)
xreplySettings"everyone", "following", "mentionedUsers"
facebookvideoType"reel", "video"
facebookcoverPhotoUrlstring — per-platform cover image URL (overrides global cover_photo)

Cover Photo

Set a video thumbnail / cover photo that applies to all supported platforms. For video posts only.

FieldTypeDescription
cover_photo.urlstringPublicly accessible URL of the cover image (JPEG or PNG).
cover_photo.timestampnumberFrame position in seconds (used by Instagram thumb_offset and TikTok video_cover_timestamp_ms).
cover_photo.is_custombooleantrue if this is a custom uploaded image, false if it's a video frame selection.

Platform support:

PlatformCustom imageFrame selectionNotes
YouTubeYesNoUploaded via thumbnails.set. Requires phone-verified channel. Max 2MB.
InstagramYes (cover_url)Yes (thumb_offset in ms)JPEG, max 8MB, 9:16 recommended.
FacebookYesNoUploaded post-publish via /{video_id}/thumbnails. Max 10MB.
LinkedInYesNoUploaded during video init (uploadThumbnail).
TikTokNoYesvideo_cover_timestamp_ms only. No custom image via API.
X / ThreadsNoNoNo cover photo API support.

Example with cover photo:

{
"content": "New video!",
"account_ids": [123, 456],
"media_ids": ["video-uuid"],
"cover_photo": {
"url": "https://imagedelivery.net/.../public",
"timestamp": 5.0,
"is_custom": true
}
}

You can also set different covers per platform using platform_content.{platform}.settings.coverPhotoUrl — this overrides the global cover_photo for that specific platform.

Full Request Example

{
"content": "Excited to share our latest update! Check it out.",
"account_ids": [2280, 2282, 2285],
"scheduled_for": "2026-04-15T14:30:00Z",
"timezone": "UTC",
"media_ids": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"],
"platform_content": {
"instagram": {
"content": "Excited to share our latest update! #newfeature #launch",
"contentType": "Reels"
},
"x": {
"content": "Excited to share our latest update! Check it out."
}
}
}

Response Examples

201 — Post created

Notice how every top-level field in the request also appears in this response with the same name. The response is round-trip safe — you can take it and POST it back to clone the post. The response also includes the rich destinations[] array (one entry per target account), the fully hydrated media[] array, and a next_step string telling you what to do next.

{
"data": {
"post_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"status": "scheduled",
"content": "Excited to share our latest update! Check it out.",
"account_ids": [2280, 2282, 2285],
"scheduled_for": "2026-04-15T14:30:00.000Z",
"timezone": "UTC",
"media_ids": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"],
"media": [
{
"media_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": "image",
"width": 1920,
"height": 1080,
"duration": null
}
],
"platform_content": {
"instagram": { "contentType": "Reels" }
},
"destinations": [
{
"account_id": 2280,
"account_name": "@mybrand",
"platform": "instagram",
"status": "queued",
"platform_post_id": null,
"platform_post_url": null,
"published_at": null,
"error": null,
"attempts": 0
},
{
"account_id": 2282,
"account_name": "@mybrand",
"platform": "x",
"status": "queued",
"platform_post_id": null,
"platform_post_url": null,
"published_at": null,
"error": null,
"attempts": 0
},
{
"account_id": 2285,
"account_name": "My Brand Inc.",
"platform": "linkedin",
"status": "queued",
"platform_post_id": null,
"platform_post_url": null,
"published_at": null,
"error": null,
"attempts": 0
}
],
"created_at": "2026-04-10T12:00:00.000Z",
"updated_at": "2026-04-10T12:00:00.000Z",
"accounts_count": 3,
"next_step": "Post scheduled for 2026-04-15T14:30:00.000Z. Poll GET /v1/posts/f47ac10b-58cc-4372-a567-0e02b2c3d479 or GET /v1/posts/f47ac10b-58cc-4372-a567-0e02b2c3d479/results to watch publish status."
},
"error": null,
"meta": {
"request_id": "a1b2c3d4",
"timestamp": "2026-04-10T12:00:00.000Z"
}
}

Notice the two timestamps:

  • scheduled_for: "2026-04-15T14:30:00.000Z"future moment when the post will publish (matches the request).
  • created_at: "2026-04-10T12:00:00.000Z"past moment when the schedule was created (5 days earlier).

These are different fields with different meanings. Don't confuse them. Both are UTC, always with a Z suffix.

Destinations vs media_ids round-trip:

  • media_ids in the response matches media_ids in the request — safe to round-trip.
  • media is the fully hydrated form with dimensions, type, duration. Response-only.
  • destinations[] is a response-only array — each entry tracks per-account publish state (status, error, URL, attempts). Poll GET /v1/posts/{post_id} to watch destinations transition from queuedpublishingpublished or failed.

400 — Validation error

All error responses use the canonical envelope {data: null, error: {message, code, details?}, meta: {request_id, timestamp}}. The details object is optional and contains structured context — here it tells you which field names you sent that were rejected.

{
"data": null,
"error": {
"message": "Missing required fields: content and account_ids are required.",
"code": "validation_error",
"details": {
"missing_fields": ["content", "account_ids"]
}
},
"meta": {
"request_id": "e5f6g7h8",
"timestamp": "2026-04-10T12:00:00.000Z"
}
}

Invalid field name error — if you accidentally send media, mediaId, attachments, or scheduled_at where media_ids / scheduled_for was expected:

{
"data": null,
"error": {
"message": "Unknown field 'media'. Did you mean 'media_ids'?",
"code": "invalid_field_name",
"details": {
"field": "media",
"suggestion": "media_ids"
}
},
"meta": {
"request_id": "e5f6g7h8",
"timestamp": "2026-04-10T12:00:00.000Z"
}
}

401 — Unauthorized

{
"data": null,
"error": {
"message": "Invalid API key. Check that the key is correct and has not been deleted.",
"code": "invalid_api_key"
},
"meta": {
"request_id": "i9j0k1l2",
"timestamp": "2026-04-10T12:00:00.000Z"
}
}

429 — Rate limited

{
"data": null,
"error": {
"message": "Rate limit exceeded. Try again in 30 seconds.",
"code": "rate_limited"
},
"meta": {
"request_id": "m3n4o5p6",
"timestamp": "2026-04-10T12:00:00.000Z"
}
}

Request

Responses

Post created