{"_id":"@apifyhub/analytics","_rev":"7-f025a29ccc868797cfa68a76dfddda83","name":"@apifyhub/analytics","dist-tags":{"latest":"0.5.0"},"versions":{"0.2.0":{"name":"@apifyhub/analytics","version":"0.2.0","_id":"@apifyhub/analytics@0.2.0","maintainers":[{"name":"strike90","email":"strike90@gmail.com"}],"dist":{"shasum":"456f7375e8af89d88a45ba22ed36cff7b18265c5","tarball":"https://registry.npmjs.org/@apifyhub/analytics/-/analytics-0.2.0.tgz","fileCount":8,"integrity":"sha512-Uyeovxsey6HG7lYGAJCUF2l7/izJ19G5cs9JrG+OwissDU98CNn4Kwn5LtyLo7ir79HzL7p0iTHyrhvYkiEIog==","signatures":[{"sig":"MEYCIQDKTKkJoWDn/B8yPRBf/PQ/aYbZ9XhqCPeYHgL8JZaNEwIhAMpAGaiuC3ng7W5vch4YtEuyfZ+pbdxop3B6zJ1k7BO1","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":34858},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"bf3be5d2a4ee04320ad49b1b07f11cfae209df66","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"strike90","email":"strike90@gmail.com"},"_npmVersion":"10.9.2","description":"Drop-in instrumentation for Apify actors. One call: instrument({ apifyHubKey }).","directories":{},"_nodeVersion":"22.13.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","typescript":"^5.4.0"},"_npmOperationalInternal":{"tmp":"tmp/analytics_0.2.0_1780081752569_0.8965850073986541","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@apifyhub/analytics","version":"0.3.0","_id":"@apifyhub/analytics@0.3.0","maintainers":[{"name":"strike90","email":"strike90@gmail.com"}],"dist":{"shasum":"48745adfd0ce5dcab1b606bca3236a72b0184d8a","tarball":"https://registry.npmjs.org/@apifyhub/analytics/-/analytics-0.3.0.tgz","fileCount":8,"integrity":"sha512-iib4gYekfv8UXGMaFQoe6YYFuoRapTNT18tPtR74L0dVNFAjavovVzo0nZmvC5mVF6PIb10uXPdFDGlNORD32g==","signatures":[{"sig":"MEUCIGgXX+jp2vhioSnOXMloFIgRAlyh2NNgdhTuPyaBsFSLAiEApQ2X7JxNHphWTIMmYNC5cRuw9yNc+up5Xy6YRUYlpE0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":58275},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"021d3679f319501226afe0175dee2594e072d29d","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"strike90","email":"strike90@gmail.com"},"_npmVersion":"10.9.2","description":"Drop-in instrumentation for Apify actors. One call: instrument({ apifyHubKey }).","directories":{},"_nodeVersion":"22.13.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","typescript":"^5.4.0"},"_npmOperationalInternal":{"tmp":"tmp/analytics_0.3.0_1780480485917_0.23968816025086714","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@apifyhub/analytics","version":"0.3.1","_id":"@apifyhub/analytics@0.3.1","maintainers":[{"name":"strike90","email":"strike90@gmail.com"}],"dist":{"shasum":"acef47e3432b9340880b42d95e8402fc1ae23d23","tarball":"https://registry.npmjs.org/@apifyhub/analytics/-/analytics-0.3.1.tgz","fileCount":8,"integrity":"sha512-yyL69LzOFHSeGpkweof/YJtUGNacsGY6Ehr4gNr62iTa/prAOTLa9u5Bo0dqI0C267RHF9DnfZLTo6dRTbw4xg==","signatures":[{"sig":"MEQCIF5naQc39v1lKBMuVkqZv59LZf+F61iUOpuRIWlwG6OQAiBq0WZRkm7Pq7YF79CKOQ2WjYJUkCYsIYCsoxWP+/w4KA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":58843},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"021d3679f319501226afe0175dee2594e072d29d","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"strike90","email":"strike90@gmail.com"},"_npmVersion":"10.9.2","description":"Drop-in instrumentation for Apify actors. One call: instrument({ apifyHubKey }).","directories":{},"_nodeVersion":"22.13.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","typescript":"^5.4.0"},"_npmOperationalInternal":{"tmp":"tmp/analytics_0.3.1_1780481455526_0.003668113523134231","host":"s3://npm-registry-packages-npm-production"}},"0.3.2":{"name":"@apifyhub/analytics","version":"0.3.2","_id":"@apifyhub/analytics@0.3.2","maintainers":[{"name":"strike90","email":"strike90@gmail.com"}],"dist":{"shasum":"d4e98ca4d9b7c764ab85fc07765d56dbad53b158","tarball":"https://registry.npmjs.org/@apifyhub/analytics/-/analytics-0.3.2.tgz","fileCount":8,"integrity":"sha512-u+MQXojaUUs68VyMpiyWFHQXkJGYNE8RDENFXDcrMbaUTGfIlwAmsqvtqy0J315z9eSbKswM3UwcPFY7IYKfUA==","signatures":[{"sig":"MEQCIEZCvjFfw6u3aFIKCje8QIC7WX2s0O5kfqKT+zot6vlLAiB+yQMOiJqnSGt/bMRPtXs4pavjG0TvzSliAraTdWDLWA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":57943},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"4d5b2ca4f6bd111074db3aa4209bba88814b18ee","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"strike90","email":"strike90@gmail.com"},"_npmVersion":"10.9.2","description":"Drop-in instrumentation for Apify actors. One call: instrument({ apifyHubKey }).","directories":{},"_nodeVersion":"22.13.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","typescript":"^5.4.0"},"_npmOperationalInternal":{"tmp":"tmp/analytics_0.3.2_1780508303296_0.42912275574645586","host":"s3://npm-registry-packages-npm-production"}},"0.3.3":{"name":"@apifyhub/analytics","version":"0.3.3","_id":"@apifyhub/analytics@0.3.3","maintainers":[{"name":"strike90","email":"strike90@gmail.com"}],"dist":{"shasum":"e331a4bd863c33d61187135725e4016635244224","tarball":"https://registry.npmjs.org/@apifyhub/analytics/-/analytics-0.3.3.tgz","fileCount":8,"integrity":"sha512-+ZoF6J3JjA8ehl4gDprP3GT2fSpNubqKattSvHxgGPWydPKfIczgz8cAKF5YM6D6KEEa3UUlgxkY94P3rO1jTQ==","signatures":[{"sig":"MEYCIQC0raTeBH+aka7kmBRJMjSoHheI/ZmDvgkmUqGACY4WNAIhAKMJpqX8wbde2DiQSRnoTBRpvF39SR6WsWxzVzbYx70x","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":59916},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"12a5267dce7bbe2d0c2b7b6d6e876ab358173929","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"strike90","email":"strike90@gmail.com"},"_npmVersion":"10.9.2","description":"Drop-in instrumentation for Apify actors. One call: instrument({ apifyHubKey }).","directories":{},"_nodeVersion":"22.13.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","typescript":"^5.4.0"},"_npmOperationalInternal":{"tmp":"tmp/analytics_0.3.3_1780880952021_0.8668377104813956","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@apifyhub/analytics","version":"0.4.0","_id":"@apifyhub/analytics@0.4.0","maintainers":[{"name":"strike90","email":"strike90@gmail.com"}],"dist":{"shasum":"098dfeb6c056199a889639916463e99ead44a76f","tarball":"https://registry.npmjs.org/@apifyhub/analytics/-/analytics-0.4.0.tgz","fileCount":8,"integrity":"sha512-AY31nHQCUs6U4Ioql+OkYuaNpqi98vzDIjivabbNhiWnKjQoOZQTRtmw9maz2YquReDj6mMJi+fMGyZnbD+SPg==","signatures":[{"sig":"MEUCIFCY6ScympGJIvlMHCqqqiCDx1vF9ZWP7zbmOktehztHAiEA5RZ/3Rl55TyoNeCNcAkTb2jgCpEc559XeuruEf5BNq4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":81170},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"f7409eee58ccfa6dd62487b7765495f4f9bb82c7","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"strike90","email":"strike90@gmail.com"},"_npmVersion":"10.9.2","description":"Drop-in instrumentation for Apify actors. One call: instrument({ apifyHubKey }).","directories":{},"_nodeVersion":"22.13.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","typescript":"^5.4.0"},"_npmOperationalInternal":{"tmp":"tmp/analytics_0.4.0_1783563729652_0.20056917917793715","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@apifyhub/analytics","version":"0.5.0","description":"Drop-in instrumentation for Apify actors. One call: instrument({ apifyHubKey }).","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"engines":{"node":">=18"},"scripts":{"build":"tsup","dev":"tsup --watch","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"devDependencies":{"tsup":"^8.0.0","typescript":"^5.4.0"},"_id":"@apifyhub/analytics@0.5.0","gitHead":"288bdb8694422df42cb948e319a1108d8432b1d4","_nodeVersion":"22.13.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-IEs897fuNcIymEaz8TJeE8lbNohbnUZEboOpIztXhL0ZErWb7gJqDQHG3uO1/V2MC8ZX+Rm5czyTdBu5WPmqBg==","shasum":"a98928fd34f4c3f597f787828735b7a75559893a","tarball":"https://registry.npmjs.org/@apifyhub/analytics/-/analytics-0.5.0.tgz","fileCount":8,"unpackedSize":73528,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIG1T3id9vl5V3ZwmSlHrulLo+g4i6wsqlObcvxv1k32BAiEAlUoE38WHh62teU0wpX1OELC24e6IqDZihCxOfdfIyZc="}]},"_npmUser":{"name":"strike90","email":"strike90@gmail.com"},"directories":{},"maintainers":[{"name":"strike90","email":"strike90@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/analytics_0.5.0_1786260412383_0.6097749593596189"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-29T19:09:12.315Z","modified":"2026-08-09T07:26:52.819Z","0.2.0":"2026-05-29T19:09:12.709Z","0.3.0":"2026-06-03T09:54:46.057Z","0.3.1":"2026-06-03T10:10:55.684Z","0.3.2":"2026-06-03T17:38:23.440Z","0.3.3":"2026-06-08T01:09:12.209Z","0.4.0":"2026-07-09T02:22:09.780Z","0.5.0":"2026-08-09T07:26:52.587Z"},"description":"Drop-in instrumentation for Apify actors. One call: instrument({ apifyHubKey }).","maintainers":[{"name":"strike90","email":"strike90@gmail.com"}],"readme":"# @apifyhub/analytics\n\nDrop-in analytics instrumentation for [Apify](https://apify.com) actors. Wrap\nyour actor body in one call and each run reports itself to your apifyHub\ndashboard — start, finish, status, timing, and (for pay-per-event actors)\ncharges and revenue. No method patching, no platform webhooks.\n\n## Install\n\n```bash\nnpm install @apifyhub/analytics\n```\n\nRequires Node 18+ (uses the built-in `fetch`). Zero runtime dependencies.\n\n## Usage\n\nWrap your actor's body in `withAnalytics()`:\n\n```js\nimport { withAnalytics } from '@apifyhub/analytics';\n\nawait withAnalytics({ apifyHubKey: process.env.APIFYHUB_KEY }, async () => {\n  // ...the rest of your actor code runs normally.\n});\n```\n\nCommonJS works too:\n\n```js\nconst { withAnalytics } = require('@apifyhub/analytics');\n\nwithAnalytics({ apifyHubKey: process.env.APIFYHUB_KEY }, async () => {\n  // ...\n});\n```\n\n`withAnalytics` POSTs a start-of-run snapshot, runs your function, then — on\nboth success and failure — POSTs an end-of-run event with the terminal status,\ntiming, and charge/revenue data. The original return value is passed through,\nand a thrown error is re-thrown after reporting, so wrapping never changes your\nactor's behavior.\n\n### Start-only mode\n\n`instrument()` is still exported for cases where you can't wrap the body. It\nfires only the start beacon — it cannot capture terminal status, timing, or\nrevenue:\n\n```js\nimport { instrument } from '@apifyhub/analytics';\ninstrument({ apifyHubKey: process.env.APIFYHUB_KEY });\n```\n\nPrefer `withAnalytics` when you can.\n\n### Cost tracking\n\nIf your actor spends money on external services during a run — LLM calls, paid\nproxies, any third-party API — call `trackCost()` as many times as you like.\nTotals per `source` are accumulated in-process and folded into the end-of-run\nbeacon automatically, so the dashboard can report profit margin, not just\nrevenue:\n\n```js\nimport { withAnalytics, trackCost } from '@apifyhub/analytics';\n\nawait withAnalytics({ apifyHubKey: process.env.APIFYHUB_KEY }, async () => {\n  const completion = await openai.chat.completions.create({ ... });\n  trackCost('openai', 0.0032);\n  // ...\n});\n```\n\n`trackCost` never throws — a bad `amountUsd` is silently dropped rather than\ncorrupting the run's totals or crashing the actor. Costs tracked before\n`withAnalytics`/`instrument` runs (or without either ever running) are simply\nnever sent.\n\n### Setup\n\n1. Generate an **apifyHub key** on your [apifyhub.com](https://apifyhub.com)\n   profile. It's shown once — copy it then.\n2. Add it to your actor as a **secret environment variable** named\n   `APIFYHUB_KEY` (Apify Console → your actor → Settings → Environment\n   variables).\n3. Deploy. Every run now reports itself.\n\n## Options\n\nBoth `withAnalytics(options, fn)` and `instrument(options)` take the same\noptions object:\n\n```ts\n{\n  apifyHubKey: string;         // required — your apifyHub key (Bearer token)\n  endpoint?: string;           // override the ingest URL (defaults to apifyHub prod)\n  debug?: boolean;             // log to console.error on failures (default false)\n  forceOutsideApify?: boolean; // send even when not running on Apify (default false)\n  apifyMarginPct?: number;     // Apify's margin fraction (e.g. 0.2 = Apify keeps 20%);\n                               // if set, the server reports net revenue = gross × (1 − margin)\n}\n```\n\n## What it sends\n\nThe key always travels as an `Authorization: Bearer` header and is never sent\nin the body. Both events POST JSON to the same `/v1/sdk-ingest` endpoint; the\nserver merges the end event onto the start row by `runId`.\n\n**Start beacon** — reads the `APIFY_*` environment variables the platform\ninjects: run id, actor id/full name, build/task ids, consumer `userId`,\n`APIFY_USER_IS_PAYING`, origin, memory, timeout, max total charge, and\npermission level.\n\n**End beacon** (`withAnalytics` only) — terminal status (`succeeded` /\n`failed`), start/finish timestamps and duration, plus charge data read from the\nin-process Apify `ChargingManager`: pricing model, per-event counts and USD,\ngross revenue, and result count. Charges are read synchronously in-process — no\nApify API call. If `apify` isn't importable or the actor doesn't charge, the\nend event carries status and timing only.\n\nIt also carries an external-cost signal, so the dashboard can report profit\nmargin, not just revenue:\n\n- **External service cost** (`externalCostUsd`, `externalCostBreakdown`) —\n  whatever you reported via `trackCost()` during the run, totaled by source.\n\n**Apify's own infra cost** (compute/proxy/dataset) is not read by the SDK.\nReading `usageTotalUsd` in-process just before the actor exits returns `$0` —\nthe platform hasn't finalized it yet at that instant. Instead the apifyhub\nserver fetches it from `/v2/actor-runs/{runId}` about 30 seconds after the end\nbeacon lands, using the Apify API token you save on your apifyhub profile, and\nfolds it into the run's cost then. No token, no infra-cost reconciliation —\nrevenue and external cost still work.\n\n## Guarantees\n\n- **Never throws.** A bad key, network outage, or non-Apify environment can\n  never crash your actor, and the wrapper never swallows your function's own\n  error — it re-throws it after reporting (or logs when `debug: true`).\n- **Start is non-blocking.** The start POST is fired with a 5s timeout and not\n  awaited, so it never delays your actor's work. The end POST *is* awaited\n  (briefly, with a timeout) so it survives container teardown.\n- **Idempotent start.** Calling `instrument()` — or `withAnalytics`, which\n  calls it internally — more than once in the same process is a silent no-op\n  for the start beacon after the first call.\n- **Outside Apify it no-ops** (unless `forceOutsideApify: true`), so local runs\n  and tests don't send anything.\n- **A hard kill bypasses the end beacon.** OOM, timeout, or abort skips the\n  `finally`, so those runs report the start beacon only.\n","readmeFilename":"README.md"}