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

@anisprouts/dsp-sdk-trial

v0.2.0

Published

Trial JavaScript/TypeScript SDK for the Sprouts Data Platform (DSP) API: search, look-alike (similar), entity lookups, and key usage.

Readme

@anisprouts/dsp-sdk-trial

Official JavaScript / TypeScript SDK for the Sprouts Data Platform (DSP) API.

Covers the API-key surface:

| Method | Endpoint | Scope | |---|---|---| | client.search(params) | POST /v1/search | dsp.search.read | | client.similar(params) | POST /v1/similar | dsp.search.read | | client.entities.*(params) | POST /v1/entities/{category} | dsp.entities.read | | client.usage(params?) | GET /v1/keys/usage | dsp.usage.read |

Zero runtime dependencies. Node.js ≥ 18 (uses the built-in fetch). Ships ESM and CJS with full TypeScript types.

📘 New to the platform? Start with the User Guide — the full journey from getting an API key to production troubleshooting. This README is the quick reference.

Install

npm install @anisprouts/dsp-sdk-trial

Quick start

import { DspClient } from '@anisprouts/dsp-sdk-trial';

const client = new DspClient({
  apiKey: process.env.DSP_API_KEY, // sk_dsp_live_… or sk_dsp_test_…
});

const page = await client.search({
  query: 'industrial robotics companies in Germany',
  mode: 'auto',
  category: 'company',
  limit: 25,
});

for (const hit of page.results) {
  console.log(hit.id, hit.score, hit['name']);
}

apiKey defaults to the DSP_API_KEY environment variable, and baseUrl to DSP_BASE_URL (falling back to the QA environment, https://dsp.gtmsprouts.ai/backend).

Server-side only. DSP keys carry your tenant's whole credit balance, and the API refuses any browser request carrying a key (403 auth.browser_origin). The client throws if constructed in a browser; call it from your server and have your frontend call you.

Look-alike search

const lookalikes = await client.similar({
  anchors: [
    { value: 'acme.com' },                          // by: 'domain' is the default
    { value: 'Globex', by: 'name', weight: 0.5 },
  ],
  combine: 'and', // 'and' = like all anchors together; 'or' = like any one
  limit: 50,
});

Entity lookup

Fetch full records by id — one method per category: companies, persons, patents, news, trends, marketReports, fundings, academicReports, jobOpenings. Responses are fully typed (CompanyRecord, PatentRecord, …) and billed per requested id.

const page = await client.entities.companies({
  ids: ['000006dddc0f7a39', 'definitely_not_real'],
  fields: ['account_name', 'industry', 'employee_count'], // omit for the full record
});

for (const hit of page.results) {
  if (!hit.found) continue; // misses are returned, never silently dropped
  console.log(hit.fields.account_name, hit.fields.employee_count);
}

companies and persons can also resolve raw identifiers instead of platform ids — set by (≤ 50 ids per call in that mode):

const byDomain = await client.entities.companies({
  ids: ['adyen.com', 'checkout.com'],
  by: 'domain', // 'auto' | 'domain' | 'li_url' | 'name' ('domain' not valid for persons)
});

Batch limits: 1000 ids per call for companies/persons, 100 for the document categories (document records are large), 50 whenever by is set.

Usage (the calling key's spend)

const usage = await client.usage({
  granularity: 'day',                 // 'hour' | 'day' | 'month' | 'total'
  from: new Date('2026-07-01'),       // Date or ISO string
  to: new Date('2026-08-01'),
});
console.log(usage.total_requests, usage.total_credits, usage.buckets);

Defaults to the last 30 days, bucketed by day. total_credits is net of refunds. Only the calling key's usage is readable.

Pagination

Results are cursor-paginated (next_cursor is opaque and signed; there is no page/offset parameter). Either follow it yourself:

let cursor: string | undefined;
do {
  const page = await client.search({ query: 'fintech', cursor });
  process(page.results);
  cursor = page.next_cursor ?? undefined;
} while (cursor);

…or use the async iterators:

for await (const page of client.searchPages({ query: 'fintech', limit: 100 })) {
  process(page.results);
}
// client.similarPages(...) works the same way.

Do not change filters mid-pagination — the server rejects the cursor (pagination.cursor_invalid) rather than silently reshuffling results.

Error handling

Every non-2xx response is an RFC 9457 problem, surfaced as a typed error. error.code is a stable machine-readable string and error.requestId is the handle to quote when contacting support.

import {
  APIError,
  APIConnectionError,
  AuthenticationError,
  PermissionDeniedError,
  InsufficientCreditsError,
  RateLimitError,
  BadRequestError,
  DspValidationError,
  ErrorCode,
} from '@anisprouts/dsp-sdk-trial';

try {
  await client.search({ query: 'fintech' });
} catch (err) {
  if (err instanceof RateLimitError) {
    // 429 — quota.* ; err.retryAfter is the Retry-After header in seconds
  } else if (err instanceof InsufficientCreditsError) {
    // 402 credits.insufficient — waiting does not help; top up credits
  } else if (err instanceof PermissionDeniedError) {
    // 403 — usually auth.insufficient_scope: the key lacks a scope
  } else if (err instanceof AuthenticationError) {
    // 401 — key missing, unknown, revoked, or expired
  } else if (err instanceof BadRequestError) {
    // 400/422 — fix the request; err.problem.extra may carry specifics
  } else if (err instanceof APIConnectionError) {
    // network failure or timeout — no response was received
  } else if (err instanceof DspValidationError) {
    // the SDK rejected the call locally; nothing was sent
  }
}

| Error class | Status | Typical code | |---|---|---| | DspValidationError | — (not sent) | — | | BadRequestError | 400 / 422 | request.invalid, pagination.cursor_invalid | | AuthenticationError | 401 | auth.unauthorized, auth.invalid_key, auth.expired | | InsufficientCreditsError | 402 | credits.insufficient | | PermissionDeniedError | 403 | auth.insufficient_scope, auth.browser_origin | | NotFoundError | 404 | — | | ConflictError | 409 | idempotency.* | | RateLimitError | 429 | quota.* (has retryAfter) | | InternalServerError | 5xx | upstream.*, auth.unavailable, internal | | APITimeoutError | — | request exceeded timeoutMs | | APIConnectionError | — | network failure |

The full closed set of codes is exported as ErrorCode.

Retries, timeouts, aborts

Connection errors, timeouts, and 429 / 502 / 503 / 504 responses are retried with exponential backoff and jitter, honoring Retry-After. Defaults: 2 retries, 60 s per attempt. All endpoints here are reads, so retries cannot double-charge.

const client = new DspClient({ maxRetries: 3, timeoutMs: 30_000 });

// Per request:
const controller = new AbortController();
await client.search(
  { query: 'fintech' },
  { maxRetries: 0, timeoutMs: 5_000, signal: controller.signal },
);

Reading responses honestly

The envelope carries fields worth checking, not just results:

  • total_is_estimate — when true, total is an estimate.
  • degradation[] — what the server couldn't do for this request (a corpus timed out, vector coverage incomplete, pagination truncated…). Empty means "checked, nothing degraded".
  • meta.environmentlive or test, echoed so a test-key result can never be mistaken for a live one.
  • meta.request_id — quote it in any support conversation.

Development

npm install
npm run typecheck
npm test
npm run build