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

soundlink

v1.2.0

Published

Official TypeScript SDK for the Soundlink Public API

Readme

soundlink

Official TypeScript SDK for the Soundlink Public API.

Install

npm install soundlink

Version note: 1.0.0 on npm was an empty placeholder. Use >=1.1.0 for the SDK. Write surface (create / budget / stop / tiers) ships in 1.2.0.

Requires Node.js 18+ (native fetch). Works in Edge Runtime when fetch is available.

Quickstart

import { Soundlink } from 'soundlink';

const soundlink = new Soundlink({
  apiKey: process.env.SOUNDLINK_API_KEY!,
});

// Verify your key
const { data, error, meta } = await soundlink.ping();
if (error) {
  console.error(error.message, meta?.requestId);
  process.exit(1);
}

console.log(data?.status); // "ok"

You can also pass the API key directly:

const soundlink = new Soundlink('sk_your_prefix_your_secret');

Authentication

Pass your Soundlink API key (sk_<prefix>_<secret>) via the client constructor. The SDK sends it in the x-api-key header on every request.

Your organization is determined from the key. Do not send Firebase Bearer tokens on the Public API host.

Write methods require the campaigns:write scope and an Idempotency-Key (passed as idempotencyKey on the method options).

Response pattern

Every method returns:

type ApiResponse<T> = {
  data: T | null;
  error: ApiError | null;
  meta?: { requestId: string };
};

Example:

const { data, error } = await soundlink.campaigns.list({ page: 1, pageSize: 100 });

if (error) {
  console.error(error.code, error.message, error.requestId);
  return;
}

console.log(data.items);

HTTP errors from the API are never thrown. Exceptions are reserved for SDK configuration, parsing, and unexpected transport failures.

Campaigns

const { data, error } = await soundlink.campaigns.list({
  page: 1,
  pageSize: 100,
  sortBy: 'createdAt',
  sortOrder: 'desc',
});

const { data: campaign } = await soundlink.campaigns.get('camp_abc123');
// campaign.generation === 3 → wallet (writable)

// Async iterator across all pages
for await (const item of soundlink.campaigns.listAll({ pageSize: 100 })) {
  console.log(item.campaignId, item.generation);
}

Create, budget, tiers, stop (wallet / generation: 3)

const { data: strategies } = await soundlink.strategies.list();

const { data: created, error: createError } = await soundlink.campaigns.create(
  {
    spotifyUrl: 'https://open.spotify.com/track/...',
    dailyBudget: 20,
    durationDays: 7,
    genre: 'Pop',
    strategyType: 'maximum_growth',
  },
  { idempotencyKey: 'create-mytrack-01' },
);

if (createError || !created) {
  console.error(createError);
  return;
}

await soundlink.campaigns.increaseBudget(
  created.campaignId,
  { amount: 50, mode: 'current_and_renewals' },
  { idempotencyKey: 'budget-inc-01' },
);

// GET returns `tierId`; PATCH expects the same value as `targetingTierId`
const { data: tiers } = await soundlink.campaigns.tiers.get(created.campaignId);
if (!tiers) {
  return;
}
await soundlink.campaigns.tiers.update(created.campaignId, {
  items: tiers.tiers.map((tier) => ({
    targetingTierId: tier.tierId,
    isEnabled: tier.isEnabled,
    newAllocationPercent: tier.allocationPercent,
  })),
});

await soundlink.campaigns.stop(created.campaignId, {
  idempotencyKey: 'stop-01',
});

Custom strategy create (requires tierTargeting):

const { data: created } = await soundlink.campaigns.create(
  {
    spotifyUrl: 'https://open.spotify.com/track/...',
    dailyBudget: 20,
    durationDays: 7,
    genre: 'Pop',
    strategyType: 'custom',
    tierTargeting: {
      customTiers: [{ name: 'US focus', countries: ['US'], percentBudget: 100 }],
    },
  },
  { idempotencyKey: 'create-custom-01' },
);

Metrics

const { data: overview } = await soundlink.metrics.overview('camp_abc123', {
  startDate: '2026-01-01',
  endDate: '2026-03-31',
});

const { data: breakdown } = await soundlink.metrics.breakdown.list('camp_abc123', {
  page: 1,
  pageSize: 50,
});

JSONL exports (streaming)

Export endpoints stream newline-delimited JSON. Use for await for pipelines:

const { data: stream, error } = await soundlink.metrics.breakdown.export(
  'camp_abc123',
  {
    startDate: '2026-01-01',
    endDate: '2026-03-31',
  },
);

if (error || !stream) return;

for await (const row of stream) {
  await warehouse.insert(row);
}

Collect all rows into memory when the dataset is small:

const { data } = await soundlink.metrics.breakdown.export.collect('camp_abc123');
console.log(data?.rowCount, data?.rows);

Engagement exports work the same way:

const { data: stream } = await soundlink.metrics.engagement.export('camp_abc123', {
  engagementContext: 'catalog',
});

Configuration

const soundlink = new Soundlink({
  apiKey: process.env.SOUNDLINK_API_KEY!,
  baseUrl: 'https://api.getsoundlink.com', // default
  timeout: 30_000, // ms, default
  maxRetries: 2, // retries on 429/5xx (same Idempotency-Key on writes), default
  fetch: customFetch, // optional, for Edge/tests
});

Error codes

| Code | Typical HTTP | | -------------------------- | ------------ | | invalid_api_key | 401 | | api_key_revoked | 401 | | api_key_expired | 401 | | mixed_credentials | 401 | | insufficient_scope | 403 | | wallet_not_enabled | 403 | | not_found | 404 | | campaign_not_found | 404 | | invalid_request | 400 | | invalid_query_parameter | 400 | | invalid_date_range | 400 | | page_size_exceeded | 400 | | insufficient_credit | 402 | | idempotency_key_conflict | 409 | | tier_update_cooldown | 409 | | rate_limit_exceeded | 429 | | internal_error | 500 |

Include meta.requestId (or error.requestId) when contacting Soundlink support. On 402 insufficient_credit, check error.details for available / required wallet amounts.

API reference

Full endpoint documentation: docs.getsoundlink.com.

OpenAPI spec shipped with this package: openapi/soundlink-public-api-v1.yaml.

Test before publish

Smoke test against the live API (uses the local SDK source, not npm):

# Option A — .env file
cp .env.example .env
# edit .env → SOUNDLINK_API_KEY=sk_...

npm run test:live

# Option B — inline
SOUNDLINK_API_KEY=sk_your_prefix_your_secret npm run test:live

# Option C — CLI argument
npm run test:live -- sk_your_prefix_your_secret

The script runs: pingstrategies.listcampaigns.list (checks generation) → campaigns.getmetrics.overview (best effort).

Opt-in write smoke (debits wallet — use a sandbox org):

SOUNDLINK_LIVE_WRITE=1 \
SOUNDLINK_LIVE_SPOTIFY_URL='https://open.spotify.com/track/...' \
SOUNDLINK_API_KEY=sk_... \
npm run test:live

Roadmap

  • Video import helpers (videos:write) — when prioritized
  • Webhook helpers — when Public API webhooks ship

Releases

GitHub Releases are created automatically when a Version packages PR is merged. Release notes come from CHANGELOG.md, with PR/issue links when referenced in changesets.

See CONTRIBUTING.md for the full flow.

Contributing

See CONTRIBUTING.md.

License

MIT