Publish
Publish one post to one or many networks now, and check a post's status.
One request publishes to every network you name, in parallel. Each network gets its own result, so one failing never fails the others. posted counts the networks that went out. Send per_platform drafts (from AI writing) to publish a different native text on each network, and options for the per-network options.
POST /api/post: Post (or cross-post) to one or more platformsGET /api/post/status: Check the status of a created post
Post (or cross-post) to one or more platforms
POST
/api/postNeeds an API key: Authorization: Bearer pw_live_….
Body (JSON)
| Field | Type | Description |
|---|---|---|
platform | string | Single platform; or use 'platforms'. |
platforms | array of string | Cross-post to many at once, in parallel. |
text | string | Caption / description. |
title | string | Title (used by YouTube). |
video_url | string | URL of the video to post (required for YouTube; TikTok takes a video or photos). |
photo_url | string | URL of one image. TikTok posts it as a photo post. Instagram and TikTok take JPEG (TikTok also WebP): PostWire converts PNG, WebP, AVIF and still GIF to JPEG automatically; animated GIF and HEIC are refused. |
privacy | string | e.g. private | public | unlisted (YouTube). |
media | array of object | Several images (or images and videos) in one post, in order. Instagram: a carousel of up to 10 (images and MP4 videos). TikTok: a photo post of up to 35 images. PNG (and WebP, AVIF, still GIF) is converted to JPEG automatically for Instagram and TikTok. Bluesky: up to 4 images. Mastodon: up to 4. LinkedIn: up to 20 images, or one document (PDF/PPT/DOC). Networks that take one item get the first, and the result says so. Also accepted inside a per_platform draft. Up to 35 items. |
media[].url required | string | Public https URL of the file. |
media[].type | string | One of: image, video, document. Default "image". |
media[].alt | string | Alt text (Bluesky, Mastodon, LinkedIn). |
media[].title | string | Document title (LinkedIn). |
photo_urls | array of string | Shorthand for media: a list of image URLs. |
per_platform | object | Per-network overrides, keyed by network name (e.g. {"tiktok":{"text":"…"},"youtube":{"title":"…","text":"…"}}). Each value may carry text, title, video_url, photo_url, media and network options. |
options | object (see Network options) | Options per network, keyed by network name. See Network options. |
brand_id | string | Brand whose connected accounts publish this post (GET /api/brands). Defaults to the account's only brand. |
idempotency_key | string | Send the same key on a retry and the post is not published twice. |
Responses
| Status | Means |
|---|---|
200 | One result per network. Check `posted` and each `results[].ok`. Each result carries account { platform, handle, display_name, brand_id, brand, label } and published_to names every account reached. With the same network in more than one brand and no brand_id: 409 ambiguous_brand, nothing published. On a plan with a networks-per-post limit (Free: 2) the first networks that pass their checks are published and the others come back held (code networks_per_post), listed in held_networks with held_message and an upgrade link: a partial publish is a 200 that says what it held back. |
400 | Bad request |
401 | Missing or invalid API key |
403 | Email not verified, or the key is read-only |
curl
curl -X POST "https://postwire.io/api/post" \
-H "Authorization: Bearer $POSTWIRE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"platforms": [
"bluesky",
"linkedin",
"telegram"
],
"text": "Our sourdough now proofs for 36 hours. Here is what changed in the crumb.",
"idempotency_key": "bakery-2026-10-03-sourdough"
}'Node
const res = await fetch("https://postwire.io/api/post", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
"platforms": [
"bluesky",
"linkedin",
"telegram"
],
"text": "Our sourdough now proofs for 36 hours. Here is what changed in the crumb.",
"idempotency_key": "bakery-2026-10-03-sourdough"
}),
});
console.log(res.status, await res.json());Python
import os, requests
r = requests.post(
"https://postwire.io/api/post",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
json={
"platforms": ["bluesky", "linkedin", "telegram"],
"text": "Our sourdough now proofs for 36 hours. Here is what changed in the crumb.",
"idempotency_key": "bakery-2026-10-03-sourdough",
},
)
print(r.status_code, r.json())Example response
JSON
{
"posted": 3,
"published_to": "Published to @bakery.bsky.social (Bakery) on bluesky, Ana Ruiz (Bakery) on linkedin, chat @bakerynews (Bakery) on telegram.",
"results": [
{
"ok": true,
"platform": "bluesky",
"id": "at://did:plc:4x2…/app.bsky.feed.post/3l7…",
"url": "https://bsky.app/profile/bakery.bsky.social/post/3l7…",
"account": {
"platform": "bluesky",
"handle": "bakery.bsky.social",
"brand": "Bakery",
"label": "@bakery.bsky.social (Bakery)"
}
},
{
"ok": true,
"platform": "linkedin",
"id": "urn:li:share:72…",
"url": "https://www.linkedin.com/feed/update/urn:li:share:72…"
},
{
"ok": true,
"platform": "telegram",
"id": "412",
"url": "https://t.me/bakerynews/412"
}
]
}Each result also carries account, the connected account it went to. The second and third are shortened here.
Check the status of a created post
GET
/api/post/statusNeeds an API key: Authorization: Bearer pw_live_….
Parameters
| Field | Type | Description |
|---|---|---|
platform required | query, string | |
id required | query, string |
Responses
| Status | Means |
|---|---|
200 | { platform, id, status } |
curl
curl "https://postwire.io/api/post/status?platform=instagram&id=igp_5d0c%E2%80%A6" \
-H "Authorization: Bearer $POSTWIRE_API_KEY"Node
const res = await fetch("https://postwire.io/api/post/status?platform=instagram&id=igp_5d0c%E2%80%A6", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}` },
});
console.log(res.status, await res.json());Python
import os, requests
r = requests.get(
"https://postwire.io/api/post/status?platform=instagram&id=igp_5d0c%E2%80%A6",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
)
print(r.status_code, r.json())Example response
JSON
{
"platform": "instagram",
"id": "igp_5d0c…",
"status": "processing"
}