Skip to main content
Platforms

Twitter / X

Post tweets, threads, polls, and media to Twitter/X via the PostPeer API.

Overview

Publish tweets, threads, polls, and rich media to Twitter/X through a single API endpoint. PostPeer handles OAuth, token refreshes, and rate limits.

X is the most expensive API PostPeer talks to, and it's the only platform with non-uniform credit pricing. X bills us per request, and posts containing a link are priced at roughly 13× the rate of plain text. We pass that through:

  • Post on X (no URL): 5 credits
  • Post on X with a URL: 50 credits
  • Every other platform: 1 credit per post

If you're cross-posting to several platforms, expect the X line to dominate your usage. See Pricing below for the full breakdown.

Quick Start

1. Connect a Twitter Account

curl https://api.postpeer.dev/v1/connect/twitter \
  -H "x-access-key: YOUR_API_KEY"

Response:

{
	"url": "https://twitter.com/i/oauth2/authorize?..."
}

Redirect the user to the url. After authorization, the account is connected to your project. See Connect Accounts for the full OAuth flow.

2. Get the Account ID

curl https://api.postpeer.dev/v1/connect/integrations \
  -H "x-access-key: YOUR_API_KEY"

Find the integration with "platform": "twitter" and note the id.

3. Post a Tweet

curl -X POST "https://api.postpeer.dev/v1/posts" \
  -H "x-access-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Hello from PostPeer!",
    "platforms": [
      { "platform": "twitter", "accountId": "abc123" }
    ],
    "publishNow": true
  }'
import PostPeer from '@postpeer/node';

const client = new PostPeer();
const { data } = await client.posts.create({
	body: {
		content: 'Hello from PostPeer!',
		platforms: [{ platform: 'twitter', accountId: 'abc123' }],
		publishNow: true,
	},
});
from postpeer import PostPeer

with PostPeer() as client:
    post = client.posts.create(
        content="Hello from PostPeer!",
        platforms=[{"platform": "twitter", "accountId": "abc123"}],
        publish_now=True,
    )

Response:

{
	"success": true,
	"status": "published",
	"postId": "post_xyz789",
	"platforms": [
		{
			"platform": "twitter",
			"success": true,
			"platformPostUrl": "https://twitter.com/you/status/123456"
		}
	]
}

Features

Text Posts

Post a standard tweet with up to 280 characters.

Images & Videos

Attach media using the mediaItems array:

curl -X POST "https://api.postpeer.dev/v1/posts" \
  -H "x-access-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Check out this photo!",
    "platforms": [
      { "platform": "twitter", "accountId": "abc123" }
    ],
    "mediaItems": [
      { "type": "image", "url": "https://example.com/photo.jpg" }
    ],
    "publishNow": true
  }'

Supported media:

  • Images: JPG, PNG, GIF, WebP (up to 4 per tweet)
  • Videos: MP4 (1 per tweet)
  • GIFs: animated GIF (1 per tweet)

Threads

Create multi-tweet threads using platformSpecificData.threadItems:

curl -X POST "https://api.postpeer.dev/v1/posts" \
  -H "x-access-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "1/ Here'\''s a thread about our API...",
    "platforms": [
      {
        "platform": "twitter",
        "accountId": "abc123",
        "platformSpecificData": {
          "threadItems": [
            { "text": "2/ Connect any social account via OAuth" },
            { "text": "3/ Post to all platforms with one request" }
          ]
        }
      }
    ],
    "publishNow": true
  }'

Polls

Create polls with 2-4 options:

curl -X POST "https://api.postpeer.dev/v1/posts" \
  -H "x-access-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "What should we build next?",
    "platforms": [
      {
        "platform": "twitter",
        "accountId": "abc123",
        "platformSpecificData": {
          "poll": {
            "options": ["Analytics", "Webhooks", "Bulk upload"],
            "durationMinutes": 1440
          }
        }
      }
    ],
    "publishNow": true
  }'

Community Posts

Publish a tweet into a Twitter Community by providing platformSpecificData.communityId. Optionally set shareWithFollowers to true to also share the post with your followers.

curl -X POST "https://api.postpeer.dev/v1/posts" \
  -H "x-access-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Excited to share this with the community!",
    "platforms": [
      {
        "platform": "twitter",
        "accountId": "abc123",
        "platformSpecificData": {
          "communityId": "1234567890",
          "shareWithFollowers": true
        }
      }
    ],
    "publishNow": true
  }'
FieldTypeDescription
communityIdstringThe ID of the Twitter Community to post into
shareWithFollowersbooleanAlso share the Community post with your followers (default false)

Reply to a Tweet

Post a tweet as a reply to an existing tweet by providing platformSpecificData.replyToTweetId:

curl -X POST "https://api.postpeer.dev/v1/posts" \
  -H "x-access-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Great point!",
    "platforms": [
      {
        "platform": "twitter",
        "accountId": "abc123",
        "platformSpecificData": {
          "replyToTweetId": "1234567890"
        }
      }
    ],
    "publishNow": true
  }'

Replying is not always available. It depends on the reply settings the original author picked for their tweet (e.g. everyone, people you follow, verified accounts, or mentioned users only). If your account doesn't match the author's selection, the reply will fail.

Reply Settings

Control who can reply to your tweet using platformSpecificData.replySettings:

  • Omit replySettings to allow everyone (default)
  • "following"
  • "mentionedUsers"
  • "subscribers"
  • "verified"

Long Posts

By default PostPeer rejects tweets over 280 characters. If your connected account has X Premium or Premium+, set platformSpecificData.longPost to true to publish a single long post (up to 25,000 characters) instead of splitting it into a thread:

{
	"content": "A very long post that goes well beyond 280 characters...",
	"platforms": [
		{
			"platform": "twitter",
			"accountId": "YOUR_ACCOUNT_ID",
			"platformSpecificData": {
				"longPost": true
			}
		}
	]
}

With longPost enabled, PostPeer skips the 280-character pre-check and lets X enforce the real limit for the account. If the account is not eligible for long posts, X rejects it and the error is returned to you. This is a single write request, so it costs the same as a normal tweet (cheaper than a multi-tweet thread).

Platform-Specific Data

Pass this object in platformSpecificData when posting to Twitter/X, including Community post options.

FieldTypeRequiredValues and constraintsDescription
communityIdstringNoCommunity ID to publish this post into.
shareWithFollowersbooleanNoWhen posting to a Community, also share the post with followers.
replyToTweetIdstringNoTweet ID to reply to.
replySettingsstringNofollowing, mentionedUsers, subscribers, verified
threadItemsobject[]NoAdditional tweets to chain as a thread. The root tweet uses post.content.
threadItems[].contentstringYes, in each threadItems[] itemText of this thread tweet (max 280 chars)
threadItems[].mediaItemsobject[]No
threadItems[].mediaItems[].typestringYes, in each threadItems[].mediaItems[] itemimage, video, gif, document
threadItems[].mediaItems[].urlstringYes, in each threadItems[].mediaItems[] itemFormat: uri
threadItems[].mediaItems[].thumbnailstringNoFormat: uriThumbnail image URL for video items. Supported on YouTube regular videos (not Shorts). JPEG, PNG, or GIF, max 2 MB, min 640 px wide.
longPostbooleanNoOpt in to long posts (over 280 characters). Skips PostPeer's 280-character pre-check so X can accept up to 25,000 characters for eligible Premium / Premium+ accounts. If the connected account is not eligible, X rejects the post and the error is surfaced back. No extra API cost.
pollobjectNoCannot be combined with media or threadItems.
poll.optionsstring[]Yes, when poll is providedItems: 2–42–4 poll options.
poll.durationMinutesnumberYes, when poll is providedRange: 5–10080Poll duration in minutes (5 min – 7 days).

Limits

LimitValue
Character limit280 per tweet
Images per tweet4
Videos per tweet1
Thread lengthNo hard limit
Poll options2-4

Pricing

Most platforms cost a flat 1 credit per post. X is the exception, because X charges PostPeer differently for plain tweets and tweets with links. Rather than spread that markup across every platform's base rate, we keep it isolated to X.

ActionCreditsNotes
Post on X (no URL)5Text, image, video, GIF, poll, thread without links
Post on X with a URL50Body contains http:// or https://
Post on any other platform1Instagram, LinkedIn, TikTok, YouTube, Pinterest, Facebook, Threads, Bluesky
Analytics request1One credit per call, regardless of how many posts come back

The split mirrors X's own developer pricing:

X API operationX's rateWhy it matters
Content: Create$0.015 per requestPlain tweet, no link
Content: Create (with URL)$0.200 per requestAny http(s):// in the body, ~13× the plain rate
Posts: Read$0.005 per resourceAnalytics lookups, post fetches

This is X's pricing, not PostPeer's markup. The full rate sheet lives at docs.x.com/x-api/getting-started/pricing. If you don't need the link to be a real hyperlink, dropping it from the body takes the same tweet from 50 credits down to 5.

On this page