Skip to main content
A post in SocialAPI is one piece of content sent to one or more connected accounts. You create it with POST /v1/posts, name each destination in targets, and read the delivery result per platform. The same request publishes to Instagram, Facebook, Threads, TikTok, YouTube, LinkedIn, X, Pinterest, Google Business Profile, Zalo, and Bluesky. Zalo publishes text articles only, and Bluesky publishes text and up to 4 images. What each platform accepts is on its publishing page, linked from Platform support. A post costs 1 post credit when it is published or scheduled, whatever the number of targets. Saving a draft is free. See Plans and usage.

Endpoints

GET /v1/posts takes these query parameters, all optional. Pagination is in Pagination.

Your first post

Publish one text post to a Threads account and a Facebook Page:
Response (HTTP 201):
The post is accepted, and delivery continues after the response. Read the post again or wait for the post.published, post.partial, or post.failed event. Track post delivery explains the statuses, the HTTP codes, and the per-target errors.

Choose what the request does

A scheduled_at that is not RFC 3339 returns 400 validation.field_invalid. With at least one target, a scheduled_at in the past returns 400 validation.field_invalid. With no targets, the post is saved as a draft.

Fields every platform shares

Each item of targets has: A target that does not set a field inherits the post-level value. The same account and Page twice in one request returns 400 publishing.duplicate_target. SocialAPI ignores request fields it does not recognize. POST /v1/posts is not idempotent: a request returns 409 resource.conflict when the same account has a post with the same title and text that was created in the last 24 hours and is now published. Send skip_duplicate_check: true to post the same content again. See IDs.

Where platform data goes

Put platform-specific fields in platform_data, in one of 2 places:
  • On the post, under the platform slug: platform_data.instagram applies to every Instagram target.
  • On a target, as flat keys: targets[].platform_data applies to that target only.
When both set the same key, the target’s value wins. A platform slug used as a key inside a target’s platform_data is dropped, so write {"reply_control": "everyone"} on a target, not {"threads": {...}}.
The keys each platform accepts are on its publishing page, for example YouTube platform data fields.

Validate before you publish

POST /v1/posts/validate checks a post without creating it and costs no credit. Name the platforms with platforms, account_ids, or targets. It accepts the same text, media, scheduled_at, segments, and platform_data fields as POST /v1/posts.
Response (HTTP 200):
errors lists the violations that block publishing. warnings lists the rest, for example text within 10% of a platform’s length limit. Each issue has platform, field, message, and, when it applies to one target, target and segment_index. A scheduled_at that is not in the future returns 400 validation.field_invalid. Calling it without any platform returns 400 validation.field_required. POST /v1/posts runs the same validation unless you send skip_validation: true. A post with errors returns 400 publishing.validation_failed, and error.meta.issues holds the same list. Drafts skip validation until you publish them. GET /v1/posts/validate returns each platform’s text and media rules, so a client can check content before it sends a request. Add ?platform=instagram for one platform. An unknown platform returns 400 validation.field_invalid.
Response (HTTP 200):
The endpoint covers Instagram, Facebook, Threads, LinkedIn, YouTube, TikTok, Google Business Profile, Bluesky, and X. counting_mode is chars, utf16, or bytes. TikTok’s per-creator privacy levels and maximum duration come from GET /v1/accounts/{id}/creator-info. See TikTok publishing. supports_mixed_media is about platform_attachment_id items only: false means a platform_attachment_id item must be the only item in media and cannot sit next to url or media_id items. It does not limit a carousel, which can mix images and videos where carousel.allow_video is true and carousel.images_only is false, as on Instagram.

Drafts

A post saved without publish_now or scheduled_at is a draft. It costs no credit, skips validation, and can have no targets.
The response is HTTP 201 with "status": "draft". Publish it later with POST /v1/posts/{pid}/publish, which costs 1 post credit.

Edit a post before it publishes

PATCH /v1/posts/{pid} changes a post that has not been delivered yet. Send only the fields you change.
The response is HTTP 200 with the updated post. Editing costs no credit.
  • Editable statuses: draft, scheduled, and failed. Any other status returns 409 post.state_invalid, and error.meta.state is the current status. A published post cannot be edited on any platform, except a YouTube video, which has its own endpoint. See YouTube publishing.
  • Editable fields: text, title, media, visibility, first_comment, scheduled_at, hidden, segments, platform_data, and targets. Sending targets replaces all the post’s targets.
  • Validation: an edit to content runs the validation again. It returns 400 publishing.validation_failed for a post with errors, unless you send skip_validation: true.
  • After a failure: edit a failed post, then call POST /v1/posts/{pid}/retry.
  • Event: SocialAPI sends post.updated with changed_fields.

Import posts from a CSV file

POST /v1/posts/import creates posts from a CSV file sent as multipart form data in the file field. The first row is a header and must start with the text column. The file holds at most 500 data rows and 5 MB. Columns are read by position, in this order. A row can end early: only text is required. There is no publish_now column, so an imported post is a draft or a scheduled post.
Response (HTTP 200):
dry_run=true checks only each row’s text, scheduled_at, and platform_data format, and creates nothing. Row numbers count the header as row 1, so the first data row is row 2. A request with no file field returns 400 validation.field_required. A file over 5 MB, a file that is not valid CSV, a header that does not start with text, a file with no data rows, or a file with more than 500 data rows returns 400 validation.field_invalid. When the file has more valid rows than you have post credits left, drafts included, the request returns 429 billing.post_limit and creates nothing. Each created post is a normal post: it costs 1 post credit when scheduled and nothing as a draft. Without dry_run, a row that passes these checks but fails when SocialAPI creates the post (for example 409 resource.conflict or 400 publishing.validation_failed) is listed in errors with its row number and message and counted in failed. The other rows are still created, and the response is still 200: check created, failed, and errors.

Unpublish and delete

POST /v1/posts/{pid}/unpublish removes the published copies from the platforms and keeps the post. It returns 200 {"success": true} when every targeted copy was removed, 400 validation.field_invalid when no target is published with a platform_post_id, 501 resource.not_supported when at least one remaining target is on a platform that cannot delete posts (such as TikTok), and 500 system.internal when a platform fails to delete a copy. With a 501 or 500, every target that was removed is unpublished. See Unpublish a post. DELETE /v1/posts/{pid} deletes a post and also removes its published copies. See Delete a post.

Platform support

Each platform accepts different content, sizes, and platform_data keys. The cross-platform matrix is on Platform support. The details are on each platform’s publishing page: Every platform accepts scheduled_at. A platform that cannot do something returns the error described on its page, for example 501 resource.not_supported.

Limits

Errors

400 publishing.validation_failed, 409 resource.conflict, 409 post.state_invalid, and 429 publishing.velocity_limit are the codes you branch on most. Every code is in Errors. A failure after the request returned appears on the target, not as an HTTP error. A target on an account without the posts.write permission fails with account.permission_missing. See Track post delivery.

Scopes

GET /v1/posts/validate needs no scope: any valid API key can call it. A brand-restricted key can publish only to accounts in its brands. See Scoped API keys.

Events

Payloads are in Webhook events.

Use from an AI agent

The import endpoint has no tool. See MCP tools.
Last modified on October 19, 2026