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

su-sdk

v2.1.3

Published

SearchUnify javascript SDK enables developers to easily work with the SearchUnify platform and build scalable solutions with search, analytics, crawlers and more.

Readme

SearchUnify SDK

Version npm

NPM

Overview

The SearchUnify SDK enables developers to easily work with the SearchUnify platform and build scalable solutions with search, analytics, crawlers and more. You can get started in minutes using NPM. The SearchUnify SDK simplifies use of SearchUnify Services by providing a set of libraries that are consistent and familiar for the developers. It provides support for API lifecycle consideration such as credential management, retries, data marshaling, and serialization. The SearchUnify SDKs also support higher level abstractions for simplified development.

Key Features

  • HTTP/2 Support and pluggable HTTP layer, new programming interfaces seamlessly take advantage of HTTP/2 features and provide new ways to build applications.
  • Nonblocking I/O, the SearchUnify SDK for Javascript utilizes a new, nonblocking SDK architecture to support true nonblocking I/O. It features truly non blocking asynchronous clients that implement high concurrency across a few threads.

Getting Started

Sign up for SearchUnify, before you begin, you need a SearchUnify account. Please see the oAuth section of the developer guide for information about how to retrieve your SearchUnify credentials.

Installation

SDK requires Node.js to run.

npm install su-sdk

Authentication

The SDK supports multiple authentication methods to securely connect to your SearchUnify instance. Depending on your setup, you can initialize the SDK using OAuth 2.0, API Key, or Client Credentials authentication.

tenantId on SearchUnifyRestClient is optional and intended for non-MCP SDK callers. SearchUnify MCP does not send tenantId on outbound requests; the admin BFF injects it from the tenant-id header when proxying to analytics.

1. OAuth 2.0 (Password Grant)

An access token is generated internally and refreshed automatically on expiry (4 hours).

const { SearchUnifyRestClient, AUTH_TYPES } = require('su-sdk');

const suRestClient = new SearchUnifyRestClient({
  instance: 'https://yourInstance.searchunify.com',
  timeout: 60000,
  authType: AUTH_TYPES.PASSWORD,
  oauth2: {
    username: 'changeme',
    password: 'changeme',
    clientId: 'changeme',
    clientSecret: 'changeme'
  }
});

2. API Key Authentication

Generate the API key from the SearchUnify admin panel.

const { SearchUnifyRestClient, AUTH_TYPES } = require('su-sdk');

const suRestClient = new SearchUnifyRestClient({
  instance: 'https://yourInstance.searchunify.com',
  timeout: 60000,
  authType: AUTH_TYPES.API_KEY,
  apiKey: 'changeme'
});

3. Client Credentials (OAuth 2.0)

For server-to-server communication. The access token is generated internally and refreshed automatically on expiry (4 hours).

const { SearchUnifyRestClient, AUTH_TYPES } = require('su-sdk');

const suRestClient = new SearchUnifyRestClient({
  instance: 'https://yourInstance.searchunify.com',
  timeout: 60000,
  authType: AUTH_TYPES.CLIENT_CREDENTIALS,
  oauth2: {
    clientId: 'your-client-id',
    clientSecret: 'your-client-secret'
  }
});

Sample API Call

const tileData = async () => {
  try {
    const Analytics = suRestClient.Analytics();
    const data = await Analytics.getTilesData({
      startDate: '2022-12-09',
      endDate: '2022-12-10',
      searchClientId: 'searchClient-UID'
    });
    console.log('data', data);
  } catch (error) {
    console.log('error', error);
  }
};

tileData();

Available APIs

Note: For analytics methods, searchClientId and ecoSystemId are mutually exclusive — pass one or the other, not both.


Search Clients

const SearchClients = suRestClient.SearchClients();

// Get all search clients (returns id, name, uid, search_client_type)
const searchClients = await SearchClients.getSearchClients();

Search

const Search = suRestClient.Search();

// Search results — uid is the search client UUID
const results = await Search.getSearchResults({
  uid: 'searchClient-UUID',
  searchString: 'your query'
});

// GPT-enhanced search
// requestType: 'SEARCH_GPT' | 'GPT'
// sortby: '_score' | '_post_time'
// When requestType = 'SEARCH_GPT': pageNo and resultsPerPage are required
// When requestType = 'GPT': context, from, and articles are required
const gptResults = await Search.getGPTResults({
  searchClientId: 'searchClient-UUID',
  searchString: 'your query',
  requestType: 'SEARCH_GPT',
  sortby: '_score',
  pageNo: 1,
  resultsPerPage: 10
});

Analytics

const Analytics = suRestClient.Analytics();

Overview / Tile Data

// Tile summary metrics
await Analytics.getTilesData({ startDate: '2025-01-01', endDate: '2025-03-26', searchClientId: 'uid' });

// Search summary chart data
await Analytics.getSearchSummaryChart({ startDate: '2025-01-01', endDate: '2025-03-26', searchClientId: 'uid' });

// Average click position chart
await Analytics.getAverageClickPosition({ startDate: '2025-01-01', endDate: '2025-03-26', searchClientId: 'uid' });

// Content tile data
await Analytics.getTileDataContent({ startDate: '2025-01-01', endDate: '2025-03-26', searchClientId: 'uid' });

// Tile metrics (set 1 and set 2)
await Analytics.getTileDataMetrics1({ startDate: '2025-01-01', endDate: '2025-03-26', searchClientId: 'uid' });
await Analytics.getTileDataMetrics2({ startDate: '2025-01-01', endDate: '2025-03-26', searchClientId: 'uid' });

Search Queries

These methods require startDate, endDate, and count (1–500). searchClientId or ecoSystemId is optional.

// All search queries
await Analytics.getAllSearchQuery({ startDate: '2025-01-01', endDate: '2025-03-26', count: 10, searchClientId: 'uid' });

// Queries that returned results
await Analytics.searchQueryWithResult({ startDate: '2025-01-01', endDate: '2025-03-26', count: 10, searchClientId: 'uid' });

// Queries with no clicks
await Analytics.searchQueryWithNoClicks({ startDate: '2025-01-01', endDate: '2025-03-26', count: 10, searchClientId: 'uid' });

// Queries without results
await Analytics.searchQueryWithoutResults({ startDate: '2025-01-01', endDate: '2025-03-26', count: 10, searchClientId: 'uid' });

// Search query histogram
await Analytics.searchQueryHistogram({ startDate: '2025-01-01', endDate: '2025-03-26', count: 10, searchClientId: 'uid' });

// Missed query histogram
await Analytics.missedQueryHistogram({ startDate: '2025-01-01', endDate: '2025-03-26', count: 10, searchClientId: 'uid' });

// KCS support search queries
await Analytics.getKcsSupportSearchQuery({ startDate: '2025-01-01', endDate: '2025-03-26', count: 10, searchClientId: 'uid' });

Search Conversions

// All search conversions
await Analytics.getAllSearchConversion({ startDate: '2025-01-01', endDate: '2025-03-26', count: 10, searchClientId: 'uid' });

// Conversions not on first page
await Analytics.searchConversionNotOnFirstPage({ startDate: '2025-01-01', endDate: '2025-03-26', count: 10, searchClientId: 'uid' });

// Conversions with filters applied
await Analytics.searchConversionWithFilters({ startDate: '2025-01-01', endDate: '2025-03-26', searchClientId: 'uid' });

// Conversions by session ID
await Analytics.searchConversionBySessionId({
  startDate: '2025-01-01', endDate: '2025-03-26',
  count: 10, searchClientId: 'uid', sessionId: 'session-id'
});

// Discussions ready to become articles
await Analytics.discussionsReadyToBecomeArticles({ startDate: '2025-01-01', endDate: '2025-03-26', count: 10 });

KCS / Case Articles

// searchType: 'all' | 'global' | 'support'
await Analytics.getCaseCreatedArticles({ startDate: '2025-01-01', endDate: '2025-03-26', searchClientId: 'uid', searchType: 'all' });
await Analytics.getCaseDeflectedArticles({ startDate: '2025-01-01', endDate: '2025-03-26', searchClientId: 'uid', searchType: 'all' });
await Analytics.getAttachedArticles({ startDate: '2025-01-01', endDate: '2025-03-26', searchClientId: 'uid' });
await Analytics.getAttachedOnCase({ startDate: '2025-01-01', endDate: '2025-03-26', searchClientId: 'uid', url: 'https://case-url' });

Leadership dashboard

Leadership methods call POST /api/v2/leadership/* on your SearchUnify admin instance URL (same base as other analytics mirrors). The admin service proxies to the analytics service and injects tenantId from the tenant-id header when present.

| SDK method | Path | Notes | |------------|------|--------| | postLeadershipUnassistedSelfSolveVolume | /api/v2/leadership/unassisted-self-solve-volume | USSV; optional directlyViewSetting | | postLeadershipAssistedSelfSolveVolume | /api/v2/leadership/assisted-self-solve-volume | ASSV / KM effectiveness | | postLeadershipAssistedCaseVolume | /api/v2/leadership/assisted-case-volume | Tenant-scoped; optional indexName | | postLeadershipDeflectionCount | /api/v2/leadership/deflection-count | Explicit + implicit quarterly counts | | postLeadershipDeflectionCostSavingsDownload | /api/v2/leadership/deflection-cost-savings-download | CSV/email; requires costPerCase, csv | | postLeadershipGetContentSources | /api/v2/leadership/get-content-sources | Content source / facet discovery |

Scope: For deflection-count, USSV, and ASSV, pass uid or ecoId (mutually exclusive), not both. internalUser defaults to 'all' on deflection-count and assisted-case-volume.

Date range: Omit from and to to use the analytics default (last six completed quarters), matching the Admin Leadership tab. When set, use YYYY-MM-DD HH:mm:ss (or values accepted by your analytics deployment).

// Unassisted Self Solve Volume (last six quarters when from/to omitted)
await Analytics.postLeadershipUnassistedSelfSolveVolume({
  uid: 'searchClient-UUID',
  internalUser: 'all',
  directlyViewSetting: true
});

// Assisted Self Solve Volume
await Analytics.postLeadershipAssistedSelfSolveVolume({
  uid: 'searchClient-UUID',
  internalUser: 'all'
});

// Deflection counts (explicit + implicit per quarter)
await Analytics.postLeadershipDeflectionCount({
  uid: 'searchClient-UUID',
  internalUser: 'all'
});

// Assisted case volume (optional indexName for one content source)
await Analytics.postLeadershipAssistedCaseVolume({
  internalUser: 'all',
  indexName: 'optional-elastic-index-name'
});

// Content sources for Leadership facet labels
await Analytics.postLeadershipGetContentSources({});

Admin UI vs SDK path: The SearchUnify Admin UI calls POST /analytics/leadership/* (session auth + analytics-secret on the proxy). SDK and MCP use /api/v2/leadership/* on the same admin host. Both should reach the same analytics handlers when /api/v2 leadership mirrors are deployed.

Performance — deflection-count: postLeadershipDeflectionCount runs two rollup queries in parallel (assisted + unassisted quarterly tables). On large tenants or slow databases it can take longer than other Leadership calls. If you see timeout of 60000ms exceeded, increase timeout on SearchUnifyRestClient (see below).


Request timeout

Every analytics call uses the timeout (ms) passed to SearchUnifyRestClient (default 60000). Axios aborts the client wait when the limit is reached; the upstream request may still be running on the server.

const suRestClient = new SearchUnifyRestClient({
  instance: 'https://yourInstance.searchunify.com',
  timeout: 120000, // recommended for leadership deflection-count on busy tenants
  authType: AUTH_TYPES.API_KEY,
  apiKey: 'changeme'
});

| Symptom | Likely cause | Mitigation | |---------|----------------|------------| | timeout of 60000ms exceeded on postLeadershipDeflectionCount | Heavy rollup SQL or loaded analytics DB | Raise timeout (e.g. 120000–180000); check analytics query logs | | Fast 401 on direct analytics host | Missing analytics-secret on legacy POST /leadership/* | Call through admin /api/v2/leadership/* or send the header your deployment expects | | Works in Admin UI, times out in SDK | Browser has no 60s cap; SDK enforces timeout | Increase SDK timeout; same backend may need DB tuning |

MCP: su-mcp forwards timeout via searchunify-timeout (HTTP headers) or timeout in creds.json.


Sessions

// Search queries grouped by session
await Analytics.getSearchQueryInSessions({ startDate: '2025-01-01', endDate: '2025-03-26', count: 10, searchClientId: 'uid' });

// Session details log
// sortByField: 'search' | 'click' | 'support' | 'case' | 'page_view' | 'end_date' | 'start_date'
// sortType: 'asc' | 'desc'
await Analytics.getSessionDetails({ startDate: '2025-01-01', endDate: '2025-03-26', searchClientId: 'uid' });

// Session list table (count is required)
await Analytics.getSessionListTable({ startDate: '2025-01-01', endDate: '2025-03-26', searchClientId: 'uid', count: 10 });

// Session by search session ID
await Analytics.getSearchSessionBySearchSessionId({
  startDate: '2025-01-01', endDate: '2025-03-26',
  searchClientId: 'uid', sessionId: 'session-id'
});

// Session by case UID (authenticated)
await Analytics.searchSessionByCaseUidAuth({ startDate: '2025-01-01', endDate: '2025-03-26', count: 10, caseUid: 'case-uuid' });

// Session by case UID
await Analytics.getSearchSessionByCaseUid({ startDate: '2025-01-01', endDate: '2025-03-26', count: 10, caseUid: 'case-uuid' });

Content

const Content = suRestClient.Content();

// Get all content sources
await Content.getContentSources();

// Get content source by ID
await Content.getContentSourceById({ contentSourceId: 'id' });

// Get objects and fields for a content source
await Content.getObjectAndFields({ contentSourceId: 'id' });

// Get object data (offset and size are optional; size max: 50)
await Content.getObjectSpecificData({ contentSourceId: 'cs-id', objectId: 'obj-id', offset: 0, size: 10 });

// Get a specific document by ID
await Content.getObjectSpecificDataWithId({ contentSourceId: 'cs-id', objectId: 'obj-id', documentId: 'doc-id' });

// Update a document (data must be a non-empty object)
await Content.updateDoucmentById({
  contentSourceId: 'cs-id',
  objectId: 'obj-id',
  documentId: 'doc-id',
  data: { field: 'value' }
});

// Bulk upload documents (data must be a non-empty array)
await Content.uploadData({
  contentSourceId: 'cs-id',
  objectId: 'obj-id',
  data: [{ field: 'value' }]
});

Documentation

Please refer to the SearchUnify developer guide to use the SDK. https://docs.searchunify.com/Content/Developer-Guides/SDKs.htm

License

MIT

© Powered by SearchUnify!