For developers and agents

The Bluesky posting APIin one REST call

The Bluesky API (AT Protocol) is free and needs no API key: you log in as an account and write records. OpenTweet's Bluesky posting API wraps it in one REST call, POST /api/v1/posts with platforms ["bluesky"], and you connect accounts through Bluesky OAuth, so no app password is stored.

Last updated: October 2026

7-day free trial. Cancel anytime.

What the Bluesky API gives you for free

No key, no developer account, no paid tier. Also no queue, no facets and no retries.

Bluesky runs on the AT Protocol. A post is a record of type app.bsky.feed.post written to the account's repository, and any code that is logged in as that account can write one. There is nothing to apply for and nothing to pay. Why there is no API key.

The limits are rate limits per account: 5,000 write points an hour and 35,000 a day, with a new post costing 3 points. What that means in posts per day.

What the raw API leaves to you: a URL in the text is not clickable unless the record carries a facet with its byte range, images must fit 2,000,000 bytes each, a thread needs the root and parent reference on every reply, and there is no field that delays a post. Scheduling means running your own cron or queue.

Raw atproto vs the OpenTweet Bluesky API

Both write the same record to Bluesky. The difference is how much code sits around it.

Raw atproto (@atproto/api)OpenTweet
PriceFreeFrom $11.99 a month, Bluesky on every plan
API keyNone. You log in as the accountOne ot_ key for every network
AuthAn app password in your environment, or atproto OAuth you build and host yourselfBluesky OAuth once in the dashboard, scoped to writing posts and uploading images
Clickable links and mentionsYou compute facets, for example with RichText.detectFacetsFacets added for you. A mention that does not resolve stays plain text
ImagesuploadBlob, then an images embed. You keep each under 2,000,000 bytesmedia_urls, up to 4, resized to fit 2,000,000 bytes
ThreadsSet reply root and parent on every postis_thread + thread_tweets, up to 25 posts per call
SchedulingYour own cron or queuescheduled_date on the same call
RetriesYour codeRetried only when nothing reached Bluesky, so a half-posted thread is never posted twice
Several accountsOne session per account1 / 3 / 10 by plan, picked with bluesky_account_id
Same post to X and LinkedInSeparate integrationsAdd them to platforms
Reading feeds, replies, searchYesNo. Publishing only

OpenTweet limits come from its Bluesky adapter: 300 graphemes and 3,000 bytes of text, up to 4 images. Per-network limits in full.

Create a Bluesky post with the API

Connect your Bluesky account once in the dashboard, create an API key, then call POST /api/v1/posts. Use scheduled_date to queue it or "publish_now": true to post now.

schedule a Bluesky post
curl -X POST https://opentweet.io/api/v1/posts \
  -H "Authorization: Bearer ot_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Release notes are up: https://example.com/changelog",
    "platforms": ["bluesky"],
    "scheduled_date": "2026-10-20T14:00:00Z"
  }'

Every network the post reached comes back in results, with the Bluesky AT URI, CID and a bsky.app link:

response excerpt
"results": [
  {
    "platform": "bluesky",
    "status": "published",
    "post_id": "3l5x7k2qz3c2t",
    "uri": "at://did:plc:abc123/app.bsky.feed.post/3l5x7k2qz3c2t",
    "cid": "bafyreib2rxk3rh6kzwq...",
    "url": "https://bsky.app/profile/you.bsky.social/post/3l5x7k2qz3c2t"
  }
]

With more than one Bluesky account connected, pick one with bluesky_account_id. Leave it out and the primary account is used. GET /api/v1/cross-posting lists what is connected. Name platforms on every call from an unattended script, because a post with no platforms follows the account's auto cross-post setting. Full API reference.

The same post with @atproto/api, for comparison

The official TypeScript SDK. This is the version OpenTweet runs; the latest on npm in October 2026 is 0.24.3.

post.mjs (@atproto/api 0.20.42)
// npm install @atproto/api   (written against 0.20.42)
import { AtpAgent, RichText } from '@atproto/api'

const agent = new AtpAgent({ service: 'https://bsky.social' })
await agent.login({
  identifier: 'yourname.bsky.social',
  password: process.env.BLUESKY_APP_PASSWORD,
})

// Without facets, the URL below is plain text, not a link
const rt = new RichText({ text: 'Release notes are up: https://example.com/changelog' })
await rt.detectFacets(agent)

await agent.post({
  text: rt.text,
  facets: rt.facets,
  createdAt: new Date().toISOString(),
})

That is the whole happy path for one text post on one account. Images add an uploadBlob call per file, threads add reply references, and scheduling adds a process that is awake at the right minute. Log in once and reuse the session: session creation is capped at 30 per 5 minutes and 300 a day per account.

To see exactly what detectFacets produces for your text, paste it into the Bluesky facet generator. For the Python SDK and a bot that runs on a schedule, see post to Bluesky with Python and build a Bluesky bot.

What the OpenTweet layer does

The parts of a Bluesky integration that are easy to get wrong, handled before the record is written.

No app password

Accounts connect through Bluesky OAuth with a write-only scope for posts and image uploads. OpenTweet never sees or stores the account password or an app password.

Links that work

Link, mention and hashtag facets are computed for every post, so a URL is clickable on Bluesky instead of arriving as plain text.

Images that fit

Bluesky caps each image at 2,000,000 bytes, well below X. Images over that are resized before upload, so the same media_urls work on both.

A real queue

scheduled_date puts the post in a queue that publishes on time, shows on the calendar, and stays editable with PUT /api/v1/posts/{id}.

Need X too? One endpoint posts to X and Bluesky at once, and LinkedIn joins with one more entry in platforms.

For AI agents: the Bluesky MCP server

If the agent runs in Claude, Cursor, ChatGPT or another MCP client, skip the HTTP code. The hosted OpenTweet MCP server exposes 43 tools on the same account.

the tool call the agent makes
opentweet_create_tweet({
  text: "Release notes are up: https://example.com/changelog",
  platforms: ["bluesky"],
  scheduled_date: "2026-10-20T14:00:00Z"
})

Setup for each client, and why OAuth beats an app password in an agent config, is on the Bluesky MCP server page.

Honest limits

What the OpenTweet Bluesky API does not do, as of October 2026.

Not supported on Bluesky

  • Video. A post with a video is skipped on Bluesky with a reason
  • Link cards. Links are clickable, but no preview card is attached
  • Bluesky analytics. There are no Bluesky stats in OpenTweet
  • Auto-splitting. Text over 300 graphemes is skipped, not split into a thread
  • Reading. No feeds, replies, search or follower data

Plan limits that apply

  • Posts a day: 20 on Pro, 100 on Advanced, 300 on Agency
  • Posts with a link a day: 10 on Advanced, 20 on Agency. Pro needs a link credit pack
  • Over the link allowance with no credits, links are removed before posting
  • Threads sent through the API: up to 25 posts

When to skip OpenTweet

If you need Bluesky video, link cards, feeds or analytics, or you only ever post from one account and are happy running your own cron, call the Bluesky API directly. It is free.

Bluesky API pricing through OpenTweet

Bluesky charges nothing. OpenTweet is a monthly plan with the API, MCP server and CLI included.

Pro

$11.99 / month

1 Bluesky account, plus the same number of X and LinkedIn accounts.

Advanced

$29 / month

3 Bluesky accounts, plus the same number of X and LinkedIn accounts.

Agency

$49 / month

10 Bluesky accounts, plus the same number of X and LinkedIn accounts.

No per-post fee. Every plan starts with a 7-day free trial. Compare plans.

Frequently asked questions

Is the Bluesky API free?

Yes. Bluesky has no API key, no developer account and no paid tier. Posting is writing a record to the account’s repository, and that costs nothing. Your costs are hosting your own code and staying inside the per-account rate limits: 5,000 write points an hour and 35,000 a day, where a new post costs 3 points.

Do I need a Bluesky API key?

No. There is nothing to apply for. A script logs in as the account, usually with an app password, and an app can use atproto OAuth, where the client_id is the URL of a metadata document you host. OpenTweet uses OAuth, so you sign in to Bluesky once in the dashboard and your code holds only an OpenTweet ot_ key.

How do I create a post with the Bluesky API?

With @atproto/api, log in with AtpAgent, run RichText.detectFacets on your text so links and mentions are clickable, then call agent.post with the text, the facets and a createdAt timestamp. Through OpenTweet, send POST https://opentweet.io/api/v1/posts with your text and "platforms": ["bluesky"], plus scheduled_date or "publish_now": true.

Can I schedule Bluesky posts through an API?

Not with the Bluesky API itself: creating a record publishes it immediately, and there is no field that delays it. You run your own cron or queue. OpenTweet takes a scheduled_date in ISO 8601 on the same call and publishes at that time, and the post stays editable with PUT /api/v1/posts/{id} until it goes out.

Can one API call post to Bluesky, X and LinkedIn?

Yes. Put "bluesky", "x" and "linkedin" in the platforms array. Each network gets its own native post and reports its own outcome in the results array. A post that breaks one network’s rules, such as going over 300 graphemes on Bluesky, is skipped there with a reason and still goes out on the others.

Does the OpenTweet Bluesky API support video or link cards?

No. A post with a video goes out on X but is skipped on Bluesky with a reason. Links are clickable through facets, but no link card (the app.bsky.embed.external embed) is attached. OpenTweet also has no Bluesky analytics. If you need those, call the Bluesky API directly.

How much does the OpenTweet Bluesky API cost?

The API is included on every plan. Pro is $11.99 a month with 1 Bluesky account, Advanced is $29 with 3, and Agency is $49 with 10. Each plan includes the same number of X and LinkedIn accounts, and there is no per-post fee.

More on Bluesky API access: posting without an API key and how many posts a day the rate limits allow.

Post to Bluesky with one API call

Bluesky OAuth, scheduling, clickable links and cross-posting to X and LinkedIn, on one API key. From $11.99 a month.