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
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
apiKey | string | Yes | - | Your API key (zsk_pk_ or zsk_sk_) |
baseUrl | string | No | https://api.zensearch.dev | API base URL |
defaultCollection | string | No | - | Default collection ID for searches |
timeout | number | No | 30000 | Request timeout in milliseconds |
fetch | typeof fetch | No | globalThis.fetch | Custom 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
| Option | Type | Default | Description |
|---|---|---|---|
collection | string | defaultCollection | Collection ID to search |
limit | number | 10 | Maximum results (max: 100) |
offset | number | 0 | Pagination offset |
minScore | number | 0.5 | Minimum relevance score (0-1). Results below this are filtered out |
highlight | boolean | false | Enable term highlighting |
highlightPre | string | <mark> | Custom highlight start tag |
highlightPost | string | </mark> | Custom highlight end tag |
section | string | - | Filter by documentation section |
version | string | - | Filter by documentation version |
tags | string[] | - | Filter by tags |
docTypes | string[] | - | Filter by document types |
language | string | - | Filter by language |
signal | AbortSignal | - | 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
| Option | Type | Default | Description |
|---|---|---|---|
collection | string | defaultCollection | Collection ID to search |
limit | number | 5 | Maximum suggestions (max: 20) |
signal | AbortSignal | - | 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
| Type | Description | Required Fields |
|---|---|---|
click | User clicked a result | queryId, url, position |
feedback | User provided feedback | queryId, 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
| Method | Returns | Description |
|---|---|---|
isClientError() | boolean | True for 4xx errors |
isServerError() | boolean | True for 5xx errors |
isAuthError() | boolean | True for 401/403 errors |
isRateLimitError() | boolean | True for 429 errors |
Error Properties
| Property | Type | Description |
|---|---|---|
message | string | Error message |
status | number | HTTP status code |
code | string? | 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
- Authentication - API key types and security
- API Keys Settings - Manage your API keys
- Search API - REST API reference