Post to X from a Python, Node or shell script

Send a POST request to https://octopost.ink/api/posts with your Octopost API key and the text. The post goes out on the X account you connected in the dashboard. No X developer app, no OAuth flow in your code and no token refresh: the key is the only credential your script holds.

What the X API asks of a small script

To post with the X API directly, you register a developer app, run an OAuth 2.0 PKCE flow in a browser to get a user token, store the refresh token, and refresh the access token every two hours. Images need a separate upload call first. For a cron job that posts a daily stat, that is more code than the job itself.

With Octopost the OAuth part happens once, in the dashboard, when you connect X. Your script sends JSON with a bearer key, and Octopost handles tokens, media upload and threads.

Copy-paste examples

Create an API key in the dashboard, connect your X account, then use any of these:

cURL (cron, shell scripts, CI)

curl -X POST https://octopost.ink/api/posts \
  -H "Authorization: Bearer $OCTOPOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"content": "Nightly build passed: 1,204 tests in 3m 12s", "platforms": ["twitter"], "published": true}'

Python (requests)

import os, requests

r = requests.post(
    "https://octopost.ink/api/posts",
    headers={"Authorization": f"Bearer {os.environ['OCTOPOST_API_KEY']}"},
    json={
        "content": "New episode is up: how we cut our build time in half",
        "platforms": ["twitter"],
        "scheduledFor": "2026-11-10T15:00:00Z",
    },
    timeout=30,
)
r.raise_for_status()
print(r.json()["post"]["id"])

TypeScript (Bun or Node 18+)

const res = await fetch("https://octopost.ink/api/posts", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.OCTOPOST_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    content: "v2.3.0 is out with offline mode",
    platforms: ["twitter"],
    mediaUrls: ["https://example.com/screenshot.png"],
    queueStatus: "pending", // wait for approval in the dashboard
  }),
});
if (!res.ok) throw new Error(await res.text());

published: true posts now, scheduledFor posts at a set time, queueStatus: "queued" drops it into your interval queue and "pending" holds it until you approve it. Add segments to post a thread (see posting a thread through the API). Every field is in the Posts API reference, and for release announcements from CI there is a ready GitHub Actions workflow.

Limits worth knowing

  • 280 characters per post, links count as 23. Longer text needs segments (a thread).
  • Up to 4 images or 1 video per post, given as public URLs.
  • scheduledFor must be ISO 8601 with a timezone offset, for example 2026-11-10T15:00:00Z.
  • X posts use units from your monthly allowance: 1 for a plain post, 13 for a post with a link. Basic includes 500 a month.
  • X is the live network. Bluesky, LinkedIn and Mastodon are in testing, and Threads opens after Meta approves the Octopost app.

Questions

Can I post to Twitter from Python without a developer account?

Yes. Connect your X account to Octopost once in the dashboard, then post from Python with an Octopost API key. Octopost publishes through its own X app, so you never create an X developer app.

How do I keep the API key safe in a script?

Read it from an environment variable or your CI secret store, as in the examples. Keys can be revoked and replaced in the dashboard at any time.

How do I post an image to X from a script?

Put a public image URL in mediaUrls. Octopost downloads it and uploads it to X for you, up to 4 images per post.

Can the script post as a different X account?

Yes, if you have connected more than one. Pass accounts: {"twitter": "@handle"}, or set a default account on the API key when you create it.

Will I know if a post failed?

With published: true the response includes publishResults for each network. Business plans can also subscribe to post.published and post.failed webhooks.

Start for $1

Every plan includes the MCP server and the REST API. Basic is $29 a month for 3 accounts, Premium $79 for 10, Business $199 for unlimited. The first 7 days cost $1, then the plan renews monthly unless you cancel.

Try it 7 days for $1