npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

boosty-api

v0.1.0

Published

Unofficial TypeScript client for the internal Boosty (boosty.to) API. Full endpoint coverage, works on Bun and Node.

Readme

boosty-api

TypeScript Bun License

🇬🇧 English · 🇷🇺 Русский


English

Unofficial TypeScript client for the internal Boosty API — a port and extension of barsikus007/boosty, Bun-first, with broad endpoint coverage (posts, comments, blog, user, media, social, feed, messaging, income).

⚠️ This is an undocumented internal API. It can change without notice. Use it only to access your own content and within Boosty's Terms of Service.

Features

  • 📦 Resource namespacesapi.posts, api.comments, api.blog, api.user, api.media, api.social, api.feed, api.messaging, api.income.
  • 🔓 Anonymous mode — public posts/comments/profile work without a token.
  • 🔐 Auth with auto-refresh — bearer token, proactive (by expires_at) and reactive (401) refresh with a single-retry guard.
  • 🧩 Tolerant models — unknown response fields are preserved (resilient to Boosty schema drift).
  • 🏷️ Honest status tags — every method is annotated @verified / @experimental / @unverified.

Quick start

npm install boosty-api      # or: bun add boosty-api / pnpm add boosty-api

Works on both Node 18+ and Bun (ships ESM + CJS builds and type declarations).

import { API } from "boosty-api";

const api = new API(); // anonymous if no auth.json

// Public post (works without a token)
const post = await api.posts.get("boosty", "c9fb8a19-c45e-4602-9942-087c3af28c1b");
console.log(post.title, post.url);
const [text] = post.text; // rendered text + entities

// Blog feed, profile, subscription levels
const { data } = await api.posts.list("boosty", { limit: 10 });
const profile = await api.blog.profile("boosty");
const levels = await api.blog.subscriptionLevels("boosty");

Interactive browser login (api's interactiveLogin) needs the optional puppeteer dependency: npm i puppeteer. Everything else works without it.

Authentication

Public endpoints work anonymously. For private content and account operations you need a token in auth.json (kept out of git via .gitignore):

cp auth.json.example auth.json   # fill in manually, or:
bun run login                    # interactive browser login (Puppeteer)

Manual way: log in at boosty.to → DevTools → Application → Cookies. Cookie auth is URL-encoded JSON, cookie _clientId is the device id. auth.json uses snake_case, the cookie uses camelCase — map them:

| auth cookie | auth.json field | |---|---| | accessToken | access_token | | refreshToken | refresh_token | | expiresAt | expires_at | | _clientId (cookie) | device_id |

A non-empty device_id is required for token refresh.

API status legend

✅ verified — confirmed by a live response (200, or 401 "auth required" = path exists). 🟡 experimental — path reconstructed from the web client, not confirmed end-to-end. ❔ unverified — exact path not found yet.

| Namespace | Methods (selected) | Status | |---|---|---| | posts | list get create update delete getDeferredAccess updateDeferredAccess | ✅ reads / 🟡 writes | | comments | list replies create like unlike | ✅ list / 🟡 rest | | blog | profile subscribers subscriptionLevels blacklist | ✅ | | user | current | ✅ | | media | list (media_album, type+limit_by=media) | ✅ | | social | likePost unlikePost voteOption removeVote | 🟡 | | feed | posts searchBlogs | ✅ | | messaging | dialogs messages sendMessage notifications | ✅ / ❔ notifications | | income | sales (POST form; may be "Category disabled" per account) | ⚠️ |

Legacy aliases api.getPost, api.getPostComments, api.request are kept for compatibility.

Recipes

Pagination — list endpoints return extra.offset; loop until it is empty:

let offset: string | undefined;
do {
  const page = await api.posts.list("boosty", { limit: 20, offset });
  for (const post of page.data ?? []) console.log(post.title);
  offset = page.extra?.offset;
} while (offset);

Send a message — build content blocks with buildMessage:

import { API, buildMessage } from "boosty-api";

const blocks = buildMessage(["Hi! Here is your link:", { link: "https://example.com/sub" }]);
await api.messaging.sendMessage(dialogId, blocks);

Handle errors — failed requests throw BoostyError with status code and body:

import { BoostyError } from "boosty-api";

try {
  await api.posts.get("boosty", "non-existent-id");
} catch (e) {
  if (e instanceof BoostyError) console.error(e.statusCode, e.body);
  else throw e;
}

Note on declare fields

Models extend BaseObject, which copies response fields in its constructor. Bun transpiles class fields with define semantics, so a plain title?: string would run this.title = undefined after super(data) and wipe the copied value. Therefore every model field populated from a response is declared with declare (type-only, no runtime code). See tsconfig.json.

src/
  index.ts        public entry point (barrel exports)
  client.ts       API core: request() + resource assembly + legacy aliases
  http.ts         HTTPClient (native fetch), RequestOptions, ApiCore, BaseResource, BoostyError
  auth/           Auth, AuthData, FileAuthDataResolver
  resources/      posts, comments, blog, user, media, social, feed, messaging, income
  types/          models (extend BaseObject; fields use `declare`)
  utils/          logging, post (renderText), video (getVideoSizes), browser_login (Puppeteer), consts
examples/         demo.ts, login.ts

License

MIT.


Русский

Неофициальный TypeScript-клиент внутреннего API Boosty — порт и расширение barsikus007/boosty, на Bun, с широким покрытием эндпоинтов (posts, comments, blog, user, media, social, feed, messaging, income).

⚠️ Это недокументированный внутренний API. Он может измениться без предупреждения. Используйте только для доступа к своему контенту и в рамках правил Boosty.

Возможности

  • 📦 Resource-неймспейсыapi.posts, api.comments, api.blog, api.user, api.media, api.social, api.feed, api.messaging, api.income.
  • 🔓 Анонимный режим — публичные посты/комментарии/профиль работают без токена.
  • 🔐 Авторизация с авто-refresh — bearer-токен, проактивный (по expires_at) и реактивный (401) refresh с защитой от рекурсии.
  • 🧩 Толерантные модели — неизвестные поля ответа сохраняются (устойчивость к изменениям схемы Boosty).
  • 🏷️ Честные метки статуса — у каждого метода JSDoc @verified / @experimental / @unverified.

Быстрый старт

npm install boosty-api      # либо: bun add boosty-api / pnpm add boosty-api

Работает на Node 18+ и Bun (поставляется ESM + CJS сборка и декларации типов).

import { API } from "boosty-api";

const api = new API(); // анонимно, если нет auth.json

// Публичный пост (работает без токена)
const post = await api.posts.get("boosty", "c9fb8a19-c45e-4602-9942-087c3af28c1b");
console.log(post.title, post.url);
const [text] = post.text; // отрендеренный текст + entities

// Лента блога, профиль, уровни подписки
const { data } = await api.posts.list("boosty", { limit: 10 });
const profile = await api.blog.profile("boosty");
const levels = await api.blog.subscriptionLevels("boosty");

Интерактивный вход через браузер (interactiveLogin) требует опциональной зависимости puppeteer: npm i puppeteer. Всё остальное работает без неё.

bun run dev          # запуск examples/demo.ts
bun run typecheck    # tsc --noEmit

Авторизация

Публичные эндпоинты работают анонимно. Для приватного контента и аккаунт-операций нужен токен в auth.json (исключён из git через .gitignore):

cp auth.json.example auth.json   # заполнить вручную, либо:
bun run login                    # интерактивный вход через браузер (Puppeteer)

Вручную: войти на boosty.to → DevTools → Application → Cookies. Cookie auth — это URL-encoded JSON, cookie _clientId — это device id. auth.json использует snake_case, cookie — camelCase, сопоставь:

| cookie auth | поле auth.json | |---|---| | accessToken | access_token | | refreshToken | refresh_token | | expiresAt | expires_at | | _clientId (cookie) | device_id |

Для refresh обязателен непустой device_id.

Легенда статусов API

✅ verified — подтверждён живым ответом (200, либо 401 «нужен токен» = путь существует). 🟡 experimental — путь восстановлен из веб-клиента, не проверен сквозным ответом. ❔ unverified — точный путь пока не найден.

| Неймспейс | Методы (выборочно) | Статус | |---|---|---| | posts | list get create update delete getDeferredAccess updateDeferredAccess | ✅ чтение / 🟡 запись | | comments | list replies create like unlike | ✅ list / 🟡 остальное | | blog | profile subscribers subscriptionLevels blacklist | ✅ | | user | current | ✅ | | media | list (media_album, type+limit_by=media) | ✅ | | social | likePost unlikePost voteOption removeVote | 🟡 | | feed | posts searchBlogs | ✅ | | messaging | dialogs messages sendMessage notifications | ✅ / ❔ notifications | | income | sales (POST form; может быть «Category disabled» у аккаунта) | ⚠️ |

Легаси-алиасы api.getPost, api.getPostComments, api.request сохранены для совместимости.

Рецепты

Пагинация — list-методы возвращают extra.offset; крутим, пока он не пуст:

let offset: string | undefined;
do {
  const page = await api.posts.list("boosty", { limit: 20, offset });
  for (const post of page.data ?? []) console.log(post.title);
  offset = page.extra?.offset;
} while (offset);

Отправка сообщения — собираем блоки контента через buildMessage:

import { API, buildMessage } from "boosty-api";

const blocks = buildMessage(["Привет! Вот твоя ссылка:", { link: "https://example.com/sub" }]);
await api.messaging.sendMessage(dialogId, blocks);

Обработка ошибок — неуспешные запросы бросают BoostyError со статусом и телом:

import { BoostyError } from "boosty-api";

try {
  await api.posts.get("boosty", "non-existent-id");
} catch (e) {
  if (e instanceof BoostyError) console.error(e.statusCode, e.body);
  else throw e;
}

Про поля declare

Модели наследуют BaseObject, который копирует поля ответа в конструкторе. Bun транспилирует поля класса с define-семантикой, поэтому обычное title?: string выполнилось бы как this.title = undefined после super(data) и затёрло бы скопированное значение. Поэтому все поля моделей, наполняемые из ответа, объявлены через declare (только тип, без рантайм-кода). См. tsconfig.json.

src/
  index.ts        публичная точка входа (barrel-экспорт)
  client.ts       ядро API: request() + сборка ресурсов + легаси-алиасы
  http.ts         HTTPClient (нативный fetch), RequestOptions, ApiCore, BaseResource, BoostyError
  auth/           Auth, AuthData, FileAuthDataResolver
  resources/      posts, comments, blog, user, media, social, feed, messaging, income
  types/          модели (наследуют BaseObject; поля через `declare`)
  utils/          logging, post (renderText), video (getVideoSizes), browser_login (Puppeteer), consts
examples/         demo.ts, login.ts

Лицензия

MIT.