Docs
Docs/AI Agents/Command Line (CLI)

Command Line (CLI)

Post to X, Bluesky, LinkedIn, Mastodon and Threads from your terminal with the octopost command. One binary for macOS and Linux, browser sign-in or an API key.

octopost posts from your terminal: publish now, schedule, add to your queue, check a post before it goes out, and see what happened. It's one self-contained binary for macOS and Linux, so there's nothing else to install.

Network status (2026-11): X is live. Threads opens after Meta approves the Octopost app. Bluesky, LinkedIn and Mastodon are in testing.

Install

curl -fsSL https://octopost.ink/cli/install.sh | sh

The script picks the build for your computer (macOS or Linux, Apple silicon/ARM or Intel/x64), checks its SHA-256 against SHA256SUMS, and puts octopost in ~/.local/bin. It doesn't use sudo. Set OCTOPOST_INSTALL_DIR to install somewhere else.

The macOS builds are signed with Great Work's Developer ID and notarized by Apple.

Prefer to download by hand? The binaries are at https://octopost.ink/cli/<platform>/octopost, where platform is darwin-arm64, darwin-x64, linux-x64 or linux-arm64.

Sign in

octopost login

Your browser opens on Octopost. Sign in, check the account and click Allow. The CLI saves the sign-in to ~/.config/octopost/credentials.json (readable only by you) and refreshes it on its own. It shows up as "Octopost CLI" under Connected apps on the API keys page, where Disconnect ends it.

On a server, in CI, or anywhere without a browser, use an API key instead:

octopost login --key op_...
# or, without saving anything:
OCTOPOST_API_KEY=op_... octopost accounts

octopost whoami shows how you're signed in. octopost logout forgets the saved sign-in.

Post

octopost post "Shipped the new export button today"

With no --to, the post goes to every network you have connected. Pick networks with --to (comma-separated or repeated; x means X/Twitter):

octopost post "Hello from the terminal" --to x,bluesky

Attach images or a video with --media. Local files are uploaded for you; URLs are used as they are. --alt sets alt text for the --media in the same position:

octopost post "The new dashboard" --to x \
  --media ./screenshot.png --alt "Dashboard with a weekly chart" \
  --media https://example.com/second.jpg

Make a thread with --thread, one per reply. Putting a link in a reply instead of the first post keeps X from showing the post to fewer people:

octopost post "I wrote up how we cut our build time in half" --to x \
  --thread "https://example.com/blog/faster-builds"

Read the text from a file or another command with -:

cat announcement.txt | octopost post - --to x

Other options: --account x=@handle picks the account when a network has several connected, and --allow-duplicate posts the same text again (Octopost refuses the same text to the same network within 7 days without it).

Check before posting

octopost preview "Is this too long for X? ..." --to x,bluesky

Shows each network's character count against its limit, the X units it would use, media checks and anything that would stop it. Nothing is saved or posted. It exits with status 1 when the post wouldn't go out, so scripts can stop early.

Schedule

octopost schedule "Weekly update is live" --to x --at "2026-11-10 09:00"

--at takes:

You writeMeans
+30m, +2h, +1dthat long from now
17:30, 9am, 9:30pmtoday at that time, or tomorrow if it has passed
2026-11-10 09:00that date and time in your computer's time zone
2026-11-10T09:00:00-05:00an exact ISO 8601 time

Add --approval to hold it until someone approves it from the email Octopost sends. See Scheduling Posts.

Queue

octopost queue "An evergreen tip about keyboard shortcuts" --to x
octopost queue "Draft from the release script" --to x --approval
octopost queue          # how many are queued, when the next one goes out

Queued posts go out one at a time during your active hours. See Post Queue.

See your posts

octopost list                      # newest first
octopost list --status failed      # published, scheduled, draft, queued, pending, partial, failed, retrying
octopost list --limit 50
octopost get <post-id>             # text, networks, links, errors
octopost retry <post-id>           # retry the networks that failed
octopost retry <post-id> --to x    # retry only X

When there are more posts, list prints the --cursor for the next page.

Accounts and usage

octopost accounts    # connected accounts, which one is the default per network
octopost usage       # plan, X units left this month, accounts used

Scripts and agents

Every command takes --json and prints the raw answer, the same JSON the MCP tools return:

octopost list --status scheduled --json | jq -r '.posts[].id'

Exit status is 0 on success, 1 when Octopost refused or the post failed (including a partly published post), and 2 for a mistake in the command itself. Error messages go to stderr.

Coding agents (Claude Code, Cursor, Codex) can run octopost like any other command once you've signed in. If your agent supports MCP, connecting https://octopost.ink/mcp directly gives it the same tools without a shell; see Agent Integration.

Reference

octopost login [--key op_...] [--no-browser]
octopost logout | whoami
octopost post "text" [--to x,bluesky] [--media FILE|URL ...] [--alt "text" ...] [--thread "text" ...]
octopost schedule "text" --at TIME [--approval] [post options]
octopost queue ["text"] [--approval] [post options]
octopost preview "text" [post options]
octopost list [--status S] [--limit N] [--cursor C]
octopost get <post-id>
octopost retry <post-id> [--to x]
octopost accounts | usage

Global options: --json, --key op_... (one command only), --base URL (another Octopost server; also OCTOPOST_URL), --help, --version.

The CLI talks to https://octopost.ink/mcp, which accepts both browser sign-ins and API keys. Your sign-in lives in $XDG_CONFIG_HOME/octopost/credentials.json (default ~/.config/octopost).