Make a bot in TypeScript
This guide writes a bot that posts to Bluesky with
@atproto/api, the official
TypeScript SDK. It runs with Node.js 20 or later.
1. Create an app password
Sign in to Bluesky as the bot, then open Settings → Privacy and security → App passwords and create one. A bot signs in with an app password, never with the account's main password: you can revoke it at any time without touching the account.
2. Set up the project
mkdir my-bot && cd my-bot
npm init -y
npm pkg set type=module
npm install @atproto/api
npm install --save-dev tsx typescriptKeep the credentials out of the code, in environment variables:
export BLUESKY_HANDLE="weather.bluesky.bot"
export BLUESKY_APP_PASSWORD="xxxx-xxxx-xxxx-xxxx"3. Write the bot
// bot.ts: post once as the bot. Run with `npx tsx bot.ts`.
import { AtpAgent, RichText } from "@atproto/api";
// The bot's own account, signed in with an app password
// (Settings > Privacy and security > App passwords), never its main password.
const identifier = process.env.BLUESKY_HANDLE ?? "";
const password = process.env.BLUESKY_APP_PASSWORD ?? "";
if (!identifier || !password) {
throw new Error("Set BLUESKY_HANDLE and BLUESKY_APP_PASSWORD.");
}
const agent = new AtpAgent({ service: "https://bsky.social" });
await agent.login({ identifier, password });
// RichText finds links, mentions and hashtags, so they are clickable.
const text = new RichText({
text: "Today: sunny, 21°C. Forecast at https://example.com #weather",
});
await text.detectFacets(agent);
const { uri } = await agent.post({
text: text.text,
facets: text.facets,
createdAt: new Date().toISOString(),
});
console.log(`Posted ${uri}`);What it does:
AtpAgenttalks tohttps://bsky.social, the server of accounts hosted by Bluesky. If the bot lives on another server (a self-hosted PDS), use that server's URL.loginopens a session with the handle and the app password.RichTextanddetectFacetsturn the link and#weatherinto clickable facets: a plaintextwould show them as plain characters.postcreates the post and returns itsat://URI.
4. Run it
npx tsx bot.tsErrors worth handling
- Rate limits: Bluesky answers
429when an account or a server does too much. Back off and retry later; see Best practices. - Expired credentials: a revoked app password makes
loginfail. Create a new one. - Long posts: a post holds 300 characters (graphemes). Cut longer text, or post a thread.
Next
- Schedule posts to run the bot on its own.
- Give it a bluesky.bot handle.
- More in Bluesky's own bot starter templates and posts guide.