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" }
}
}
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.
scheduled_for with created_atscheduled_foris a future timestamp — when the post will publish. You set this in the request.created_atis 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.
media_ids (request) with media (response)- Request:
media_idsis an array of UUIDs (strings) —["a1b2-...", "c3d4-..."] - Response:
mediais 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 /posts — Content-Type: application/json
| Field | Type | Required | Description |
|---|---|---|---|
content | string | Yes | The text content of the post |
account_ids | integer[] | Yes | Array of social account IDs (integers) to post to. Get them from GET /accounts. |
scheduled_for | string (ISO 8601) | No | UTC 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. |
timezone | string | No | IANA 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_ids | string[] (UUID) | No | Array 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_content | object | No | Platform-specific content overrides and settings (see below). |
cover_photo | object | No | Cover photo / thumbnail for video posts. Applied to all platforms that support it. See Cover Photo below. |
scheduled_at | string (ISO 8601) | No | Deprecated 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
}
}
}
}
| Platform | Settings | Values |
|---|---|---|
instagram | contentType | "Post", "Story", "Reels", "Trial Reel" |
instagram | altText | string (max 100 chars) |
instagram | coverPhotoUrl | string — per-platform cover image URL (overrides global cover_photo) |
tiktok | privacyLevel | "PUBLIC_TO_EVERYONE", "MUTUAL_FOLLOW_FRIENDS", "FOLLOWER_OF_CREATOR", "SELF_ONLY" |
tiktok | allowComments, allowDuet, allowStitch | boolean (default true) |
youtube | title | string (required for YouTube, max 100 chars) |
youtube | privacyStatus | "public", "unlisted", "private" |
youtube | tags | string[] |
youtube | coverPhotoUrl | string — per-platform cover image URL (overrides global cover_photo) |
linkedin | visibility | "PUBLIC", "CONNECTIONS", "LOGGED_IN_MEMBERS" |
linkedin | coverPhotoUrl | string — per-platform cover image URL (overrides global cover_photo) |
x | replySettings | "everyone", "following", "mentionedUsers" |
facebook | videoType | "reel", "video" |
facebook | coverPhotoUrl | string — 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.
| Field | Type | Description |
|---|---|---|
cover_photo.url | string | Publicly accessible URL of the cover image (JPEG or PNG). |
cover_photo.timestamp | number | Frame position in seconds (used by Instagram thumb_offset and TikTok video_cover_timestamp_ms). |
cover_photo.is_custom | boolean | true if this is a custom uploaded image, false if it's a video frame selection. |
Platform support:
| Platform | Custom image | Frame selection | Notes |
|---|---|---|---|
| YouTube | Yes | No | Uploaded via thumbnails.set. Requires phone-verified channel. Max 2MB. |
Yes (cover_url) | Yes (thumb_offset in ms) | JPEG, max 8MB, 9:16 recommended. | |
| Yes | No | Uploaded post-publish via /{video_id}/thumbnails. Max 10MB. | |
| Yes | No | Uploaded during video init (uploadThumbnail). | |
| TikTok | No | Yes | video_cover_timestamp_ms only. No custom image via API. |
| X / Threads | No | No | No 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_idsin the response matchesmedia_idsin the request — safe to round-trip.mediais 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). PollGET /v1/posts/{post_id}to watch destinations transition fromqueued→publishing→publishedorfailed.
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
- 201
- 400
- 401
- 429
Post created
Invalid request parameters
Missing or invalid API key
Rate limit exceeded
Response Headers
Request limit per window
Remaining requests in current window
Unix timestamp when the window resets
Seconds to wait before retrying