X (Twitter)
Postgale publishes to X with the X API v2: posts, threads, polls, quotes, replies, images, GIFs and videos, with replies, metrics, mentions and direct messages. Everything is available over the REST API (paths relative to https://postgale.com/api/v1) and the MCP server (create_post, list_comments, moderate_comment, get_analytics, send_message, start_conversation, x_engagement, x_search_posts, x_lookup). Each connected X user is one account with platform x.
Connect an account
Ask for a connect link and send the account owner to it. They authorize Postgale on X (OAuth 2.0); tokens are stored encrypted and refreshed automatically.
curl https://postgale.com/api/v1/connect/x?externalId=cust-1 \
-H 'Authorization: Bearer sk_…'
# {"authUrl":"https://x.com/i/oauth2/authorize?…","expiresInSeconds":600}Publish
Text is limited to 280 weighted characters (URLs count 23, emoji and CJK characters 2), or 25,000 for accounts with an X Premium subscription. The limit is checked when the post is created. Videos are processed by X after upload; while that runs the target stays publishing and the worker finishes it.
curl -X POST https://postgale.com/api/v1/posts \
-H 'Authorization: Bearer sk_…' \
-H 'Content-Type: application/json' \
-d '{"content":"We just shipped scheduled threads 🧵","platforms":[{"accountId":"acc_x","platformSpecificData":{"threadItems":[{"content":"We just shipped scheduled threads 🧵"},{"content":"Write the thread once, pick a time, done.","mediaUrl":"https://media.postgale.com/demo.png","altText":"Thread editor"}],"replySettings":"following"}}],"publishNow":true}'curl -X POST https://postgale.com/api/v1/posts \
-H 'Authorization: Bearer sk_…' \
-H 'Content-Type: application/json' \
-d '{"content":"Which feature next?","platforms":[{"accountId":"acc_x","platformSpecificData":{"poll":{"options":["Bluesky","LinkedIn","YouTube"],"duration_minutes":1440}}}],"publishNow":true}'| platformSpecificData | What it does |
|---|---|
customContent | Text used for X instead of the post content (e.g. a shorter version) |
mediaUrl / mediaUrls | Public https URLs: up to 4 images (5 MB each), or 1 GIF (15 MB), or 1 video (512 MB) |
mediaType | text | image | gif | video (default: from the URL extension) |
altText / altTexts | Image alt text (≤1000 characters), per image |
threadItems | A thread (≤25 posts) published as a reply chain: [{content, mediaUrl?, mediaUrls?, altText?}]; replaces the post content |
replyToTweetId | Post id or URL to reply to (see the reply rule below) |
quoteTweetId | Post id or URL to quote (not with media or a poll) |
replySettings | following | mentionedUsers | subscribers | verified (default: everyone) |
poll | {options: 2-4 strings ≤25 characters, duration_minutes: 5-10080 (default 1440)}; not with media, threads or quotes |
madeWithAi / paidPartnership | Disclosure labels |
sensitiveMedia | {adultContent?, graphicViolence?, other?}: content warning on the media |
geoRestriction | {countries: [ISO codes, ≤25]}: only show the media in these countries |
longVideo | Upload the video as amplify_video (long videos, eligible accounts) |
Endpoints
| Method | Path | What it does |
|---|---|---|
| POST | /posts | Publish or schedule (targets with an X accountId and platformSpecificData above) |
| GET | /inbox/comments?accountId=&platformPostId= | Replies to a post (last 7 days) |
| POST | /inbox/comments/{id}/reply | Reply to a reply |
| POST | /inbox/comments/{id}/hide | Hide or unhide a reply to your post |
| POST | /posts/{id}/edit | Edit (X Premium, within 1 hour; creates a new post id) |
| POST | /posts/{id}/unpublish | Delete from X |
| GET | /analytics/posts/{id} | Impressions, likes, replies, reposts, quotes, bookmarks, clicks |
| GET | /accounts/{id}/mentions | Posts mentioning the account |
| POST / DELETE | /x/accounts/{id}/reposts | likes | bookmarks | {postId}: repost, like or bookmark, and undo |
| POST / DELETE | /x/accounts/{id}/following | {userId | username}: follow and unfollow |
| GET | /x/accounts/{id}/search?query= | Recent search (last 7 days) |
| GET | /x/accounts/{id}/posts/{postId} | One post |
| GET | /x/accounts/{id}/users/{username} | One user (incl. receivesYourDm) |
| POST | /inbox/conversations | Start a DM conversation: {accountId, participantUsername | participantId, text | mediaUrl} |
| POST | /inbox/conversations/{id}/messages | Send a DM in a conversation |
| POST | /inbox/conversations/sync | Backfill DM conversations ({accountId}) |
Direct messages
X has no 24-hour messaging window: you can message anyone whose DM settings accept messages from the account, and only X lets you write first. Start with POST /inbox/conversations, then reply with POST /inbox/conversations/{id}/messages. Encrypted X Chat conversations are not available through the X API.
curl -X POST https://postgale.com/api/v1/inbox/conversations \
-H 'Authorization: Bearer sk_…' \
-H 'Content-Type: application/json' \
-d '{"accountId":"acc_x","participantUsername":"happy_customer","text":"Thanks for the shoutout! Here is 20% off: THANKS20"}'Comments, mentions and inbound events
Replies to your posts are listed from X's recent search (the last 7 days). When the instance enables the inbox poll (X_INBOX_POLL_MINUTES), new replies arrive as comment.received, other mentions as mention.received, and DMs as message.received webhooks.
X's rules to know
- Replies: X only accepts API replies to a post whose author mentioned or quoted the account (or in the account's own conversations). Other replies fail with 403.
- Likes, follows and quote posts are restricted by X to Enterprise API access; they may fail with
missing_permission. - Edits need X Premium and must happen within 1 hour (up to 5 times); X gives the edited post a new id.
- Costs: X bills every API call (posts, reads, DMs) to the app. Posts containing a link cost noticeably more on X's side.
- Rate limits (per account, 15 minutes): 100 posts, 50 deletes, 15 DM sends; 429 answers
rate_limited.