Twitter / X API
Schedule and automate Twitter/X posts with SocialSyncerAPI — Tweets, threads, images, videos, GIFs, and polls.
Quick Reference
- Character limit: 280 (free) / 25,000 (Premium)
- Images per post: 4 (or 1 GIF)
- Videos per post: 1
- Image formats: JPEG, PNG, WebP, GIF
- Image max size: 5 MB (images), 15 MB (GIFs)
- Video formats: MP4, MOV
- Video max size: 512 MB
- Video max duration: 140 seconds
- Threads: Yes (via threadItems)
- Scheduling: Yes
- Analytics: Yes
Before You Start
customContent to provide a shorter Twitter version for cross-platform posts.
- Duplicate tweets are rejected (even very similar content)
- Free accounts: 280 characters, Premium accounts: 25,000 characters
- X API costs are passed through at exact rate, with zero markup
- Common rates: Posts Read $0.005, Content Create $0.015, Content Create with URL $0.200
Quick Start
Post a tweet in under 60 seconds:
Python (httpx)
import httpx
resp = httpx.post(
"https://api.socialsyncerapi.com/v1/posts",
headers={"Authorization": "Bearer sk_your_api_key"},
json={
"content": "Hello from SocialSyncerAPI!",
"platforms": [
{"platform": "twitter", "accountId": "YOUR_ACCOUNT_ID"}
],
"publishNow": True
}
)
print("Tweet posted!", resp.json()["id"]) curl
curl -X POST https://api.socialsyncerapi.com/v1/posts \
-H "Authorization: Bearer sk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"content": "Hello from SocialSyncerAPI!",
"platforms": [
{"platform": "twitter", "accountId": "YOUR_ACCOUNT_ID"}
],
"publishNow": true
}' Node.js (fetch)
const resp = await fetch("https://api.socialsyncerapi.com/v1/posts", {
method: "POST",
headers: {
"Authorization": "Bearer sk_your_api_key",
"Content-Type": "application/json"
},
body: JSON.stringify({
content: "Hello from SocialSyncerAPI!",
platforms: [
{ platform: "twitter", accountId: "YOUR_ACCOUNT_ID" }
],
publishNow: true
})
});
const data = await resp.json();
console.log("Tweet posted!", data.id); Content Types
Text Tweet
A simple text-only tweet. Keep it under 280 characters for free accounts.
Text Tweet — Python
import httpx
resp = httpx.post(
"https://api.socialsyncerapi.com/v1/posts",
headers={"Authorization": "Bearer sk_your_api_key"},
json={
"content": "Just shipped a new feature. Check it out!",
"platforms": [
{"platform": "twitter", "accountId": "YOUR_ACCOUNT_ID"}
],
"publishNow": True
}
) Text Tweet — curl
curl -X POST https://api.socialsyncerapi.com/v1/posts \
-H "Authorization: Bearer sk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"content": "Just shipped a new feature. Check it out!",
"platforms": [
{"platform": "twitter", "accountId": "YOUR_ACCOUNT_ID"}
],
"publishNow": true
}' Text Tweet — Node.js
const resp = await fetch("https://api.socialsyncerapi.com/v1/posts", {
method: "POST",
headers: {
"Authorization": "Bearer sk_your_api_key",
"Content-Type": "application/json"
},
body: JSON.stringify({
content: "Just shipped a new feature. Check it out!",
platforms: [
{ platform: "twitter", accountId: "YOUR_ACCOUNT_ID" }
],
publishNow: true
})
}); Image Tweet
Attach up to 4 images per tweet. JPEG, PNG, WebP, and GIF formats are supported.
Image Tweet — Python
import httpx
resp = httpx.post(
"https://api.socialsyncerapi.com/v1/posts",
headers={"Authorization": "Bearer sk_your_api_key"},
json={
"content": "Check out this photo!",
"mediaItems": [
{"type": "image", "url": "https://cdn.example.com/photo.jpg"}
],
"platforms": [
{"platform": "twitter", "accountId": "YOUR_ACCOUNT_ID"}
],
"publishNow": True
}
) Image Tweet — curl
curl -X POST https://api.socialsyncerapi.com/v1/posts \
-H "Authorization: Bearer sk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"content": "Check out this photo!",
"mediaItems": [
{"type": "image", "url": "https://cdn.example.com/photo.jpg"}
],
"platforms": [
{"platform": "twitter", "accountId": "YOUR_ACCOUNT_ID"}
],
"publishNow": true
}' Image Tweet — Node.js
const resp = await fetch("https://api.socialsyncerapi.com/v1/posts", {
method: "POST",
headers: {
"Authorization": "Bearer sk_your_api_key",
"Content-Type": "application/json"
},
body: JSON.stringify({
content: "Check out this photo!",
mediaItems: [
{ type: "image", url: "https://cdn.example.com/photo.jpg" }
],
platforms: [
{ platform: "twitter", accountId: "YOUR_ACCOUNT_ID" }
],
publishNow: true
})
}); Video Tweet
Attach a single video per tweet. MP4 and MOV formats, up to 512 MB, max 140 seconds.
Video Tweet — Python
import httpx
resp = httpx.post(
"https://api.socialsyncerapi.com/v1/posts",
headers={"Authorization": "Bearer sk_your_api_key"},
json={
"content": "New product demo",
"mediaItems": [
{"type": "video", "url": "https://cdn.example.com/demo.mp4"}
],
"platforms": [
{"platform": "twitter", "accountId": "YOUR_ACCOUNT_ID"}
],
"publishNow": True
}
) Video Tweet — curl
curl -X POST https://api.socialsyncerapi.com/v1/posts \
-H "Authorization: Bearer sk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"content": "New product demo",
"mediaItems": [
{"type": "video", "url": "https://cdn.example.com/demo.mp4"}
],
"platforms": [
{"platform": "twitter", "accountId": "YOUR_ACCOUNT_ID"}
],
"publishNow": true
}' Video Tweet — Node.js
const resp = await fetch("https://api.socialsyncerapi.com/v1/posts", {
method: "POST",
headers: {
"Authorization": "Bearer sk_your_api_key",
"Content-Type": "application/json"
},
body: JSON.stringify({
content: "New product demo",
mediaItems: [
{ type: "video", url: "https://cdn.example.com/demo.mp4" }
],
platforms: [
{ platform: "twitter", accountId: "YOUR_ACCOUNT_ID" }
],
publishNow: true
})
}); Thread (Multi-Tweet)
Create Twitter threads with multiple connected tweets using platformSpecificData.threadItems. Each item becomes a reply to the previous tweet and can have its own content and media.
threadItems is provided, the top-level content field is used only for display and search purposes, it is NOT published. You must include your first tweet as threadItems[0].Thread — Python
import httpx
resp = httpx.post(
"https://api.socialsyncerapi.com/v1/posts",
headers={"Authorization": "Bearer sk_your_api_key"},
json={
"platforms": [{
"platform": "twitter",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"threadItems": [
{
"content": "1/ Starting a thread about API design",
"mediaItems": [{"type": "image", "url": "https://cdn.example.com/image1.jpg"}]
},
{"content": "2/ First, always use proper HTTP methods..."},
{"content": "3/ Second, version your APIs from day one..."},
{"content": "4/ Finally, document everything! /end"}
]
}
}],
"publishNow": True
}
) Thread — curl
curl -X POST https://api.socialsyncerapi.com/v1/posts \
-H "Authorization: Bearer sk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"platforms": [{
"platform": "twitter",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"threadItems": [
{
"content": "1/ Starting a thread about API design",
"mediaItems": [{"type": "image", "url": "https://cdn.example.com/image1.jpg"}]
},
{"content": "2/ First, always use proper HTTP methods..."},
{"content": "3/ Second, version your APIs from day one..."},
{"content": "4/ Finally, document everything! /end"}
]
}
}],
"publishNow": true
}' Thread — Node.js
const resp = await fetch("https://api.socialsyncerapi.com/v1/posts", {
method: "POST",
headers: {
"Authorization": "Bearer sk_your_api_key",
"Content-Type": "application/json"
},
body: JSON.stringify({
platforms: [{
platform: "twitter",
accountId: "YOUR_ACCOUNT_ID",
platformSpecificData: {
threadItems: [
{
content: "1/ Starting a thread about API design",
mediaItems: [{ type: "image", url: "https://cdn.example.com/image1.jpg" }]
},
{ content: "2/ First, always use proper HTTP methods..." },
{ content: "3/ Second, version your APIs from day one..." },
{ content: "4/ Finally, document everything! /end" }
]
}
}],
publishNow: true
})
}); Poll
Create a Twitter/X poll by providing platformSpecificData.poll. Polls are mutually exclusive with media attachments and threads.
Poll — Python
import httpx
resp = httpx.post(
"https://api.socialsyncerapi.com/v1/posts",
headers={"Authorization": "Bearer sk_your_api_key"},
json={
"content": "Which feature should we ship next?",
"platforms": [{
"platform": "twitter",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"poll": {
"options": ["Dark mode", "New analytics", "More integrations"],
"duration_minutes": 1440
}
}
}],
"publishNow": True
}
) Poll — curl
curl -X POST https://api.socialsyncerapi.com/v1/posts \
-H "Authorization: Bearer sk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"content": "Which feature should we ship next?",
"platforms": [{
"platform": "twitter",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"poll": {
"options": ["Dark mode", "New analytics", "More integrations"],
"duration_minutes": 1440
}
}
}],
"publishNow": true
}' Poll — Node.js
const resp = await fetch("https://api.socialsyncerapi.com/v1/posts", {
method: "POST",
headers: {
"Authorization": "Bearer sk_your_api_key",
"Content-Type": "application/json"
},
body: JSON.stringify({
content: "Which feature should we ship next?",
platforms: [{
platform: "twitter",
accountId: "YOUR_ACCOUNT_ID",
platformSpecificData: {
poll: {
options: ["Dark mode", "New analytics", "More integrations"],
duration_minutes: 1440
}
}
}],
publishNow: true
})
}); Reply
Use platformSpecificData.replyToTweetId to publish a tweet as a reply to an existing tweet.
replyToTweetId cannot be combined with replySettings. For threads, only the first tweet replies to the target.Reply — Python
import httpx
resp = httpx.post(
"https://api.socialsyncerapi.com/v1/posts",
headers={"Authorization": "Bearer sk_your_api_key"},
json={
"content": "Replying via SocialSyncerAPI",
"platforms": [{
"platform": "twitter",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"replyToTweetId": "1748391029384756102"
}
}],
"publishNow": True
}
) Reply — curl
curl -X POST https://api.socialsyncerapi.com/v1/posts \
-H "Authorization: Bearer sk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"content": "Replying via SocialSyncerAPI",
"platforms": [{
"platform": "twitter",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"replyToTweetId": "1748391029384756102"
}
}],
"publishNow": true
}' Reply — Node.js
const resp = await fetch("https://api.socialsyncerapi.com/v1/posts", {
method: "POST",
headers: {
"Authorization": "Bearer sk_your_api_key",
"Content-Type": "application/json"
},
body: JSON.stringify({
content: "Replying via SocialSyncerAPI",
platforms: [{
platform: "twitter",
accountId: "YOUR_ACCOUNT_ID",
platformSpecificData: {
replyToTweetId: "1748391029384756102"
}
}],
publishNow: true
})
}); Media Requirements
Images
- Max images: 4 per tweet
- Formats: JPEG, PNG, WebP, GIF
- Max file size: 5 MB (images), 15 MB (GIFs)
- Min dimensions: 4 x 4 px
- Max dimensions: 8192 x 8192 px
- Recommended: 1200 x 675 px (16:9)
GIFs
- Max per tweet: 1 (consumes all 4 image slots)
- Max file size: 15 MB
- Max dimensions: 1280 x 1080 px
- Behavior: Auto-plays in timeline
Videos
- Max videos: 1 per tweet
- Formats: MP4, MOV
- Max file size: 512 MB
- Max duration: 140 seconds standard; up to ~10 min with Premium long video
- Min dimensions: 32 x 32 px
- Max dimensions: 1920 x 1200 px
- Frame rate: 40 fps max
- Bitrate: 25 Mbps max
- Recommended: 1280 x 720 px, 30 fps, H.264, AAC 128 kbps
Platform-Specific Data
All fields go inside platformSpecificData on the Twitter platform entry.
replyToTweetId— string — ID of an existing tweet to reply to. For threads, only the first tweet replies to the target.replySettings— string — "following" | "mentionedUsers" | "subscribers" | "verified". Controls who can reply. Cannot be combined with replyToTweetId.threadItems— Array of objects — Complete sequence of tweets in a thread. First item becomes root tweet. When provided, top-level content is NOT published.poll— object — Create a poll. Mutually exclusive with media and threads. poll.options: string[] (2–4 choices, max 25 chars each). poll.duration_minutes: number (5 min to 7 days).longVideo— boolean — Enable long video uploads (over 140 sec). Requires X Premium.geoRestriction— object — Restrict media visibility to specific countries. geoRestriction.countries: array of uppercase ISO 3166-1 alpha-2 codes, max 25.
Constraints
- Character limit: 280 (free) / 25,000 (Premium)
- URL character count: Always 23 characters regardless of actual length
- Emoji character count: 2 characters each
- Images per tweet: 4 (or 1 GIF)
- Videos per tweet: 1
- Video size: 512 MB max
- Video duration: 140 sec standard
- Poll options: 2–4 choices, max 25 characters each
- Poll duration: 5 min to 7 days
- Duplicate tweets: Rejected (even very similar content)
What You Can't Do
These features are not available through Twitter's API:
- Create Spaces
- Post to Communities
- Pin tweets to profile
- Add Twitter Cards (must be configured on the destination URL via meta tags)
- Post as a personal DM broadcast
Common Errors
- "Tweet text is too long" — Exceeds 280 character limit. URLs = 23 chars, emojis = 2 chars.
- "X does not allow duplicate tweets" — Same or very similar content was already posted. Modify the text.
- "Rate limit hit. Please wait 10 minutes" — Reduce posting frequency. Space posts at least 4 minutes apart.
- "Missing tweet.write scope" — Reconnect the account with all required scopes.
- Token expired — Reconnect the account. Subscribe to the account.disconnected webhook.