Publish now, schedule, or save a draft to one or more accounts, with per-target content, media and platform options.
One POST /v1/posts call publishes the same post to several accounts. Each entry in platforms is a target:
one connected account on one network.
curl -s -X POST https://api.betterpost.dev/v1/posts \
-H "x-access-key: $BETTERPOST_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Our autumn menu is here",
"mediaItems": [
{ "type": "image", "url": "https://assets.betterpost.dev/proj_123/2026/10/med_abc-menu.jpg", "altText": "Pumpkin latte on a wooden table" }
],
"platforms": [
{ "platform": "instagram", "accountId": "acc_7i3g9r1a5m2q8w4e6t0y3u1o" },
{ "platform": "twitter", "accountId": "acc_2x9c4v7b1n8m3k5j0h6g2f4d", "content": "Autumn menu is live. Pumpkin everything." },
{ "platform": "linkedin", "accountId": "acc_5l1n7k3j9h2g8f4d6s0a1q3w", "platformSpecificData": { "visibility": "PUBLIC" } }
],
"publishNow": true,
"idempotencyKey": "autumn-menu-2026"
}'content (required): the shared text. Use "" for media-only posts.platforms (required, at least one): targets { platform, accountId, content?, platformSpecificData? }.
content replaces the shared text for that target only.platformSpecificData holds network options such as X threads or LinkedIn visibility. See the
platform guides. Unknown keys are ignored.mediaItems (optional): { type: "image" | "video" | "gif", url, altText?, thumbnail? }, shared by all
targets. Host files with media upload or use any public http(s) URL.publishNow: true publishes immediately;scheduledFor (with optional timezone) schedules it (see Schedule a post);saveAsDraft: true stores it without publishing or charging.idempotencyKey (optional): makes the request safe to retry. See Idempotency.Each account can appear only once per post, and its platform must match the account's platform.
Before anything is stored, every target is checked against its network's rules (length, media count and type,
options). All problems across all targets come back in one 400, with paths such as platforms.1.mediaItems.
Nothing is created and nothing is charged. Drafts skip the platform rules until they are published.
202 Accepted, with the overall status and one entry per target in request order:
status | Meaning |
|---|---|
published | Every target published without warnings. |
partial | Some targets failed, or published with a warning. |
failed | Every target failed. success is false only in this case. |
publishing | Still running after 8 seconds; it finishes in the background. Poll GET /v1/posts/{id} or wait for a webhook. |
scheduled | Scheduled; scheduledFor holds the UTC instant. |
draft | Saved as a draft. |
Each target result has platform, accountId, success, and then platformPostUrl (published), error
(failed) or warningMessage (published with a warning). A target still in progress counts as success: true.
Targets are independent. If one fails, the others still publish, the failed target is refunded, and the post
ends partial. Fix the cause and call retry: only failed targets are published again, with
the original content.
Each target is charged when the post is accepted: 1 credit for LinkedIn, Instagram and Facebook, 5 for X, or 50 for X when any tweet in the target contains a link. Drafts are free. See Credits.
Reference: POST /v1/posts.