Posts API

Create, schedule, and manage posts across all 12 supported platforms. The Posts API handles single posts, bulk creation, content validation, and retry logic for failed deliveries.

POST/api/v1/postsCreate or schedule a post
POST/api/v1/posts/bulkCreate multiple posts at once
GET/api/v1/postsList all posts
GET/api/v1/posts/:idGet post details
PATCH/api/v1/posts/:idUpdate a scheduled or draft post
DELETE/api/v1/posts/:idCancel or delete a post
POST/api/v1/posts/:id/retryRetry a failed post
POST/api/v1/posts/validateValidate content against platform limits

Create a post

POST/api/v1/postsCreate or schedule a post

Headers

Authorization
stringRequired

Bearer token. Example: Bearer sk_live_abc123...

Content-Type
stringRequired

Must be application/json

Idempotency-Key
stringOptional

Unique key to prevent duplicate posts. Recommended for all create operations.

Request body

content
stringOptional

Post text content. Character limits vary by platform. Optional for media-only posts.

platforms
arrayRequired

Array of platform targets. Each object must include platform and accountId. accountId may be omitted (or a bare platform string used) ONLY when exactly one account is connected on that platform — with multiple accounts the API returns a 400 listing the choices instead of silently picking one. Optionally include a content field to override the top-level content for that platform.

mediaItems
arrayOptional

Array of media attachments. Each object has type ("image" or "video") and url (publicly accessible URL).

status
stringOptional

Post status.

Default: scheduled

draftscheduledimmediate
scheduledFor
stringOptional

ISO 8601 date for scheduling. Must be at least 5 minutes in the future and no more than 90 days ahead.

timezone
stringOptional

IANA timezone string (e.g. "America/New_York"). Used to interpret scheduledFor.

platformSettings
objectOptional

Platform-specific settings. Keys are platform names, values are setting objects (e.g. instagram.shareToFeed, twitter.replySettings).

publishNow
booleanOptional

Set to true to publish immediately, bypassing any schedule.

Default: false

queue
booleanOptional

Set to true to auto-schedule this post to the next available queue slot. Use instead of scheduledFor.

Default: false

firstComment
stringOptional

Auto-post this text as the first comment right after the post publishes (shorthand for firstComments: [{target: 0, text}]). Text and links only, no media.

firstComments
arrayOptional

Auto-posted first comments as [{target, text}]. target 0 = the main post (default); higher targets address thread entries (threads endpoint only). See the First Comment callout below for supported platforms and limits.

Per-platform content: Each object in the platforms array can include a content field that overrides the top-level content for that specific platform. This lets you tailor messaging per network while creating a single post.

First Comment (auto-plug): Pass firstComment (a plain string) or firstComments to automatically comment under the post ~15 seconds after it publishes — the classic "link in the first comment" workflow. Supported on 8 platforms: LinkedIn, Facebook, Instagram, YouTube, X (Twitter), Threads, Bluesky, and Mastodon. Not supported on TikTok, Pinterest, Telegram, or Google Business (no comment API): targeting only unsupported platforms is a validation error, while mixed selections publish the comment where supported and return a warning. Comment limits: Instagram 1,000 UTF-8 bytes, LinkedIn 1,500, X 280, Bluesky 300, Threads 500, YouTube 10,000 characters; Facebook and Mastodon have no fixed limit. Comments are text and links only. A failed comment never fails the post — read platforms[].firstComments on GET /api/v1/posts/:id for per-account delivery status (posted with the comment URL, failed, or skipped).

Queue integration: Instead of setting scheduledFor manually, pass "queue": true to automatically schedule the post into the next available queue slot. See the Queues API for details.

TikTok requires a privacy level. When you target tiktok, you must include platformSettings.tiktok.privacy. Valid values are PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, and SELF_ONLY, and the value must be enabled on the connected account. Posts are rejected at creation rather than published with a guessed privacy level.

Mastodon options live in platformSettings.mastodon. Set visibility to public (default), unlisted, private, or direct. Also supported: spoilerText (content warning), sensitive (boolean, blurs media), and language (ISO 639-1). See the Mastodon platform guide for polls and full details.

# Publish immediately
schedulala post "Excited to announce our new feature!" --platforms twitter,linkedin,instagram --now
# Schedule for later
schedulala post "Excited to announce our new feature!" \
--platforms twitter,linkedin,instagram \
--schedule "2026-03-15T14:00:00Z"
# With media
schedulala post "Check it out" --platforms instagram \
--media "https://example.com/announcement.jpg" --now
# Platform-specific content
schedulala post "Default text" --platforms twitter,linkedin \
--platform-content '{"linkedin":{"content":"LinkedIn version"}}' --now
{
"success": true,
"post": {
"id": "67abc456def789012345cdef",
"status": "scheduled",
"content": "Excited to announce our new feature! Check it out at example.com",
"scheduledFor": "2026-03-15T14:00:00.000Z",
"platforms": [
{
"platform": "twitter",
"accountId": "67abc123def4567890120001",
"status": "pending"
},
{
"platform": "linkedin",
"accountId": "67abc123def4567890120002",
"status": "pending"
},
{
"platform": "instagram",
"accountId": "67abc123def4567890120003",
"status": "pending"
}
],
"createdAt": "2026-03-01T12:00:00.000Z"
}
}

Bulk create posts

POST/api/v1/posts/bulkCreate multiple posts at once

Create up to 25 posts in a single request. Each post in the array follows the same schema as the create post endpoint. The response includes a summary with per-post results so you can identify any failures.

Request body

posts
arrayRequired

Array of post objects (max 25). Each follows the same schema as POST /api/v1/posts.

timezone
stringOptional

Default timezone for all posts. Individual posts can override this.

curl -X POST https://schedulala.com/api/v1/posts/bulk \
-H "Authorization: Bearer sk_live_abc123..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: bulk_$(uuidgen)" \
-d '{
"timezone": "America/New_York",
"posts": [
{
"content": "Monday motivation! Start the week strong.",
"platforms": [{ "platform": "twitter", "accountId": "67abc123def4567890120001" }],
"scheduledFor": "2026-03-16T09:00:00Z"
},
{
"content": "Wednesday tip: consistency beats perfection.",
"platforms": [{ "platform": "twitter", "accountId": "67abc123def4567890120001" }],
"scheduledFor": "2026-03-18T09:00:00Z"
}
]
}'
{
"success": true,
"results": [
{
"index": 0,
"success": true,
"post": {
"id": "67abc456def789012345cd01",
"status": "scheduled",
"content": "Monday motivation! Start the week strong.",
"scheduledFor": "2026-03-16T09:00:00.000Z",
"platforms": [
{
"platform": "twitter",
"accountId": "67abc123def4567890120001",
"status": "pending"
}
],
"createdAt": "2026-03-01T12:00:00.000Z"
}
},
{
"index": 1,
"success": true,
"post": {
"id": "67abc456def789012345cd02",
"status": "scheduled",
"content": "Wednesday tip: consistency beats perfection.",
"scheduledFor": "2026-03-18T09:00:00.000Z",
"platforms": [
{
"platform": "twitter",
"accountId": "67abc123def4567890120001",
"status": "pending"
}
],
"createdAt": "2026-03-01T12:00:00.000Z"
}
}
],
"summary": {
"total": 2,
"succeeded": 2,
"failed": 0
}
}

List posts

GET/api/v1/postsList all posts

Retrieve a paginated list of your posts. Supports filtering by status, platform, profile, and date range. Results are returned in reverse chronological order.

Query parameters

status
stringOptional

Filter by post status.

draftscheduledimmediatepostedpartially_failedfailedcancelled
brandId
stringOptional

Filter by brand ID.

platform
stringOptional

Filter by platform name.

twitterinstagramfacebooklinkedintiktokthreadsblueskyyoutubepinteresttelegram
limit
numberOptional

Number of posts to return.

Default: 20

page
numberOptional

Page number for offset-based pagination.

Default: 1

after
stringOptional

Return posts scheduled after this ISO 8601 date.

before
stringOptional

Return posts scheduled before this ISO 8601 date.

# List all posts
schedulala post:list
# Filter by status
schedulala post:list --status scheduled --limit 10
{
"success": true,
"posts": [
{
"id": "67abc456def789012345cdef",
"status": "scheduled",
"content": "Excited to announce our new feature!",
"scheduledFor": "2026-03-15T14:00:00.000Z",
"platforms": [
{
"platform": "twitter",
"accountId": "67abc123def4567890120001",
"status": "pending"
}
],
"createdAt": "2026-03-01T12:00:00.000Z"
}
],
"pagination": {
"total": 45,
"page": 1,
"limit": 10,
"pages": 5,
"hasMore": true
}
}

Get post details

GET/api/v1/posts/:idGet post details

Retrieve full details for a single post, including per-platform delivery status. After a post is published, each platform entry includes the native postId, postUrl, and postedAt timestamp.

Path parameters

id
stringRequired

The post ID (e.g. 67abc456def789012345cdef).

schedulala post:status 67abc456def789012345cdef
# Watch until published or failed
schedulala post:status 67abc456def789012345cdef --watch
{
"success": true,
"post": {
"id": "67abc456def789012345cdef",
"status": "posted",
"content": "Excited to announce our new feature! Check it out at example.com",
"scheduledFor": "2026-03-15T14:00:00.000Z",
"platforms": [
{
"platform": "twitter",
"accountId": "67abc123def4567890120001",
"status": "posted",
"postId": "1897654321098765432",
"postUrl": "https://x.com/yourhandle/status/1897654321098765432",
"postedAt": "2026-03-15T14:00:05.000Z"
},
{
"platform": "linkedin",
"accountId": "67abc123def4567890120002",
"status": "posted",
"postId": "urn:li:share:7654321098765432100",
"postUrl": "https://www.linkedin.com/feed/update/urn:li:share:7654321098765432100",
"postedAt": "2026-03-15T14:00:08.000Z",
"firstComments": [
{
"target": 0,
"status": "posted",
"commentId": "urn:li:comment:(urn:li:share:7654321098765432100,7654321098765432101)",
"url": "https://www.linkedin.com/feed/update/urn:li:share:7654321098765432100",
"postedAt": "2026-03-15T14:00:24.000Z"
}
]
},
{
"platform": "instagram",
"accountId": "67abc123def4567890120003",
"status": "posted",
"postId": "17890012345678901",
"postUrl": "https://www.instagram.com/p/ABC123xyz/",
"postedAt": "2026-03-15T14:00:12.000Z"
}
],
"mediaItems": [
{
"type": "image",
"url": "https://example.com/announcement.jpg"
}
],
"firstComments": [
{
"target": 0,
"text": "Full story: https://example.com/blog/new-feature"
}
],
"createdAt": "2026-03-01T12:00:00.000Z",
"updatedAt": "2026-03-15T14:00:12.000Z"
}
}

Update a post

PATCH/api/v1/posts/:idUpdate a scheduled or draft post

Update a post that is in draft or scheduled status. Published, failed, and cancelled posts cannot be updated. Only include the fields you want to change — omitted fields are left unchanged.

Path parameters

id
stringRequired

The post ID to update.

Request body

content
stringOptional

Updated post text content.

platforms
arrayOptional

Updated platform targets.

mediaItems
arrayOptional

Updated media attachments.

scheduledFor
stringOptional

Updated schedule time (ISO 8601).

timezone
stringOptional

Updated timezone.

platformSettings
objectOptional

Updated platform-specific settings.

status
stringOptional

Change status (e.g. move a draft to scheduled).

draftscheduled
firstComment
stringOptional

Replace the auto-posted first comment on the main post (shorthand for firstComments: [{target: 0, text}]).

firstComments
arrayOptional

Replace ALL auto-posted first comments ([{target, text}]). Pass an empty array to clear pending comments — delivery reads the post at publish time, so changes apply without rescheduling.

curl -X PATCH https://schedulala.com/api/v1/posts/67abc456def789012345cdef \
-H "Authorization: Bearer sk_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"content": "Updated announcement! Check out our new feature at example.com",
"scheduledFor": "2026-03-16T10:00:00Z"
}'
{
"success": true,
"post": {
"id": "67abc456def789012345cdef",
"status": "scheduled",
"content": "Updated announcement! Check out our new feature at example.com",
"scheduledFor": "2026-03-16T10:00:00.000Z",
"platforms": [
{
"platform": "twitter",
"accountId": "67abc123def4567890120001",
"status": "pending"
},
{
"platform": "linkedin",
"accountId": "67abc123def4567890120002",
"status": "pending"
},
{
"platform": "instagram",
"accountId": "67abc123def4567890120003",
"status": "pending"
}
],
"createdAt": "2026-03-01T12:00:00.000Z",
"updatedAt": "2026-03-01T13:30:00.000Z"
}
}

Delete a post

DELETE/api/v1/posts/:idCancel or delete a post

Cancel a scheduled or draft post. The post status is set to cancelled and it will not be published. Already-published posts cannot be deleted through this endpoint.

Path parameters

id
stringRequired

The post ID to cancel.

schedulala post:delete 67abc456def789012345cdef
{
"success": true,
"deleted": true,
"id": "67abc456def789012345cdef",
"method": "cancelled"
}

Retry a failed post

POST/api/v1/posts/:id/retryRetry a failed post

Retry delivery for a post with status failed or partially_failed. You can optionally specify which platforms to retry and add a delay before the retry attempt.

Path parameters

id
stringRequired

The post ID to retry.

Request body

platforms
arrayOptional

Array of platform names to retry. If omitted, all failed platforms are retried.

delay
numberOptional

Seconds to wait before retrying.

Default: 0

schedulala post:retry 67abc456def789012345cdef
{
"success": true,
"post": {
"id": "67abc456def789012345cdef",
"status": "scheduled",
"content": "Excited to announce our new feature! Check it out at example.com",
"platforms": [
{
"platform": "twitter",
"accountId": "67abc123def4567890120001",
"status": "posted",
"postId": "1897654321098765432",
"postUrl": "https://x.com/yourhandle/status/1897654321098765432",
"postedAt": "2026-03-15T14:00:05.000Z"
},
{
"platform": "instagram",
"accountId": "67abc123def4567890120003",
"status": "pending",
"previousError": "Media upload timeout"
}
]
},
"retriedPlatforms": [
"instagram"
]
}

Validate content

POST/api/v1/posts/validateValidate content against platform limits

Check whether your content meets each platform's requirements before creating a post. Returns per-platform validation results including character counts, media compatibility, and any errors.

Request body

content
stringRequired

The text content to validate.

platforms
arrayRequired

Array of platform name strings to validate against (e.g. ["twitter", "instagram", "tiktok"]).

mediaItems
arrayOptional

Array of media items to validate. Each has type and url.

firstComment
stringOptional

First-comment text to validate against each platform's comment limits (shorthand for firstComments: [{target: 0, text}]).

firstComments
arrayOptional

First comments to validate ([{target, text}]) — reports the same errors and warnings POST /api/v1/posts would return.

curl -X POST https://schedulala.com/api/v1/posts/validate \
-H "Authorization: Bearer sk_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"content": "Check out our latest product update! We have been working hard to bring you the best experience possible.",
"platforms": ["twitter", "tiktok", "instagram"],
"mediaItems": [
{ "type": "image", "url": "https://example.com/product.jpg" }
]
}'
{
"success": true,
"validation": {
"isValid": false,
"platforms": {
"twitter": {
"valid": true,
"contentLength": 42,
"maxLength": 280,
"remainingCharacters": 238,
"mediaValid": true
},
"tiktok": {
"valid": false,
"errors": [
"TikTok requires a video. Image-only posts are not supported."
],
"mediaValid": false
},
"instagram": {
"valid": true,
"contentLength": 42,
"maxLength": 2200,
"remainingCharacters": 2158,
"mediaValid": true
}
}
}
}

Related