Examples
Copy-paste OpenTweet API recipes for the most common jobs. Every request goes to the REST base https://opentweet.io/api/v1 and carries your key as an Authorization: Bearer ot_your_key header. Get a key in your developer dashboard. New here? Start with the quickstart.
Keep keys secret
Never ship anot_ key in client-side code or a public repo. Run these examples server-side or from your terminal. In CI, set it as an environment variable.Verify a key
A quick GET /me confirms the key works before you post anything. A 401 means the key is missing or invalid.
curl https://opentweet.io/api/v1/me \
-H "Authorization: Bearer ot_your_key"Publish now
Post immediately with publish_now:
curl -X POST https://opentweet.io/api/v1/posts \
-H "Authorization: Bearer ot_your_key" \
-H "Content-Type: application/json" \
-d '{"text": "Hello from my code.", "publish_now": true}'Same call in Node:
const res = await fetch("https://opentweet.io/api/v1/posts", {
method: "POST",
headers: {
Authorization: "Bearer ot_your_key",
"Content-Type": "application/json",
},
body: JSON.stringify({ text: "Hello from my code.", publish_now: true }),
});
const data = await res.json();
console.log(data);Schedule for a time
Pass scheduled_date in ISO 8601. It is mutually exclusive with publish_now, so send one or the other, never both.
curl -X POST https://opentweet.io/api/v1/posts \
-H "Authorization: Bearer ot_your_key" \
-H "Content-Type: application/json" \
-d '{"text": "Launch day.", "scheduled_date": "2026-07-10T09:00:00Z"}'Batch-schedule a week
Create your posts first, then hand their IDs to POST /posts/batch-schedule with a list of schedules. Up to 50 per call.
curl -X POST https://opentweet.io/api/v1/posts/batch-schedule \
-H "Authorization: Bearer ot_your_key" \
-H "Content-Type: application/json" \
-d '{
"schedules": [
{ "post_id": "POST_ID_1", "scheduled_date": "2026-07-13T09:00:00Z" },
{ "post_id": "POST_ID_2", "scheduled_date": "2026-07-14T09:00:00Z" },
{ "post_id": "POST_ID_3", "scheduled_date": "2026-07-15T09:00:00Z" }
]
}'Post a thread
Set is_thread and pass thread_tweets. Use thread_media to attach media per tweet, one array per tweet.
curl -X POST https://opentweet.io/api/v1/posts \
-H "Authorization: Bearer ot_your_key" \
-H "Content-Type: application/json" \
-d '{
"is_thread": true,
"thread_tweets": ["First tweet in the thread.", "Second tweet in the thread."],
"thread_media": [[], []],
"publish_now": true
}'Same thread in Node:
const res = await fetch("https://opentweet.io/api/v1/posts", {
method: "POST",
headers: {
Authorization: "Bearer ot_your_key",
"Content-Type": "application/json",
},
body: JSON.stringify({
is_thread: true,
thread_tweets: ["First tweet in the thread.", "Second tweet in the thread."],
thread_media: [[], []],
publish_now: true,
}),
});
console.log(await res.json());Attach media
Upload the file to POST /upload first, then pass the returned URL to a post as media_urls.
# Step 1: upload a file, get back a media URL
curl -X POST https://opentweet.io/api/v1/upload \
-H "Authorization: Bearer ot_your_key" \
-F "file=@./chart.png"
# Step 2: attach the returned URL to a post via media_urls
curl -X POST https://opentweet.io/api/v1/posts \
-H "Authorization: Bearer ot_your_key" \
-H "Content-Type: application/json" \
-d '{
"text": "Ship report.",
"media_urls": ["https://cdn.opentweet.io/your-uploaded-file.png"],
"publish_now": true
}'Target a specific account
If you manage more than one X account, add x_account_id to the request. Omit it to post from your primary account.
curl -X POST https://opentweet.io/api/v1/posts \
-H "Authorization: Bearer ot_your_key" \
-H "Content-Type: application/json" \
-d '{
"text": "Posted from my second account.",
"publish_now": true,
"x_account_id": "YOUR_X_ACCOUNT_ID"
}'List your accounts to find each id:
curl https://opentweet.io/api/v1/accounts \
-H "Authorization: Bearer ot_your_key"Poll status
List posts and filter by status to see what is queued. Use it to confirm a schedule landed.
curl "https://opentweet.io/api/v1/posts?status=scheduled" \
-H "Authorization: Bearer ot_your_key"Handling errors and rate limits
Errors come back as JSON like{"error": "...", "code": "..."}. Do not retry permanent 4xx errors, fix the request instead. Only retry 429 Too Many Requests and 5xx responses, and back off exponentially between tries. Per-minute limits are 60 (Pro), 300 (Advanced), and 600 (Agency).Keep exploring
- • Quickstart: post your first tweet in three minutes.
- • REST API reference: every endpoint, field, and status code.
- • MCP tools reference: all 36 tools your agent can call.