Do I need my Bluesky account password?
No, and you should not use it. An app password only posts. You create it under Settings, App Passwords, and you can revoke it at any time without touching your account.
Follow these steps to connect Bluesky post, send from TypeScript, and track delivery.
Bluesky speaks the AT Protocol, so there is no OAuth dance to sit through. You create an app password in your own Bluesky settings, paste it into yeetpost with your handle, and delete it whenever you like: every session made from that password dies with it, and your account password is never involved. In plain TypeScript this is a few lines in any Node process: a worker, a one-off script or a deploy step that already knows what to say.
| How you connect | An app password from Bluesky Settings, App Passwords, plus your handle |
|---|---|
| Connection slug | bluesky for the first account, then a slug built from the handle |
| Text limit | 300 graphemes, so a multi-codepoint emoji counts once |
| Images | Up to 4 per post, 1 MB each, png, jpeg, gif or webp |
| Alt text | Yes, sent with each image |
| Threads | Yes, up to 24 replies under the head post |
| First comment | No, LinkedIn only |
| Scheduling | Yes |
| Permalink | https://bsky.app/profile/<handle>/post/<record key> |
| Per-post fee | None |
Connect your Bluesky account to yeetpost with an app password, never your account password:
you.bsky.social) and the app password (xxxx-xxxx-xxxx-xxxx)https, and the default is https://bsky.socialThe account is identified by its DID, not by the handle, so renaming your handle later does not break the connection.
Paste your connection slug here and we'll auto-fill it into the code examples below.
Get your API key from the yeetpost dashboard:
Paste your API key here and we'll auto-fill it into the code examples below.
Install the yeetpost package:
npm install yeetpostExport your API key so the process can read it:
export YEETPOST_API_KEY=<your-api-key>Send a post:
import { yeetpost } from "yeetpost";
await yeetpost({
connection: "<your-connection-slug>",
text: "Hello from yeetpost!",
});To post to several connections in one call, use the JSON endpoint. It answers with one result per connection and accepts an Idempotency-Key, so a retry cannot double-post:
const response = await fetch("https://api.yeetpost.com/api/v2/posts", {
method: "POST",
headers: {
"x-api-key": process.env.YEETPOST_API_KEY!,
"content-type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
text: "Hello from yeetpost!",
connectionSlugs: ["<your-connection-slug>"],
}),
});
const { results } = await response.json();
for (const result of results) {
console.log(result.connectionSlug, result.status, result.url);
}Read each result.status rather than the HTTP status alone: one connection can fail while the rest go out.
Monitor all your posts from the yeetpost dashboard:
Review the Security Checklist to ensure your implementation is secure.
Each of these fails only the Bluesky item. Every other connection on the same request still goes out. The full vocabulary is in the errors reference.
| Code | Status | When |
|---|---|---|
| invalid_request | 400 | Text over 300 graphemes, in the post or in any thread segment |
| media_unsupported | 400 | More than 4 images, or an image over 1 MB |
| shape_unsupported | 400 | A firstComment was given. Bluesky has no first comment |
| unauthorized | 401 | The app password was revoked. Create a new one and reconnect |
| platform_rejected | 422 | The server said no, for example a duplicate post |
| internal_server_error | 500 | Our fault. Quote the req_id when you report it |
No, and you should not use it. An app password only posts. You create it under Settings, App Passwords, and you can revoke it at any time without touching your account.
The limit is graphemes, not code points. A flag or a skin-tone emoji is several code points and one grapheme, so it counts once. The check runs before anything is published.
Bluesky cannot delete a half-published thread on our behalf, so a refused reply stops the chain and leaves what already went out standing. Read threadCount in the result instead of assuming the whole thread landed.
No. Only the Bluesky item comes back failed with media_unsupported. Every other connection named in the same request still goes out.
It is a thin wrapper over the same HTTP endpoint, so anywhere you have fetch and can keep a secret it works. Do not run it in a browser bundle: the key would be readable by anyone loading the page.
Copy this guide (with your values) for use with LLMs or sharing with colleagues.