# OpenTweet for AI Agents

OpenTweet is the posting layer for X (Twitter). Give an AI agent a real X account with one API key. No X developer account, no OAuth dance. Post, schedule, build threads, and publish long-form articles through an MCP server, a REST API, or a CLI. The same call can also post to Bluesky and LinkedIn.

## Get an API key

Create a key (prefix `ot_`) at https://opentweet.io/developer. Send it as `Authorization: Bearer ot_your_key`.

## MCP server (recommended for agents)

Hosted, nothing to install. Point any MCP client at:

- URL: `https://mcp.opentweet.io/mcp`
- Transport: `streamable-http`
- Header: `Authorization: Bearer ot_your_key`

Local (stdio):

```json
{
  "mcpServers": {
    "opentweet": {
      "command": "npx",
      "args": ["-y", "@opentweet/mcp-server"],
      "env": { "OPENTWEET_API_KEY": "ot_your_key" }
    }
  }
}
```

43 tools, including `opentweet_create_tweet`, `opentweet_create_thread`, `opentweet_schedule_tweet`, `opentweet_batch_schedule`, `opentweet_create_article`, `opentweet_get_analytics`, `opentweet_list_platforms`, and the evergreen queue. Full list: https://opentweet.io/docs/mcp/tools

## REST API

Base URL: `https://opentweet.io/api/v1`

Post now:

```bash
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 agent", "publish_now": true}'
```

Schedule for later (ISO 8601, mutually exclusive with publish_now):

```json
{ "text": "Launch day.", "scheduled_date": "2026-07-10T09:00:00Z" }
```

Post a thread:

```json
{ "is_thread": true, "thread_tweets": ["first tweet", "second tweet"] }
```

Batch schedule up to 50 posts: `POST /api/v1/posts/batch-schedule`. Full OpenAPI spec: https://opentweet.io/openapi.json

## Long-form X Articles

Write, schedule, and publish X Articles from markdown:

```bash
curl -X POST https://opentweet.io/api/v1/articles \
  -H "Authorization: Bearer ot_your_key" \
  -H "Content-Type: application/json" \
  -d '{"title": "How we cut our X API bill by 80%", "content_markdown": "## The problem\n\nWe were paying per read...", "scheduled_date": "2026-07-10T09:00:00Z"}'
```

Omit both `scheduled_date` and `publish_now` to save a draft; they are mutually exclusive. `POST /api/v1/articles/:id/publish` publishes an existing article now, `PUT /api/v1/articles/:id` edits or reschedules one, and `GET /api/v1/articles?status=failed` surfaces failures, which send no email or webhook. Over MCP the same six operations are `opentweet_create_article`, `opentweet_get_article`, `opentweet_list_articles`, `opentweet_update_article`, `opentweet_publish_article`, and `opentweet_delete_article`.

Articles need a paid plan (Pro, Advanced, or Agency, not trials) and X Premium on the connected account. Markdown is the source of truth and is converted to X's article format at publish time. X has no code block type yet, so fenced code lands as plain text.

## Cross-posting to Bluesky and LinkedIn

Bluesky issues no API keys. There is nothing to apply for, no developer account, no approval. OpenTweet connects over atproto OAuth, so no password and no App Password is ever pasted in, and self-hosted Bluesky servers work because the server is resolved from the handle.

LinkedIn connects with one LinkedIn sign-in in the dashboard, with no LinkedIn developer app. Posts go to the member's personal profile, not to Company Pages. The sign-in lasts 60 days, then the user reconnects in one click, and until they do the LinkedIn account reports `usable: false`.

Name the networks with `platforms`, an optional array of `"x"`, `"bluesky"` and `"linkedin"`:

```json
{ "text": "Shipped cross-posting today.", "platforms": ["x", "bluesky", "linkedin"], "publish_now": true }
```

**Omitting `platforms` is not the same as pinning the post to X.** A post that leaves the field out follows the account's auto cross-post setting, and when that setting is on it goes to every connected platform. An explicit value is always honoured exactly and is never expanded, so `["x"]` posts to X only even while the setting is on. If your integration must stay X-only, pass `platforms: ["x"]` rather than relying on leaving the field out.

Check where a post will go before you send it:

```bash
curl https://opentweet.io/api/v1/cross-posting -H "Authorization: Bearer ot_your_key"
```

Read three fields. `default_targets` is where an unspecified post is attempted. `deliverable_targets` is the subset that will actually land, so a network in the first list but not the second will be attempted and will fail until the user reconnects it. Per network, `connected` means an account row exists while `usable` means we can publish to it right now; an account whose grant has gone stale is `connected: true` and `usable: false`, so do not report it as working. `limits` carries each network's own caps. Over MCP the same read is `opentweet_list_platforms`.

Read the per-network outcome from `results`, one entry per network the post targeted, with `platform`, `status` (`published`, `failed`, or `skipped`), `post_id` (the post URN on LinkedIn), `url`, `uri` and `cid` on Bluesky, `part_ids` for threads, `error`, `skip_reason`, and `attempted_at`. Delivery is independent per network, so a partial outcome is normal and is not an error. Check `status` per entry rather than the top-level `posted` boolean, which is true when any target published.

Limits that produce a skip: Bluesky allows 300 characters, LinkedIn 3,000 (counted in UTF-16 code units, so an emoji costs two), X allows 280 or 25,000 on Premium. Bluesky takes up to 4 images at 2MB each, with alt text. Video cross-posting to Bluesky and LinkedIn is not supported. A post over a network's limit is skipped there with a stated reason and still goes out everywhere it fits. Nothing is truncated. Threads cross-post as real reply chains on X and Bluesky. LinkedIn has no threads, so a thread goes there as one post with the parts joined by a blank line.

Target a specific account per network with `x_account_id`, `bluesky_account_id` and `linkedin_account_id`. Leave one out and the primary account for that network is used. Bluesky and LinkedIn analytics do not exist, so OpenTweet's analytics cover X only. Full reference: https://opentweet.io/docs/cross-posting

What the feature does in the product, including thread and image behaviour: https://opentweet.io/features/cross-posting. Scheduling a Bluesky account, plan limits, and how the atproto OAuth connection works: https://opentweet.io/bluesky-scheduler. The LinkedIn side, including the 60-day sign-in: https://opentweet.io/linkedin-scheduler

## Supported agents

Claude Code, Codex, Cursor, Windsurf, ChatGPT, OpenClaw, Hermes, and any MCP or REST client.

## Docs

- Quickstart: https://opentweet.io/docs/quickstart
- Full docs: https://opentweet.io/docs
- Machine reference: https://opentweet.io/llms-full.txt
