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

@tollahq/sdk

v0.2.0

Published

Free-tier economics for AI and usage-based SaaS: see what each free user costs and which ones convert.

Readme

@tollahq/sdk

See what each free user of your AI product costs, which ones convert, and which accounts are the same person. This is the server SDK for Tolla.

npm install @tollahq/sdk
import OpenAI from "openai";
import { Tolla, withUser, wrapOpenAI } from "@tollahq/sdk";

const tolla = new Tolla(); // reads TOLLA_SECRET_KEY
const openai = wrapOpenAI(new OpenAI(), tolla);

// 1. When someone signs up
tolla.identify({ userId: user.id, createdAt: user.createdAt, identity: { email: user.email, ip: req.ip } });

// 2. Every AI call: cost is recorded per user automatically
await withUser(user.id, () => openai.chat.completions.create({ model: "gpt-4o", messages }));

Payments come from Stripe: connect it with a read-only key on your Tolla Connect page. No code needed.

Safe by design

  • Never blocks, never throws. Calls return immediately; events are sent in the background in batches.
  • Survives outages. Retries with backoff and a timeout; if Tolla stays unreachable, events are dropped with a warning (bounded queue, maxQueueSize), and your product carries on.
  • Retry-safe. Every event gets an id, so a resend is never counted twice.
  • No prompts, no outputs. The AI wrappers read only model and usage from responses.
  • Server only. A secret key in a browser is refused; use the browser snippet with your publishable key there.

AI cost

wrapOpenAI and wrapAnthropic record model and tokens for every billable call (chat, responses, embeddings, messages; streaming included). Tolla prices the tokens server-side from a maintained price list, including cached-input and cache-write rates, so new prices never need an SDK update. You can override any model's price in your workspace.

Attach the user with withUser(userId, fn) (or withUser({ userId, feature }, fn)), or per client: wrapOpenAI(client, tolla, { userId, feature: "chat" }).

Streaming with OpenAI Chat Completions: usage is only sent when you set stream_options: { include_usage: true }. The Responses API and Anthropic always include it.

Other providers: send the tokens yourself.

tolla.cost({ userId, feature: "chat", model: "gemini-2.5-pro", inputTokens, outputTokens });
// or a cost you computed: tolla.cost({ userId, feature: "render", costUsd: 0.04 })

Changes

Changes you set live in Tolla (a cap on free AI, a first-day limit, a card for repeat accounts) can be applied by the wrapper, with no new code:

const openai = wrapOpenAI(new OpenAI(), tolla, { changes: true });

try {
  await withUser(user.id, () => openai.chat.completions.create({ … }));
} catch (e) {
  if (e instanceof TollaChangeError) return showUpgrade(e.message); // e.action: "limit" | "verify_card"
  throw e;
}

Before a free user's call the wrapper asks Tolla (tolla.decide(userId), cached for a minute). It waits 400 ms at most: if Tolla is slow or unreachable, the call goes ahead unchanged. Paying users and the change's control group are never limited. With changes on, wrapped calls return a plain Promise (helpers such as .withResponse() are not available on them). You can also call await tolla.decide(userId) yourself, for example to show a one-time extension (action: "extend").

Per-user allowances

When a per-user allowance is live in Tolla, ask for a person's free tier where you set up their credits and trial:

const a = await tolla.allowance(user.id); // null: keep today's free tier
if (a?.cardFirst) return askForCard(a.message);
const credits = Math.round(FREE_CREDITS * (a?.share ?? 1));   // e.g. 1.1 = 10% more, 0.5 = half
const trialDays = TRIAL_DAYS + (a?.trialDays ?? 0);            // 0 = no new trial for this group

a.reason is a plain sentence you can show the person. Heavy users get a.noticeUntil a week before their smaller tier applies.

Serverless

Background sending needs the process to stay alive. In Vercel functions, Lambda and similar, flush before the response ends:

import { after } from "next/server";
after(() => tolla.flush());

Options

| Option | Default | | |---|---|---| | apiKey | process.env.TOLLA_SECRET_KEY | Secret key (tolla_sk_…). Without one the SDK does nothing. | | baseUrl | https://tolla.vercel.app | Or TOLLA_BASE_URL. | | batchSize | 100 | Send when this many events are queued (max 500). | | flushIntervalMs | 1000 | Send at least this often. | | maxQueueSize | 10 000 | Events kept while Tolla is unreachable. | | timeoutMs | 10 000 | Per request. | | maxRetries | 5 | For network errors, 429 and 5xx. | | onError | console warning, at most once a minute | Receives every problem, including events Tolla rejected and why. | | disabled | false | Turn into a no-op (tests). |

The Connect page in Tolla has a live event debugger: every event received, and what was wrong with the ones that were not accepted.

MIT licensed.