SDK reference · v0.1

Growth SDK

A small, structured API your code (or Claude Code) drops in. Three packages — JS/TS for browser & node, Python for server. All emit the same wire format.

Install

JavaScript / TypeScript (Next.js, Vite, Express, Node):

pnpm add @growth-loop/sdk
# or
npm i @growth-loop/sdk

Python (FastAPI, Django, Flask):

pip install growth-loop-sdk
# or
uv add growth-loop-sdk

1 · Construct a client

The browser variant batches with sendBeacon, the node variant with fetch. Both share the same surface — only the transport differs.

// src/lib/growth.ts (Next.js client component)
'use client';
import { instrument } from '@growth-loop/sdk';
import { createBrowserClient } from '@growth-loop/sdk/browser';

export const growth = instrument(
  createBrowserClient({
    apiKey: process.env.NEXT_PUBLIC_GROWTH_KEY!,
    host: 'https://api.growth-loop.dev',
  }),
);
# growth_setup.py
from growth import Growth, GrowthOptions

growth = Growth(GrowthOptions(
    api_key=os.environ["GROWTH_KEY"],
    host="https://api.growth-loop.dev",
    environment=os.environ.get("APP_ENV", "production"),
))

2 · track / identify / group

The low-level API. Use this for one-off events that don’t fit a span/step/button pattern. Properties are JSON; never put PII (email, payment) here — use distinct_id for identity.

growth.track('signup_completed', { plan: 'pro' });
growth.identify({ distinctId: user.id, properties: { email_domain: 'acme.com' } });
growth.group('account', account.id, { tier: 'enterprise' });
growth.alias(user.id, anonId);  // map old anonymous id → real user

3 · Decorators (preferred)

Higher-level helpers Claude Code applies automatically. Each maps to one or more track() calls under the hood.

growth.span(name, fn, opts?)

Wraps a sync or async function. Emits name.started, name.completed (with duration_ms), and name.failed on throw with error_message / error_name.

const checkout = growth.span(
  'checkout',
  async (items: Item[]) => stripe.charge(items),
  {
    properties: { source: 'web' },
    enrich: (args, phase, result) => phase === 'success' && result
      ? { amount_cents: (result as Charge).amountCents }
      : undefined,
  },
);

growth.button(name, handler, props?)

Wraps an event handler so the click is logged before the handler runs. Returns a function with the same signature.

<button onClick={growth.button(
  'cta_clicked',
  signUp,
  { location: 'hero' },
)}>
  Sign up
</button>

growth.step(name, props?)

One-shot funnel-step event. Cheaper than span (no timing, no failure tracking).

growth.step('activation.created_first_project', { template: 'next.js' });

@growth_span (Python)

Decorator. Sync and async functions. distinct_id accepts a string or callable for dynamic resolution.

from growth import growth_span
from growth_setup import growth

@router.post("/checkout")
@growth_span(growth, "checkout",
             distinct_id=lambda body: body.user_id)
async def checkout(body: CheckoutBody):
    return await stripe.charge(body.items)

4 · Wire format

The SDK POSTs batches to /v1/ingest. You can call it directly:

curl -X POST https://api.growth-loop.dev/v1/ingest \
  -H 'content-type: application/json' \
  -H 'x-growth-api-key: pk_live_…' \
  -d '{
    "api_key": "pk_live_…",
    "sdk": { "name": "curl", "version": "1.0" },
    "sent_at": "2026-04-27T17:00:00Z",
    "events": [{
      "name": "signup_completed",
      "distinct_id": "u_42",
      "source": "web",
      "properties": { "plan": "pro" }
    }]
  }'

5 · Auto-instrument with Claude Code

Drop the growth-init skill into your repo’s .claude/skills/, then run:

claude mcp add growth-loop \
  -e GROWTH_API_KEY=pk_live_… \
  -e GROWTH_HOST=https://api.growth-loop.dev \
  -- npx -y @growth-loop/mcp-server

The skill scans your stack, picks 15-30 high-signal call sites (signup, checkout, key clicks, async flows), wraps them with the decorators above, and opens a PR. See the skill marketplace for the source.

Rate limits

Ingest is rate-limited per API key: 1000 events/sec by default. Burst over the limit returns 429 with a retry-after header. Increase via env on self-host or contact us for cloud.