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

@apifyhub/analytics

v0.5.0

Published

Drop-in instrumentation for Apify actors. One call: instrument({ apifyHubKey }).

Readme

@apifyhub/analytics

Drop-in analytics instrumentation for Apify actors. Wrap your actor body in one call and each run reports itself to your apifyHub dashboard — start, finish, status, timing, and (for pay-per-event actors) charges and revenue. No method patching, no platform webhooks.

Install

npm install @apifyhub/analytics

Requires Node 18+ (uses the built-in fetch). Zero runtime dependencies.

Usage

Wrap your actor's body in withAnalytics():

import { withAnalytics } from '@apifyhub/analytics';

await withAnalytics({ apifyHubKey: process.env.APIFYHUB_KEY }, async () => {
  // ...the rest of your actor code runs normally.
});

CommonJS works too:

const { withAnalytics } = require('@apifyhub/analytics');

withAnalytics({ apifyHubKey: process.env.APIFYHUB_KEY }, async () => {
  // ...
});

withAnalytics POSTs a start-of-run snapshot, runs your function, then — on both success and failure — POSTs an end-of-run event with the terminal status, timing, and charge/revenue data. The original return value is passed through, and a thrown error is re-thrown after reporting, so wrapping never changes your actor's behavior.

Start-only mode

instrument() is still exported for cases where you can't wrap the body. It fires only the start beacon — it cannot capture terminal status, timing, or revenue:

import { instrument } from '@apifyhub/analytics';
instrument({ apifyHubKey: process.env.APIFYHUB_KEY });

Prefer withAnalytics when you can.

Cost tracking

If your actor spends money on external services during a run — LLM calls, paid proxies, any third-party API — call trackCost() as many times as you like. Totals per source are accumulated in-process and folded into the end-of-run beacon automatically, so the dashboard can report profit margin, not just revenue:

import { withAnalytics, trackCost } from '@apifyhub/analytics';

await withAnalytics({ apifyHubKey: process.env.APIFYHUB_KEY }, async () => {
  const completion = await openai.chat.completions.create({ ... });
  trackCost('openai', 0.0032);
  // ...
});

trackCost never throws — a bad amountUsd is silently dropped rather than corrupting the run's totals or crashing the actor. Costs tracked before withAnalytics/instrument runs (or without either ever running) are simply never sent.

Setup

  1. Generate an apifyHub key on your apifyhub.com profile. It's shown once — copy it then.
  2. Add it to your actor as a secret environment variable named APIFYHUB_KEY (Apify Console → your actor → Settings → Environment variables).
  3. Deploy. Every run now reports itself.

Options

Both withAnalytics(options, fn) and instrument(options) take the same options object:

{
  apifyHubKey: string;         // required — your apifyHub key (Bearer token)
  endpoint?: string;           // override the ingest URL (defaults to apifyHub prod)
  debug?: boolean;             // log to console.error on failures (default false)
  forceOutsideApify?: boolean; // send even when not running on Apify (default false)
  apifyMarginPct?: number;     // Apify's margin fraction (e.g. 0.2 = Apify keeps 20%);
                               // if set, the server reports net revenue = gross × (1 − margin)
}

What it sends

The key always travels as an Authorization: Bearer header and is never sent in the body. Both events POST JSON to the same /v1/sdk-ingest endpoint; the server merges the end event onto the start row by runId.

Start beacon — reads the APIFY_* environment variables the platform injects: run id, actor id/full name, build/task ids, consumer userId, APIFY_USER_IS_PAYING, origin, memory, timeout, max total charge, and permission level.

End beacon (withAnalytics only) — terminal status (succeeded / failed), start/finish timestamps and duration, plus charge data read from the in-process Apify ChargingManager: pricing model, per-event counts and USD, gross revenue, and result count. Charges are read synchronously in-process — no Apify API call. If apify isn't importable or the actor doesn't charge, the end event carries status and timing only.

It also carries an external-cost signal, so the dashboard can report profit margin, not just revenue:

  • External service cost (externalCostUsd, externalCostBreakdown) — whatever you reported via trackCost() during the run, totaled by source.

Apify's own infra cost (compute/proxy/dataset) is not read by the SDK. Reading usageTotalUsd in-process just before the actor exits returns $0 — the platform hasn't finalized it yet at that instant. Instead the apifyhub server fetches it from /v2/actor-runs/{runId} about 30 seconds after the end beacon lands, using the Apify API token you save on your apifyhub profile, and folds it into the run's cost then. No token, no infra-cost reconciliation — revenue and external cost still work.

Guarantees

  • Never throws. A bad key, network outage, or non-Apify environment can never crash your actor, and the wrapper never swallows your function's own error — it re-throws it after reporting (or logs when debug: true).
  • Start is non-blocking. The start POST is fired with a 5s timeout and not awaited, so it never delays your actor's work. The end POST is awaited (briefly, with a timeout) so it survives container teardown.
  • Idempotent start. Calling instrument() — or withAnalytics, which calls it internally — more than once in the same process is a silent no-op for the start beacon after the first call.
  • Outside Apify it no-ops (unless forceOutsideApify: true), so local runs and tests don't send anything.
  • A hard kill bypasses the end beacon. OOM, timeout, or abort skips the finally, so those runs report the start beacon only.