X API

How to post on X/Twitter
via the API

Post and schedule on X (Twitter) programmatically using the API. Create a key, connect X, and write the post.

the whole flow

Five steps, then it is scheduled

You call PostWing. PostWing publishes to X at the time you set. The post also shows up on your calendar.

  1. 01Create an API key
  2. 02Connect X
  3. 03Create the post
  4. 04Cross-post to more channels
  5. 05Confirm it
01

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/v1.

02

Connect X

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/v1/accounts" \
  -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/v1/accounts/sync" \
  -H "Authorization: Bearer pw_live_…"

In the response, find the object where platform is x and copy account_id. That is the id you send when you create the post.

Sync response

{
  "synced_count": 1,
  "accounts": [
    {
      "account_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/v1/accounts" \
  -H "Authorization: Bearer pw_live_…"
03

Create the post

A caption is enough on X. Send the text, a future time, and the X account_id you copied after sync. This call creates the post. X does not publish until you confirm in the next step. scheduled_at is UTC.

Create a text post

curl -s -X POST "https://postwing.io/api/v1/posts/preview" \
  -H "Authorization: Bearer pw_live_…" \
  -H "Content-Type: application/json" \
  -d '{
  "caption": "Faster publishing, now live.",
  "scheduled_at": "2026-10-12T15:00:00Z",
  "targets": [
    { "account_id": "e5f6a7b8-c9d0-1234-ef01-345678901234" }
  ]
}'

You get back three things that matter:

  • preview: the caption, time, and X account. Check this before you confirm.
  • confirmation_token: you send this in the next step
  • expires_in_seconds: 300, so the token lasts 5 minutes

Response

{
  "preview": {
    "caption": "Faster publishing, now live.",
    "scheduled_at": "2026-10-12T15:00:00.000Z",
    "post_type": "text",
    "targets": [
      {
        "account_id": "e5f6a7b8-c9d0-1234-ef01-345678901234",
        "platform": "x",
        "handle": "brand"
      }
    ]
  },
  "confirmation_token": "eyJ…signature",
  "expires_in_seconds": 300
}

A standard post is about 280 characters. Longer text can fail on accounts that do not support long posts.

04

Cross-post to all 8 platforms

The same text can go to LinkedIn and Facebook. Connect each channel the way you connected X, then copy its account_id from the sync response.

Connect LinkedIn

curl -s -X POST "https://postwing.io/api/v1/accounts" \
  -H "Authorization: Bearer pw_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "platform": "linkedin" }'

Connect Facebook

curl -s -X POST "https://postwing.io/api/v1/accounts" \
  -H "Authorization: Bearer pw_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "platform": "facebook" }'

Open each url, finish the login, then sync once. Copy account_id where platform is linkedin, and where platform is facebook.

Add those ids to targets on the create call. One request, one confirm. Every account publishes at the same scheduled_at. Leave caption off a target 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/v1/posts/preview" \
  -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",
  "targets": [
    {
      "account_id": "e5f6a7b8-c9d0-1234-ef01-345678901234",
      "caption": "Faster publishing, now live."
    },
    { "account_id": "d4e5f6a7-b8c9-0123-def0-234567890123" },
    { "account_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_ids on its own target.

05

Confirm the post

When the X preview looks right, confirm with the token from that response. Use the token as-is. Do not invent one.

Confirm

curl -s -X POST "https://postwing.io/api/v1/posts/confirm" \
  -H "Authorization: Bearer pw_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "confirmation_token": "eyJ…signature" }'

The response includes view_url. Open it to see the post on your calendar. X publishes at the time you set. LinkedIn and Facebook publish then too, when you added them. You can edit or cancel later from the dashboard or with the same API.

If confirm says the token expired, create the post again and confirm the new token within 5 minutes.

quote

Quote a post

quote_tweet_url 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/v1/posts/preview" \
  -H "Authorization: Bearer pw_live_…" \
  -H "Content-Type: application/json" \
  -d '{
  "caption": "Adding my take.",
  "scheduled_at": "2026-10-12T16:00:00Z",
  "targets": [
    {
      "account_id": "e5f6a7b8-c9d0-1234-ef01-345678901234",
      "platform_options": {
        "quote_tweet_url": "https://x.com/someone/status/1234567890"
      }
    }
  ]
}'

A status ID works in place of the URL. Leave quote_tweet_url off for a normal post.

On that same platform_options object, reply_settings limits who can reply. Leave it off for everyone. following is people you follow. mentionedUsers is only mentioned users. subscribers and verified are the other options.

media

Add images or a video

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/v1/posts/preview" \
  -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",
  "targets": [
    { "account_id": "e5f6a7b8-c9d0-1234-ef01-345678901234" }
  ],
  "media_items": [
    { "url": "https://cdn.example.com/office.jpg", "type": "image" },
    { "url": "https://cdn.example.com/desk.jpg", "type": "image" }
  ]
}'

Add more image objects in media_items, 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.

Details worth knowing

  • scheduled_at has to be a future time in ISO 8601, usually UTC.
  • Creating the post does not publish it. Confirm is the step that schedules it.
  • X accepts a caption alone. Images, up to 4, and one video are optional.
  • A standard post is about 280 characters. Longer text can fail on accounts that do not support long posts.
  • quote_tweet_url creates a native quote post from a tweet URL or status ID.
  • The confirmation token expires in 5 minutes and only works for the user who created the post.
  • API access is included on every plan, from $9.99/mo. Posts count as normal posts on your account.

questions

X API FAQ

Yes. Create the post with caption, scheduled_at, and the X account_id. Leave out media_items. Then confirm with the token.

ready when you are

Schedule your next X post from the API

Create a key, connect X, and create the post.