
Security News
upm Launches as a Fast, Tiny Package Manager Written in TypeScript
upm uses Node.js to deliver fast npm installs in about 250 KB, with a JavaScript API and security defaults.
@chirpie/sdk
Advanced tools
One typed client for posting to X, Bluesky, LinkedIn, Threads, Mastodon, Instagram, Facebook and Telegram. Chirpie handles the OAuth, the token refresh, the media upload and the per-platform quirks, so posting to eight networks is eight calls to the same method rather than eight SDKs.
Threads, scheduling, deletion and analytics are all in here too, and the same account you post from works identically through the CLI, the MCP server, and the REST API.
npm install @chirpie/sdk
import { ChirpieClient } from "@chirpie/sdk";
const chirpie = new ChirpieClient({ apiKey: process.env.CHIRPIE_API_KEY! });
const accounts = await chirpie.listAccounts();
const post = await chirpie.createPost({
account_id: accounts[0].id,
text: "Shipped a new release.",
});
console.log(post.status, post.platform_post_url);
Get an API key from the dashboard, or run npx chirpie login, which saves one to ~/.chirpie/config.json.
const chirpie = new ChirpieClient({
apiKey: "chirpie_sk_...",
baseUrl: "https://chirpie.ai", // optional, this is the default
});
Or pick up credentials the CLI already saved:
import { ChirpieClient, requireConfig } from "@chirpie/sdk";
const config = requireConfig(); // CHIRPIE_API_KEY, else ~/.chirpie/config.json
const chirpie = new ChirpieClient({ apiKey: config.api_key, baseUrl: config.base_url });
CHIRPIE_API_KEY takes precedence over the config file. CHIRPIE_BASE_URL overrides the base URL.
// Publish now
await chirpie.createPost({ account_id, text: "Hello" });
// Publish later. Absolute ISO 8601 with a timezone; relative offsets are rejected.
await chirpie.createPost({ account_id, text: "Later", schedule_at: "2026-04-01T14:00:00Z" });
// With media. Chirpie downloads the URLs and uploads them to the platform.
await chirpie.createPost({ account_id, text: "Look", media_urls: ["https://example.com/a.png"] });
// Or upload a file first, and describe it for people using a screen reader.
const media = await chirpie.uploadMedia({ data: bytes, filename: "shot.png" });
await chirpie.createPost({
account_id,
text: "Look",
media: [{ id: media.id, alt: "The new dashboard" }],
});
// Read back, newest first. Page with limit/offset; a short page is the last one.
const posts = await chirpie.listPosts({ status: "published", limit: 20, offset: 0 });
const one = await chirpie.getPost(posts[0].id);
// Edit a post that has not gone out yet. Leaving `schedule_at` out keeps the time
// it already has, so this never publishes anything.
await chirpie.updatePost(one.id, { text: "Now with the typo fixed" });
await chirpie.updatePost(one.id, { schedule_at: "2027-04-02T09:00:00Z" });
// Delete. A published post is removed from the platform too, except on Instagram,
// which offers no delete API. Deleting any post of a scheduled thread cancels the
// whole thread, and `cancelled_ids` lists every post that went with it.
const { cancelled_ids } = await chirpie.deletePost(one.id);
The request field is schedule_at; the response field is scheduled_at. Sending scheduled_at back is rejected rather than published immediately.
Name account_ids instead of account_id to publish the same post to up to 25 accounts at once.
const { group_id, results } = await chirpie.createPost({
account_ids: [x_account, bsky_account, linkedin_account],
text: "Shared text",
account_configurations: {
// This one account publishes its own text and no media.
[bsky_account]: { text: "Shorter, for Bluesky", media: [] },
},
});
for (const result of results) {
if (!result.success) console.error(result.account_id, result.error?.message);
}
// Every post of the group, read back together.
const posts = await chirpie.listPosts({ group_id });
results carries one entry per account, in the order they were named. The call resolves even when some accounts failed, so check success on each: the accounts that worked stay published. Anything that fails the request as a whole (a character limit, a media rule, a spent quota) throws as usual and nothing is published. Each account may be named once.
An override says only what differs: an absent field inherits the request's own, and naming any media field replaces the shared media for that account outright. Every post carries the group_id it belongs to, or null when it went to a single account.
Threads work the same way, with posts replacing the whole thread for one account:
await chirpie.createThread({
account_ids: [x_account, bsky_account],
posts: [{ text: "One" }, { text: "Two" }],
account_configurations: {
[bsky_account]: { posts: [{ text: "A" }, { text: "B" }, { text: "C" }] },
},
});
const thread = await chirpie.createThread({
account_id,
posts: [{ text: "One" }, { text: "Two" }, { text: "Three" }],
schedule_at: "2026-04-01T14:00:00Z", // optional
});
2 to 25 posts. X, Bluesky, Threads, Mastodon and Telegram publish them as a native reply chain; LinkedIn, Instagram and Facebook publish each item as a standalone post. A thread counts as N posts against your quota.
// Accounts plus your plan's limits in one call
const { accounts, accounts_limit, accounts_active } = await chirpie.listAccountsWithLimits();
// Connect. OAuth platforms return a URL for the user to open.
const { authorization_url } = await chirpie.connectXAccount();
await chirpie.connectBlueskyAccount({ platform: "bluesky", identifier: "you.bsky.social", app_password: "xxxx-xxxx-xxxx-xxxx" });
await chirpie.connectTelegramAccount({ platform: "telegram", bot_token: "...", chat_id: "@yourchannel" });
// Choose which accounts publish. Deactivating frees a plan slot and keeps the
// account connected, but CANCELS its scheduled posts: `scheduled_posts` says how
// many would go, `canceled_posts` on the result says how many did.
const off = await chirpie.deactivateAccount(accounts[0].id);
await chirpie.activateAccount(accounts[1].id);
// Done with an account? Disconnecting frees a plan slot and cancels its scheduled
// posts like deactivating, but also removes the stored credential, so connecting it
// again means authorizing it on the platform again. Published posts are kept.
const gone = await chirpie.disconnectAccount(accounts[2].id);
console.log(gone.disconnected, gone.canceled_posts);
Also available: connectLinkedInAccount (pass "pages", or call connectLinkedInPagesAccount, for the LinkedIn Pages you administer: coming soon), connectMastodonAccount, and connectThreadsAccount, connectInstagramAccount and connectFacebookAccount (coming soon). Threads, Instagram, Facebook, Pinterest, TikTok, YouTube and Google Business Profile are coming soon; accounts already connected keep posting as normal.
Connect X accounts through your own X app so posts bill your X API credits, and X link posts are not surcharged. Full walkthrough: chirpie.ai/docs/x-byo-keys.
await chirpie.setXKeys({ client_id, client_secret, label: "Acme social app" });
await chirpie.getXKeysStatus(); // never returns the secret
await chirpie.removeXKeys();
const metrics = await chirpie.getPostAnalytics(post.id); // cached for 1 hour
const { key, expires_at } = await chirpie.createKey("My Bot"); // shown once, expires in 90 days
await chirpie.listKeys();
await chirpie.revokeKey(id);
import { ChirpieApiError, ChirpieError } from "@chirpie/sdk";
try {
await chirpie.createPost({ account_id, text });
} catch (err) {
if (err instanceof ChirpieApiError) {
err.code; // "usage_limit_exceeded", "rate_limited", "not_found", ...
err.status; // 400, 401, 402, 404, 429, 502
err.message; // the API's own message, safe to show a user
} else if (err instanceof ChirpieError) {
err.message; // network or configuration problem
}
}
The full code list is at chirpie.ai/docs/errors.
Every input and response type is exported, including ApiPost, ApiThread, ApiAccount, AccountList, ApiAnalytics, ApiKeyInfo, CreatePostInput, CreateThreadInput and ListPostsOptions.
MIT
FAQs
Chirpie SDK: TypeScript client for the Chirpie social media API
The npm package @chirpie/sdk receives a total of 690 weekly downloads. As such, @chirpie/sdk popularity was classified as not popular.
We found that @chirpie/sdk demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
upm uses Node.js to deliver fast npm installs in about 250 KB, with a JavaScript API and security defaults.

Company News
Socket is joining the OpenJS Security Stewardship Program to fund Node.js vulnerability research, maintainer remediation, and security releases.

Security News
Two compromised GitHub Actions were re-enabled with malicious tags intact, exposing thousands of downstream repositories to Mini Shai-Hulud.