Create an API key
Open Dashboard → AI & API and create a key. Copy it once. It starts with pw_live_. Send it on every request:
Header
Authorization: Bearer pw_live_…
Every URL below starts at https://postwing.io/api/v2.
the whole flow
You call PostWing. PostWing publishes to X at the time you set. The post also shows up on your calendar.
Open Dashboard → AI & API and create a key. Copy it once. It starts with pw_live_. Send it on every request:
Header
Authorization: Bearer pw_live_…
Every URL below starts at https://postwing.io/api/v2.
Connect the X account you want to post from. You only do this once. The platform value is x, including for Twitter.
The simple way is in the dashboard. Open Accounts, choose X, and finish the X login.
From the API, start that same login. The response includes a url. Open it and approve X.
Start connect
curl -s -X POST "https://postwing.io/api/v2/accounts/connect" \
-H "Authorization: Bearer pw_live_…" \
-H "Content-Type: application/json" \
-d '{ "platform": "x" }'After you finish in the browser, sync. This imports the account into your workspace and returns the account id.
Sync
curl -s -X POST "https://postwing.io/api/v2/accounts/sync" \ -H "Authorization: Bearer pw_live_…"
In the response, find the object where platform is x and copy id. That is the id you send when you create the post.
Sync response
{
"data": [
{
"id": "e5f6a7b8-c9d0-1234-ef01-345678901234",
"platform": "x",
"handle": "brand",
"display_name": "Brand",
"status": "connected"
}
]
}Connected X in the dashboard instead? List accounts and copy the same field:
List accounts
curl -s -X GET "https://postwing.io/api/v2/accounts" \ -H "Authorization: Bearer pw_live_…"
A caption is enough on X. Send the text, a future time, and the X account id you copied after sync. This call schedules the post. X publishes it at scheduled_at. scheduled_at is UTC.
Create a text post
curl -s -X POST "https://postwing.io/api/v2/posts" \
-H "Authorization: Bearer pw_live_…" \
-H "Content-Type: application/json" \
-d '{
"caption": "Faster publishing, now live.",
"scheduled_at": "2026-10-12T15:00:00Z",
"accounts": [
{ "id": "e5f6a7b8-c9d0-1234-ef01-345678901234" }
]
}'You get back three things that matter:
Response
{
"id": "7c1d2a8e-4b0f-4f8e-9a51-2f7f6b1c9d10",
"status": "scheduled",
"caption": "Faster publishing, now live.",
"scheduled_at": "2026-10-12T15:00:00.000Z",
"accounts": [
{
"id": "e5f6a7b8-c9d0-1234-ef01-345678901234",
"platform": "x",
"handle": "brand",
"status": "scheduled",
"url": null,
"error": null
}
],
"dashboard_url": "https://postwing.io/dashboard/post/7c1d2a8e-4b0f-4f8e-9a51-2f7f6b1c9d10"
}A standard post is about 280 characters. Longer text can fail on accounts that do not support long posts.
The same text can go to LinkedIn and Facebook. Connect each channel the way you connected X, then copy its id from the sync response.
Connect LinkedIn
curl -s -X POST "https://postwing.io/api/v2/accounts/connect" \
-H "Authorization: Bearer pw_live_…" \
-H "Content-Type: application/json" \
-d '{ "platform": "linkedin" }'Connect Facebook
curl -s -X POST "https://postwing.io/api/v2/accounts/connect" \
-H "Authorization: Bearer pw_live_…" \
-H "Content-Type: application/json" \
-d '{ "platform": "facebook" }'Open each url, finish the login, then sync once. Copy id where platform is linkedin, and where platform is facebook.
Add those ids to accounts on the create call. One request. Every account publishes at the same scheduled_at. Leave caption off an account to use the shared caption. Set caption when that channel needs different text. X is the short one here.
Create one text post for three channels
curl -s -X POST "https://postwing.io/api/v2/posts" \
-H "Authorization: Bearer pw_live_…" \
-H "Content-Type: application/json" \
-d '{
"caption": "We just shipped a faster way to publish updates.",
"scheduled_at": "2026-10-12T15:00:00Z",
"accounts": [
{
"id": "e5f6a7b8-c9d0-1234-ef01-345678901234",
"caption": "Faster publishing, now live."
},
{ "id": "d4e5f6a7-b8c9-0123-def0-234567890123" },
{ "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" }
]
}'Threads accepts this text with no media. Instagram, TikTok, Pinterest, and YouTube need an image or a video. Pinterest also needs board_id in its own options.
quote
quote_post creates a native quote post. Pass a tweet URL or a status ID. A link in the caption is not a quote.
Create a quote post
curl -s -X POST "https://postwing.io/api/v2/posts" \
-H "Authorization: Bearer pw_live_…" \
-H "Content-Type: application/json" \
-d '{
"caption": "Adding my take.",
"scheduled_at": "2026-10-12T16:00:00Z",
"accounts": [
{
"id": "e5f6a7b8-c9d0-1234-ef01-345678901234",
"options": {
"quote_post": "https://x.com/someone/status/1234567890"
}
}
]
}'A status ID works in place of the URL. Leave quote_post off for a normal post.
On that same options object, who_can_reply limits who can reply. Leave it off for everyone. following is people you follow. mentioned is only mentioned users. subscribers and verified are the other choices.
media
Media is optional. Use the same X account id. Images go out with the caption, up to 4. A video is one file.
Create an image post
curl -s -X POST "https://postwing.io/api/v2/posts" \
-H "Authorization: Bearer pw_live_…" \
-H "Content-Type: application/json" \
-d '{
"caption": "A look at the new office.",
"scheduled_at": "2026-10-12T18:00:00Z",
"accounts": [
{ "id": "e5f6a7b8-c9d0-1234-ef01-345678901234" }
],
"media": [
{ "url": "https://cdn.example.com/office.jpg", "type": "image" },
{ "url": "https://cdn.example.com/desk.jpg", "type": "image" }
]
}'Add more image objects in media, up to 4. For a video, send one item with type video instead.
The image or video URL has to be public HTTPS. Videos imported from a URL need to be 100MB or smaller. A file on your computer uses the direct upload URL, covered in the API docs.
questions
Yes. Create the post with caption, scheduled_at, and the X account id. Leave out media.