Skip to main content

Documentation Search SDK

The @zensearch/docs-sdk is the official JavaScript/TypeScript SDK for integrating ZenSearch documentation search into your applications.

Installation

npm install @zensearch/docs-sdk
# or
yarn add @zensearch/docs-sdk
# or
pnpm add @zensearch/docs-sdk

Quick Start

import { ZenSearchDocs } from '@zensearch/docs-sdk';

// Initialize with your publishable key
const client = new ZenSearchDocs({
apiKey: 'zsk_pk_xxx...', // Publishable key for client-side
defaultCollection: 'your-collection-id',
});

// Search documentation
const results = await client.search('authentication');
console.log(results.results);

// Get autocomplete suggestions
const suggestions = await client.autocomplete('auth');
console.log(suggestions.suggestions);

Configuration

const client = new ZenSearchDocs({
// Required: API key (publishable for client-side, secret for server-side)
apiKey: 'zsk_pk_xxx...',

// Optional: Custom API base URL (default: https://api.zensearch.dev)
baseUrl: 'https://api.zensearch.dev',

// Optional: Default collection to search
defaultCollection: 'collection-uuid',

// Optional: Request timeout in milliseconds (default: 30000)
timeout: 30000,

// Optional: Custom fetch implementation (for Node.js/SSR)
fetch: customFetch,
});

Configuration Options

OptionTypeRequiredDefaultDescription
apiKeystringYes-Your API key (zsk_pk_ or zsk_sk_)
baseUrlstringNohttps://api.zensearch.devAPI base URL
defaultCollectionstringNo-Default collection ID for searches
timeoutnumberNo30000Request timeout in milliseconds
fetchtypeof fetchNoglobalThis.fetchCustom fetch implementation

API Reference

search(query, options?)

Search documentation with filtering and pagination.

const results = await client.search('authentication', {
limit: 10, // Max results (default: 10, max: 100)
offset: 0, // Pagination offset
minScore: 0.5, // Min relevance score (0-1, default: 0.5)
highlight: true, // Enable term highlighting
section: 'guides', // Filter by section
version: '2.0', // Filter by version
tags: ['security'], // Filter by tags
docTypes: ['api'], // Filter by document types
language: 'en', // Filter by language
});

Search Options

OptionTypeDefaultDescription
collectionstringdefaultCollectionCollection ID to search
limitnumber10Maximum results (max: 100)
offsetnumber0Pagination offset
minScorenumber0.5Minimum relevance score (0-1). Results below this are filtered out
highlightbooleanfalseEnable term highlighting
highlightPrestring<mark>Custom highlight start tag
highlightPoststring</mark>Custom highlight end tag
sectionstring-Filter by documentation section
versionstring-Filter by documentation version
tagsstring[]-Filter by tags
docTypesstring[]-Filter by document types
languagestring-Filter by language
signalAbortSignal-Abort signal for cancellation

Search Response

interface SearchResponse {
results: SearchResult[]; // Array of search results
queryId: string; // Unique query ID for analytics
tookMs: number; // Search duration in milliseconds
totalHits: number; // Total matching documents
hasMore: boolean; // Whether more results are available
}

interface SearchResult {
url: string; // Document URL
anchor?: string; // Anchor fragment (if applicable)
title: string; // Document/section title
snippet: string; // Content snippet
hierarchy: string[]; // Breadcrumb path
level: number; // Depth level in structure
score: number; // Relevance score (0-1)
suType: string; // Structural unit type
highlights?: { // Highlighted content (if enabled)
title?: string;
content?: string;
};
}

autocomplete(query, options?)

Get search suggestions as the user types.

const suggestions = await client.autocomplete('auth', {
limit: 5, // Max suggestions (default: 5, max: 20)
});

Autocomplete Options

OptionTypeDefaultDescription
collectionstringdefaultCollectionCollection ID to search
limitnumber5Maximum suggestions (max: 20)
signalAbortSignal-Abort signal for cancellation

Autocomplete Response

interface AutocompleteResponse {
query: string; // The input query
suggestions: AutocompleteSuggestion[];
}

interface AutocompleteSuggestion {
text: string; // Suggested text
type: 'title' | 'heading' | 'recent'; // Suggestion type
score: number; // Relevance score
hierarchy?: string; // Hierarchy path
url?: string; // Document URL
}

getDocument(documentId)

Retrieve a specific document by ID.

const doc = await client.getDocument('document-uuid');

Document Response

interface Document {
id: string; // Document ID
collectionId?: string; // Collection ID
title: string; // Document title
content: string; // Full document content
url: string; // Document URL
hierarchy: string[]; // Breadcrumb path
suType: string; // Structural unit type
metadata?: Record<string, unknown>;
updatedAt: string; // Last updated timestamp
}

trackEvent(event)

Track analytics events for search improvements.

// Track a click on a search result
await client.trackEvent({
type: 'click',
queryId: results.queryId,
url: results.results[0].url,
position: 1,
});

// Track user feedback
await client.trackEvent({
type: 'feedback',
queryId: results.queryId,
rating: 5,
});

Event Types

TypeDescriptionRequired Fields
clickUser clicked a resultqueryId, url, position
feedbackUser provided feedbackqueryId, rating

Error Handling

The SDK throws ZenSearchError for API errors:

import { ZenSearchDocs, ZenSearchError } from '@zensearch/docs-sdk';

try {
const results = await client.search('query');
} catch (error) {
if (error instanceof ZenSearchError) {
console.error(`Error: ${error.message} (${error.status})`);

if (error.isAuthError()) {
// Handle 401/403 - invalid or expired API key
console.error('Authentication failed');
} else if (error.isRateLimitError()) {
// Handle 429 - too many requests
console.error('Rate limit exceeded');
} else if (error.isServerError()) {
// Handle 5xx - server error
console.error('Server error, please retry');
}
}
}

ZenSearchError Methods

MethodReturnsDescription
isClientError()booleanTrue for 4xx errors
isServerError()booleanTrue for 5xx errors
isAuthError()booleanTrue for 401/403 errors
isRateLimitError()booleanTrue for 429 errors

Error Properties

PropertyTypeDescription
messagestringError message
statusnumberHTTP status code
codestring?Error code (if provided)

Request Cancellation

Cancel in-flight requests using AbortController:

const controller = new AbortController();

// Cancel after 5 seconds
setTimeout(() => controller.abort(), 5000);

try {
const results = await client.search('query', {
signal: controller.signal,
});
} catch (error) {
if (error instanceof ZenSearchError && error.status === 408) {
console.log('Request was cancelled');
}
}

Framework Examples

React

import { useState, useEffect } from 'react';
import { ZenSearchDocs } from '@zensearch/docs-sdk';

const client = new ZenSearchDocs({
apiKey: 'zsk_pk_xxx...',
defaultCollection: 'my-docs',
});

function SearchBox() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [loading, setLoading] = useState(false);

useEffect(() => {
if (query.length < 2) {
setResults([]);
return;
}

const controller = new AbortController();
setLoading(true);

client.search(query, {
signal: controller.signal,
highlight: true,
})
.then(res => {
setResults(res.results);
setLoading(false);
})
.catch(err => {
if (err.status !== 408) { // Ignore cancellation
console.error(err);
setLoading(false);
}
});

return () => controller.abort();
}, [query]);

return (
<div>
<input
type="search"
value={query}
onChange={e => setQuery(e.target.value)}
placeholder="Search documentation..."
/>
{loading && <p>Searching...</p>}
<ul>
{results.map(result => (
<li key={result.url}>
<a href={result.url}>
<strong dangerouslySetInnerHTML={{
__html: result.highlights?.title || result.title
}} />
</a>
<p dangerouslySetInnerHTML={{
__html: result.snippet
}} />
</li>
))}
</ul>
</div>
);
}

React with Debouncing

import { useState, useEffect, useMemo } from 'react';
import { ZenSearchDocs } from '@zensearch/docs-sdk';
import debounce from 'lodash.debounce';

function SearchBox() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);

const client = useMemo(() => new ZenSearchDocs({
apiKey: 'zsk_pk_xxx...',
}), []);

const debouncedSearch = useMemo(
() => debounce(async (q: string) => {
if (q.length < 2) return;
const res = await client.search(q);
setResults(res.results);
}, 300),
[client]
);

useEffect(() => {
debouncedSearch(query);
return () => debouncedSearch.cancel();
}, [query, debouncedSearch]);

// ... render
}

Next.js (Server Component)

// app/search/page.tsx
import { ZenSearchDocs } from '@zensearch/docs-sdk';

const client = new ZenSearchDocs({
apiKey: process.env.ZENSEARCH_SECRET_KEY!, // Server-side: use secret key
});

export default async function SearchPage({
searchParams,
}: {
searchParams: { q?: string };
}) {
const query = searchParams.q;

if (!query) {
return <p>Enter a search query</p>;
}

const results = await client.search(query, { limit: 20 });

return (
<div>
<h1>Results for "{query}"</h1>
<p>{results.totalHits} results in {results.tookMs}ms</p>
<ul>
{results.results.map(result => (
<li key={result.url}>
<a href={result.url}>{result.title}</a>
</li>
))}
</ul>
</div>
);
}

Node.js / Server-Side

import { ZenSearchDocs } from '@zensearch/docs-sdk';

// Use secret key for server-side
const client = new ZenSearchDocs({
apiKey: process.env.ZENSEARCH_SECRET_KEY!,
});

async function searchDocs(query: string) {
const results = await client.search(query, {
limit: 50,
highlight: true,
});

// Track search for analytics
console.log(`Query "${query}" returned ${results.totalHits} results`);

return results;
}

TypeScript Support

Full TypeScript support with exported types:

import type {
ZenSearchDocsConfig,
SearchOptions,
SearchResponse,
SearchResult,
AutocompleteOptions,
AutocompleteResponse,
AutocompleteSuggestion,
Document,
AnalyticsEvent,
AnalyticsEventType,
APIError,
} from '@zensearch/docs-sdk';

Docusaurus Integration

For Docusaurus sites, use the dedicated plugin instead:

npm install docusaurus-search-zensearch
// docusaurus.config.js
module.exports = {
themes: [
[
'docusaurus-search-zensearch',
{
apiKey: 'zsk_pk_xxx...',
collectionId: 'your-collection-id',
placeholder: 'Search documentation...',
maxResults: 10,
enableKeyboardShortcut: true, // Cmd/Ctrl + K
enableAnalytics: true,
},
],
],
};

Next Steps