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

@savantoai/ai-sdk

v3.1.0

Published

Official JavaScript/TypeScript SDK for Savanto AI — search, chat, recommendations, and knowledge base management

Readme

@savantoai/ai-sdk

Official JavaScript/TypeScript SDK for Savanto — AI-powered search, chat, recommendations, and knowledge base management.

Auto-generated from the Savanto OpenAPI spec. Every endpoint is fully typed with zero manual maintenance.

Installation

npm install @savantoai/ai-sdk

Requires Node.js >= 18 (uses native fetch). Works in modern browsers with no polyfills.

Quick Start

import { createClient, searchProducts, chat } from '@savantoai/ai-sdk';

const client = createClient({
  baseUrl: 'https://api.savanto.ai',
  auth: 'if_sk_...',
});

// Search products
const { data } = await searchProducts({
  client,
  body: { query: 'blue running shoes', limit: 10 },
});
console.log(data);

// AI chat
const { data: chatResponse } = await chat({
  client,
  body: { message: 'What shoes do you recommend?', threadId: 'thread-1' },
});

Authentication

Pass your API key via the auth option on the client:

const client = createClient({
  baseUrl: 'https://api.savanto.ai',
  auth: 'if_sk_...',
});

The client automatically sends it as Authorization: Bearer <key> on every request.

| Key Type | Prefix | Use | |----------|--------|-----| | Secret | if_sk_ | Server-side only. Full access to all endpoints. | | Publishable | if_pk_ | Safe for client-side. Limited to search, chat, feedback, and prompts. |

Examples

Products

import {
  searchProducts,
  upsertProduct,
  getProduct,
  deleteProduct,
  bulkUpsertProducts,
  bulkDeleteProducts,
  listProductIds,
} from '@savantoai/ai-sdk';

// Search
const { data } = await searchProducts({
  client,
  body: { query: 'warm jacket', limit: 10 },
});

// Upsert
await upsertProduct({
  client,
  body: { id: 'prod-1', name: 'Running Shoe', price: 99.99 },
});

// Get / Delete
const { data: product } = await getProduct({ client, path: { id: 'prod-1' } });
await deleteProduct({ client, path: { id: 'prod-1' } });

// Bulk operations
await bulkUpsertProducts({
  client,
  body: { items: [{ id: 'p1', name: 'Shoe' }, { id: 'p2', name: 'Boot' }] },
});
await bulkDeleteProducts({ client, body: { ids: ['p1', 'p2'] } });

// List IDs
const { data: ids } = await listProductIds({ client });

Chat

import { chat } from '@savantoai/ai-sdk';

// `throwOnError: true` makes `response` non-optional so `response.body` is
// safe to read directly. Without it (since 2.2.0) `response` is typed as
// `Response | undefined`. See CHANGELOG.md for details.
const { data, response } = await chat({
  client,
  body: {
    message: 'What is your return policy?',
    threadId: 'thread-1',
    stream: true,
  },
  throwOnError: true,
});

// For streaming (NDJSON), read the response body directly. The protocol is
// block-based: text streams as `block_delta` chunks inside an active text
// block, structured items arrive as `block_data` keyed by item id, and
// per-field streaming (post summaries, product reasons, etc.) flows through
// `item_delta`. See https://savanto.ai/docs/developers/streaming for the
// full chunk-type reference.
const reader = response.body!.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  const lines = decoder.decode(value).split('\n').filter(Boolean);
  for (const line of lines) {
    const chunk = JSON.parse(line);
    if (chunk.type === 'block_delta') process.stdout.write(chunk.data.content);
  }
}

Posts

Same pattern as products — upsertPost, searchPosts, getPost, deletePost, bulkUpsertPosts, bulkDeletePosts, listPostIds.

import { searchPosts, upsertPost } from '@savantoai/ai-sdk';

await upsertPost({
  client,
  body: { id: 'post-1', title: 'Return Policy', content: '...' },
});

const { data } = await searchPosts({
  client,
  body: { query: 'shipping policy' },
});

Feedback

import { submitFeedback, listFeedback } from '@savantoai/ai-sdk';

// Submit (publishable key safe)
await submitFeedback({
  client,
  body: { threadId: 'thread-1', messageIndex: 0, rating: 5, comment: 'Helpful!' },
});

// List (secret key)
const { data } = await listFeedback({ client });

Threads

import { searchThreads, getThreadMessages, deleteThread } from '@savantoai/ai-sdk';

const { data } = await searchThreads({
  client,
  body: { query: 'returns' },
});

const { data: messages } = await getThreadMessages({ client, path: { id: 'thread-1' } });
await deleteThread({ client, path: { id: 'thread-1' } });

Webhooks

import { createWebhook, listWebhooks, testWebhook } from '@savantoai/ai-sdk';

await createWebhook({
  client,
  body: { name: 'Inventory sync', url: 'https://example.com/hook', events: ['product.upsert'] },
});

const { data } = await listWebhooks({ client });
const { data: result } = await testWebhook({ client, path: { id: 'webhook-1' } });

Recommendations

import { getProductRecommendations } from '@savantoai/ai-sdk';

const { data } = await getProductRecommendations({
  client,
  body: { productId: 'prod-1', maxRecommendations: 5 },
});

Crawl & Scrape

import { startCrawl, getCrawlStatus, scrapePage } from '@savantoai/ai-sdk';

await startCrawl({ client, body: { url: 'https://mystore.com' } });
const { data } = await getCrawlStatus({ client, path: { id: 'crawl-abc' } });
await scrapePage({ client, body: { url: 'https://mystore.com/about' } });

Response Format

Every function returns { data, error, response }:

const result = await searchProducts({ client, body: { query: 'shoes' } });

if (result.error) {
  // Typed error: { error: { message: string, code: string } }
  console.error(result.error);
} else {
  console.log(result.data);
}

To throw on errors instead of returning them:

const { data } = await searchProducts({
  client,
  body: { query: 'shoes' },
  throwOnError: true,
});

TypeScript

All types are exported:

import type {
  Product,
  ProductInput,
  ProductSearchRequest,
  Post,
  ChatRequest,
  ChatStreamChunk,
  Taxonomy,
  Prompt,
  FeedbackSubmission,
  ErrorResponse,
} from '@savantoai/ai-sdk';

Utilities

import { workspaceIdFromUrl } from '@savantoai/ai-sdk';

workspaceIdFromUrl('https://mystore.com');       // 'mystore-com'
workspaceIdFromUrl('my-store.myshopify.com');     // 'my-store-myshopify-com'

API Reference

Full endpoint documentation with request/response schemas:

savanto.ai/docs/api

Versioning & changelog

See CHANGELOG.md for per-version migration notes, including the TypeScript-level breaking changes in 2.2.0. Type narrowings and generator-driven shape changes are treated as major.

Regeneration

The SDK is auto-generated from the OpenAPI spec. To regenerate after API changes:

cd cloud && npm run openapi          # extract spec to cloud/openapi.json
cd sdks/javascript && npm run generate  # regenerate src/generated/

License

MIT