Skip to main content

Search Block

Search Block

Full-text search engine for modern applications

The Search Block provides lightning-fast full-text search capabilities for your application. Index your data, perform instant searches with typo tolerance, and deliver relevant results with faceting, filtering, and analytics—all through a simple REST API.

Features

  • Full-Text Search - Lightning-fast queries across all indexed content
  • Typo Tolerance - Fuzzy matching handles misspellings automatically
  • Faceted Filters - Dynamic faceting for result refinement
  • Auto-Suggestions - Real-time search suggestions as users type
  • Synonyms & Stemming - Language-aware matching with custom synonyms
  • Search Analytics - Track popular queries and optimize relevance
  • Custom Ranking - Configure ranking rules and boosting
  • Multi-Index - Federated search across multiple indexes
  • Highlighting - Show matched terms in context
  • Geo Search - Location-aware search and sorting

API Endpoint

ServiceURL
Searchhttps://search.api.us.23blocks.com

Environment Routing: Use the same URL for all environments. Your API key determines the environment:

  • pk_test_* / sk_test_* → Routes to Staging
  • pk_live_* / sk_live_* → Routes to Production

Quick Start

npm install @23blocks/sdk
import { create23BlocksClient } from '@23blocks/sdk';

const client = create23BlocksClient({
urls: { search: 'https://search.api.us.23blocks.com' },
apiKey: 'your-api-key', // Use pk_test_* for staging, pk_live_* for production
});

// Create an index
await client.search.createIndex('products');

// Index documents
await client.search.indexDocuments('products', [
{ id: '1', name: 'Wireless Headphones', category: 'electronics', price: 299 },
{ id: '2', name: 'Bluetooth Speaker', category: 'electronics', price: 149 },
{ id: '3', name: 'Running Shoes', category: 'sports', price: 129 }
]);

// Search with typo tolerance
const results = await client.search.query('products', {
query: 'wirless headphnes', // typos handled!
filters: { category: 'electronics' },
facets: ['category', 'brand']
});

// Get suggestions
const suggestions = await client.search.suggest('products', {
query: 'wire',
limit: 5
});

Authentication

All API requests require authentication using your API credentials.

Required Headers

X-App-Id: your-app-id
X-Api-Key: your-api-key
Content-Type: application/json

Indexes API

Manage your search indexes.

Create Index

Create a new search index.

POST /search/v1/indexes

Request Body:

{
"data": {
"type": "indexes",
"attributes": {
"uid": "products",
"primary_key": "id",
"settings": {
"searchable_attributes": ["name", "description", "category"],
"filterable_attributes": ["category", "brand", "price"],
"sortable_attributes": ["price", "created_at", "popularity"],
"ranking_rules": [
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness"
],
"stop_words": ["the", "a", "an"],
"distinct_attribute": null,
"typo_tolerance": {
"enabled": true,
"min_word_size_for_typos": {
"one_typo": 5,
"two_typos": 9
}
}
}
}
}
}

Response:

{
"data": {
"type": "indexes",
"id": "idx_a1b2c3d4",
"attributes": {
"uid": "products",
"primary_key": "id",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:30:00Z",
"number_of_documents": 0,
"is_indexing": false
}
}
}

List Indexes

Retrieve all indexes.

GET /search/v1/indexes

Response:

{
"data": [
{
"type": "indexes",
"id": "idx_a1b2c3d4",
"attributes": {
"uid": "products",
"primary_key": "id",
"number_of_documents": 15420,
"is_indexing": false
}
},
{
"type": "indexes",
"id": "idx_e5f6g7h8",
"attributes": {
"uid": "articles",
"primary_key": "id",
"number_of_documents": 3250,
"is_indexing": false
}
}
]
}

Get Index

Retrieve a specific index.

GET /search/v1/indexes/:index_uid

Response:

{
"data": {
"type": "indexes",
"id": "idx_a1b2c3d4",
"attributes": {
"uid": "products",
"primary_key": "id",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T12:45:00Z",
"number_of_documents": 15420,
"is_indexing": false,
"settings": {
"searchable_attributes": ["name", "description", "category"],
"filterable_attributes": ["category", "brand", "price"],
"sortable_attributes": ["price", "created_at", "popularity"]
}
}
}
}

Update Index Settings

Update index configuration.

PATCH /search/v1/indexes/:index_uid/settings

Request Body:

{
"data": {
"type": "index_settings",
"attributes": {
"searchable_attributes": ["name", "description", "category", "tags"],
"filterable_attributes": ["category", "brand", "price", "in_stock"],
"synonyms": {
"phone": ["smartphone", "mobile", "cell phone"],
"laptop": ["notebook", "computer"]
}
}
}
}

Delete Index

Delete an index and all its documents.

DELETE /search/v1/indexes/:index_uid

Documents API

Manage documents within an index.

Index Documents

Add or update documents.

POST /search/v1/indexes/:index_uid/documents

Request Body:

{
"data": {
"type": "documents",
"attributes": {
"documents": [
{
"id": "prod_001",
"name": "Wireless Noise-Cancelling Headphones",
"description": "Premium over-ear headphones with active noise cancellation",
"category": "electronics",
"brand": "AudioMax",
"price": 299.99,
"in_stock": true,
"rating": 4.7,
"tags": ["wireless", "noise-cancelling", "premium"]
},
{
"id": "prod_002",
"name": "Portable Bluetooth Speaker",
"description": "Waterproof speaker with 360-degree sound",
"category": "electronics",
"brand": "SoundWave",
"price": 149.99,
"in_stock": true,
"rating": 4.5,
"tags": ["bluetooth", "portable", "waterproof"]
}
]
}
}
}

Response:

{
"data": {
"type": "indexing_tasks",
"id": "task_xyz123",
"attributes": {
"status": "enqueued",
"documents_received": 2,
"enqueued_at": "2025-01-15T14:30:00Z"
}
}
}

Get Document

Retrieve a specific document.

GET /search/v1/indexes/:index_uid/documents/:document_id

Response:

{
"data": {
"type": "documents",
"id": "prod_001",
"attributes": {
"name": "Wireless Noise-Cancelling Headphones",
"description": "Premium over-ear headphones with active noise cancellation",
"category": "electronics",
"brand": "AudioMax",
"price": 299.99,
"in_stock": true,
"rating": 4.7,
"tags": ["wireless", "noise-cancelling", "premium"]
}
}
}

List Documents

Browse documents in an index.

GET /search/v1/indexes/:index_uid/documents?offset=0&limit=20

Query Parameters:

ParameterTypeDescription
offsetintegerNumber of documents to skip
limitintegerNumber of documents to return (max 1000)
fieldsstringComma-separated list of fields to return
filterstringFilter expression

Delete Documents

Delete specific documents.

DELETE /search/v1/indexes/:index_uid/documents

Request Body:

{
"data": {
"type": "document_deletions",
"attributes": {
"document_ids": ["prod_001", "prod_002", "prod_003"]
}
}
}

Delete Documents by Filter

Delete documents matching a filter.

POST /search/v1/indexes/:index_uid/documents/delete-by-filter

Request Body:

{
"data": {
"type": "document_deletions",
"attributes": {
"filter": "category = 'deprecated' AND updated_at < '2024-01-01'"
}
}
}

Search API

Perform searches across your indexed data.

POST /search/v1/indexes/:index_uid/search

Request Body:

{
"data": {
"type": "search_queries",
"attributes": {
"query": "wireless headphones",
"limit": 20,
"offset": 0
}
}
}

Response:

{
"data": {
"type": "search_results",
"attributes": {
"hits": [
{
"id": "prod_001",
"name": "Wireless Noise-Cancelling Headphones",
"description": "Premium over-ear headphones with active noise cancellation",
"category": "electronics",
"price": 299.99,
"_formatted": {
"name": "<em>Wireless</em> Noise-Cancelling <em>Headphones</em>",
"description": "Premium over-ear <em>headphones</em> with active noise cancellation"
},
"_matchesPosition": {
"name": [{ "start": 0, "length": 8 }, { "start": 31, "length": 10 }]
}
}
],
"offset": 0,
"limit": 20,
"estimated_total_hits": 128,
"processing_time_ms": 12,
"query": "wireless headphones"
}
}
}

Search with Filters

Apply filters to narrow results.

{
"data": {
"type": "search_queries",
"attributes": {
"query": "headphones",
"filter": "category = 'electronics' AND price < 300 AND in_stock = true",
"limit": 20
}
}
}

Filter Syntax:

OperatorDescriptionExample
=Equalscategory = 'electronics'
!=Not equalsbrand != 'Generic'
>Greater thanprice > 100
>=Greater or equalrating >= 4.0
<Less thanprice < 500
<=Less or equalstock <= 10
INIn arraycategory IN ['electronics', 'audio']
NOT INNot in arraystatus NOT IN ['deleted', 'hidden']
EXISTSField existsdiscount EXISTS
NOT EXISTSField doesn't existarchived NOT EXISTS
ANDLogical andprice < 100 AND in_stock = true
ORLogical orcategory = 'audio' OR category = 'video'

Search with Facets

Get facet counts for result refinement.

{
"data": {
"type": "search_queries",
"attributes": {
"query": "headphones",
"facets": ["category", "brand", "price"],
"limit": 20
}
}
}

Response:

{
"data": {
"type": "search_results",
"attributes": {
"hits": [],
"facet_distribution": {
"category": {
"electronics": 95,
"audio": 28,
"gaming": 12
},
"brand": {
"AudioMax": 42,
"SoundWave": 35,
"TechPro": 28
}
},
"facet_stats": {
"price": {
"min": 29.99,
"max": 599.99,
"avg": 189.50,
"sum": 25678.50
}
}
}
}
}

Search with Sorting

Sort results by specific attributes.

{
"data": {
"type": "search_queries",
"attributes": {
"query": "headphones",
"sort": ["price:asc", "rating:desc"],
"limit": 20
}
}
}

Search with Highlighting

Configure result highlighting.

{
"data": {
"type": "search_queries",
"attributes": {
"query": "wireless",
"attributes_to_highlight": ["name", "description"],
"highlight_pre_tag": "<mark>",
"highlight_post_tag": "</mark>",
"limit": 20
}
}
}

Search with geographic constraints.

{
"data": {
"type": "search_queries",
"attributes": {
"query": "coffee shop",
"filter": "_geoRadius(40.7128, -74.0060, 5000)",
"sort": ["_geoPoint(40.7128, -74.0060):asc"],
"limit": 20
}
}
}

Search across multiple indexes.

POST /search/v1/multi-search

Request Body:

{
"data": {
"type": "multi_search_queries",
"attributes": {
"queries": [
{
"index_uid": "products",
"query": "headphones",
"limit": 5
},
{
"index_uid": "articles",
"query": "headphones review",
"limit": 3
}
]
}
}
}

Suggestions API

Provide search suggestions as users type.

Get Suggestions

POST /search/v1/indexes/:index_uid/suggest

Request Body:

{
"data": {
"type": "suggestion_queries",
"attributes": {
"query": "wire",
"limit": 5,
"highlight": true
}
}
}

Response:

{
"data": {
"type": "suggestions",
"attributes": {
"suggestions": [
{
"suggestion": "wireless headphones",
"highlighted": "<em>wire</em>less headphones",
"frequency": 1250
},
{
"suggestion": "wireless speaker",
"highlighted": "<em>wire</em>less speaker",
"frequency": 890
},
{
"suggestion": "wireless charger",
"highlighted": "<em>wire</em>less charger",
"frequency": 654
},
{
"suggestion": "wireless mouse",
"highlighted": "<em>wire</em>less mouse",
"frequency": 421
},
{
"suggestion": "wireless keyboard",
"highlighted": "<em>wire</em>less keyboard",
"frequency": 387
}
],
"processing_time_ms": 3
}
}
}

Configure Suggestions

Configure suggestion behavior.

PATCH /search/v1/indexes/:index_uid/settings/suggestions

Request Body:

{
"data": {
"type": "suggestion_settings",
"attributes": {
"enabled": true,
"source_attributes": ["name", "category", "tags"],
"min_query_length": 2,
"max_suggestions": 10,
"include_frequency": true
}
}
}

Synonyms API

Configure synonyms for better search matching.

Set Synonyms

PUT /search/v1/indexes/:index_uid/settings/synonyms

Request Body:

{
"data": {
"type": "synonyms",
"attributes": {
"synonyms": {
"phone": ["smartphone", "mobile", "cell phone", "cellphone"],
"laptop": ["notebook", "computer", "pc"],
"tv": ["television", "smart tv", "flat screen"],
"headphones": ["earphones", "earbuds", "headset"]
},
"one_way_synonyms": {
"iphone": ["phone", "smartphone"],
"macbook": ["laptop", "notebook"]
}
}
}
}

Get Synonyms

GET /search/v1/indexes/:index_uid/settings/synonyms

Response:

{
"data": {
"type": "synonyms",
"attributes": {
"synonyms": {
"phone": ["smartphone", "mobile", "cell phone", "cellphone"],
"laptop": ["notebook", "computer", "pc"]
},
"one_way_synonyms": {
"iphone": ["phone", "smartphone"]
}
}
}
}

Stop Words API

Configure stop words to ignore in searches.

Set Stop Words

PUT /search/v1/indexes/:index_uid/settings/stop-words

Request Body:

{
"data": {
"type": "stop_words",
"attributes": {
"stop_words": ["the", "a", "an", "and", "or", "but", "is", "are", "was", "were"]
}
}
}

Ranking Rules API

Configure how results are ranked.

Set Ranking Rules

PUT /search/v1/indexes/:index_uid/settings/ranking-rules

Request Body:

{
"data": {
"type": "ranking_rules",
"attributes": {
"ranking_rules": [
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness",
"popularity:desc",
"rating:desc"
]
}
}
}

Built-in Ranking Rules:

RuleDescription
wordsMatches with more query words rank higher
typoMatches with fewer typos rank higher
proximityMatches with words closer together rank higher
attributeMatches in more important attributes rank higher
sortApplies explicit sorting from search request
exactnessExact matches rank higher than partial matches

Analytics API

Track search analytics and user behavior.

GET /search/v1/indexes/:index_uid/analytics/popular-searches

Query Parameters:

ParameterTypeDescription
periodstringTime period: 24h, 7d, 30d, 90d
limitintegerNumber of results (default: 10)

Response:

{
"data": {
"type": "analytics",
"attributes": {
"popular_searches": [
{
"query": "wireless headphones",
"count": 12450,
"unique_users": 8920,
"avg_click_position": 1.3
},
{
"query": "bluetooth speaker",
"count": 8730,
"unique_users": 6540,
"avg_click_position": 1.8
}
],
"period": "7d",
"total_searches": 156780
}
}
}

Get No-Result Searches

Find queries with no results.

GET /search/v1/indexes/:index_uid/analytics/no-result-searches

Response:

{
"data": {
"type": "analytics",
"attributes": {
"no_result_searches": [
{
"query": "solar charger",
"count": 234,
"last_searched_at": "2025-01-15T14:30:00Z"
},
{
"query": "wireless earbuds case",
"count": 189,
"last_searched_at": "2025-01-15T14:25:00Z"
}
],
"period": "7d",
"total_no_result_searches": 1250
}
}
}

Get Click Analytics

Track which results users click.

GET /search/v1/indexes/:index_uid/analytics/clicks

Response:

{
"data": {
"type": "analytics",
"attributes": {
"click_data": [
{
"document_id": "prod_001",
"clicks": 5420,
"impressions": 18500,
"ctr": 0.293,
"avg_position": 1.2
}
],
"period": "7d",
"total_clicks": 45600,
"total_impressions": 156780,
"avg_ctr": 0.29
}
}
}

Track Search Event

Log search analytics events.

POST /search/v1/analytics/events

Request Body:

{
"data": {
"type": "search_events",
"attributes": {
"event_type": "click",
"index_uid": "products",
"query": "wireless headphones",
"document_id": "prod_001",
"position": 1,
"user_id": "user_abc123",
"session_id": "sess_xyz789"
}
}
}

Tasks API

Monitor indexing tasks.

Get Task Status

GET /search/v1/tasks/:task_id

Response:

{
"data": {
"type": "tasks",
"id": "task_xyz123",
"attributes": {
"status": "succeeded",
"task_type": "document_addition",
"index_uid": "products",
"details": {
"documents_received": 250,
"documents_indexed": 250
},
"enqueued_at": "2025-01-15T14:30:00Z",
"started_at": "2025-01-15T14:30:01Z",
"finished_at": "2025-01-15T14:30:03Z",
"duration_ms": 2340
}
}
}

List Tasks

GET /search/v1/tasks?status=succeeded&index_uid=products&limit=20

Task Statuses:

StatusDescription
enqueuedTask is waiting to be processed
processingTask is currently being processed
succeededTask completed successfully
failedTask failed with an error
canceledTask was canceled

Webhooks

Configure webhooks for search events.

Register Webhook

POST /search/v1/webhooks

Request Body:

{
"data": {
"type": "webhooks",
"attributes": {
"url": "https://your-app.com/webhooks/search",
"events": [
"indexing.completed",
"indexing.failed",
"index.created",
"index.deleted"
],
"secret": "your-webhook-secret"
}
}
}

Webhook Events

EventDescription
indexing.completedBatch indexing completed successfully
indexing.failedBatch indexing failed
index.createdNew index created
index.deletedIndex was deleted
settings.updatedIndex settings changed

Webhook Payload Example

{
"event": "indexing.completed",
"occurred_at": "2025-01-15T14:30:03Z",
"data": {
"task_id": "task_xyz123",
"index_uid": "products",
"documents_indexed": 250,
"duration_ms": 2340
}
}

Error Handling

Error Response Format

{
"errors": [
{
"status": "400",
"code": "invalid_filter_syntax",
"title": "Invalid Filter Syntax",
"detail": "The filter 'price <> 100' contains an invalid operator. Use '!=' instead of '<>'.",
"source": {
"parameter": "filter"
}
}
]
}

Common Error Codes

CodeStatusDescription
index_not_found404The specified index does not exist
document_not_found404The specified document does not exist
invalid_filter_syntax400Filter syntax is invalid
invalid_sort_attribute400Sort attribute is not sortable
attribute_not_filterable400Attribute is not configured as filterable
max_documents_exceeded400Document batch exceeds maximum size
index_already_exists409Index with this UID already exists
rate_limit_exceeded429Too many requests

SDK Examples

React Search Component

import { useState, useEffect, useCallback } from 'react';
import { useDebounce } from 'use-debounce';
import { create23BlocksClient } from '@23blocks/sdk';

const client = create23BlocksClient({
urls: { search: 'https://search.api.us.23blocks.com' },
appId: 'your-app-id',
});

function ProductSearch() {
const [query, setQuery] = useState('');
const [debouncedQuery] = useDebounce(query, 300);
const [results, setResults] = useState([]);
const [suggestions, setSuggestions] = useState([]);
const [facets, setFacets] = useState({});
const [filters, setFilters] = useState({});
const [loading, setLoading] = useState(false);

// Fetch suggestions as user types
useEffect(() => {
if (debouncedQuery.length < 2) {
setSuggestions([]);
return;
}

client.search.suggest('products', {
query: debouncedQuery,
limit: 5
}).then(data => {
setSuggestions(data.suggestions);
});
}, [debouncedQuery]);

// Perform search
const performSearch = useCallback(async (searchQuery) => {
if (!searchQuery) {
setResults([]);
setFacets({});
return;
}

setLoading(true);
try {
const response = await client.search.query('products', {
query: searchQuery,
filter: buildFilterString(filters),
facets: ['category', 'brand'],
limit: 20,
attributesToHighlight: ['name', 'description']
});

setResults(response.hits);
setFacets(response.facetDistribution);

// Track search analytics
await client.search.trackEvent({
eventType: 'search',
indexUid: 'products',
query: searchQuery,
resultsCount: response.estimatedTotalHits
});
} finally {
setLoading(false);
}
}, [filters]);

// Handle result click
const handleResultClick = async (hit, position) => {
await client.search.trackEvent({
eventType: 'click',
indexUid: 'products',
query,
documentId: hit.id,
position
});
};

return (
<div className="search-container">
{/* Search Input */}
<div className="search-input-wrapper">
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && performSearch(query)}
placeholder="Search products..."
className="search-input"
/>

{/* Suggestions Dropdown */}
{suggestions.length > 0 && (
<ul className="suggestions-dropdown">
{suggestions.map((suggestion, index) => (
<li
key={index}
onClick={() => {
setQuery(suggestion.suggestion);
performSearch(suggestion.suggestion);
setSuggestions([]);
}}
dangerouslySetInnerHTML={{ __html: suggestion.highlighted }}
/>
))}
</ul>
)}
</div>

<div className="search-layout">
{/* Facets Sidebar */}
<aside className="facets-sidebar">
{Object.entries(facets).map(([facetName, values]) => (
<div key={facetName} className="facet-group">
<h3>{facetName}</h3>
{Object.entries(values).map(([value, count]) => (
<label key={value}>
<input
type="checkbox"
checked={filters[facetName]?.includes(value)}
onChange={() => toggleFilter(facetName, value)}
/>
{value} ({count})
</label>
))}
</div>
))}
</aside>

{/* Results */}
<main className="search-results">
{loading ? (
<div className="loading">Searching...</div>
) : (
<ul>
{results.map((hit, index) => (
<li
key={hit.id}
onClick={() => handleResultClick(hit, index + 1)}
className="result-item"
>
<h3 dangerouslySetInnerHTML={{
__html: hit._formatted?.name || hit.name
}} />
<p dangerouslySetInnerHTML={{
__html: hit._formatted?.description || hit.description
}} />
<span className="price">${hit.price}</span>
</li>
))}
</ul>
)}
</main>
</div>
</div>
);
}

Vue 3 Composable

<script setup>
import { ref, watch, computed } from 'vue';
import { useDebounceFn } from '@vueuse/core';
import { create23BlocksClient } from '@23blocks/sdk';

const client = create23BlocksClient({
urls: { search: 'https://search.api.us.23blocks.com' },
appId: 'your-app-id',
});

const query = ref('');
const results = ref([]);
const suggestions = ref([]);
const facets = ref({});
const selectedFilters = ref({});
const loading = ref(false);

// Debounced suggestion fetch
const fetchSuggestions = useDebounceFn(async (searchQuery) => {
if (searchQuery.length < 2) {
suggestions.value = [];
return;
}

const data = await client.search.suggest('products', {
query: searchQuery,
limit: 5
});
suggestions.value = data.suggestions;
}, 300);

watch(query, (newQuery) => {
fetchSuggestions(newQuery);
});

// Build filter string from selected filters
const filterString = computed(() => {
const parts = [];
for (const [key, values] of Object.entries(selectedFilters.value)) {
if (values.length > 0) {
parts.push(`${key} IN [${values.map(v => `'${v}'`).join(', ')}]`);
}
}
return parts.join(' AND ');
});

// Perform search
async function search() {
if (!query.value) return;

loading.value = true;
try {
const response = await client.search.query('products', {
query: query.value,
filter: filterString.value || undefined,
facets: ['category', 'brand'],
limit: 20
});

results.value = response.hits;
facets.value = response.facetDistribution;
} finally {
loading.value = false;
}
}

// Toggle facet filter
function toggleFilter(facetName, value) {
if (!selectedFilters.value[facetName]) {
selectedFilters.value[facetName] = [];
}

const index = selectedFilters.value[facetName].indexOf(value);
if (index === -1) {
selectedFilters.value[facetName].push(value);
} else {
selectedFilters.value[facetName].splice(index, 1);
}

search();
}
</script>

<template>
<div class="search-page">
<div class="search-box">
<input
v-model="query"
@keyup.enter="search"
placeholder="Search products..."
/>

<ul v-if="suggestions.length" class="suggestions">
<li
v-for="(suggestion, index) in suggestions"
:key="index"
@click="query = suggestion.suggestion; search(); suggestions = []"
v-html="suggestion.highlighted"
/>
</ul>
</div>

<div class="content">
<aside class="filters">
<div v-for="(values, facetName) in facets" :key="facetName">
<h4>{{ facetName }}</h4>
<label v-for="(count, value) in values" :key="value">
<input
type="checkbox"
:checked="selectedFilters[facetName]?.includes(value)"
@change="toggleFilter(facetName, value)"
/>
{{ value }} ({{ count }})
</label>
</div>
</aside>

<main class="results">
<div v-if="loading">Loading...</div>
<div v-else-if="results.length === 0">No results found</div>
<ul v-else>
<li v-for="hit in results" :key="hit.id">
<h3 v-html="hit._formatted?.name || hit.name" />
<p v-html="hit._formatted?.description || hit.description" />
<span class="price">${{ hit.price }}</span>
</li>
</ul>
</main>
</div>
</div>
</template>

Rate Limits

PlanSearches/secondDocuments/indexIndexes
Free1010,0003
Starter50100,00010
Growth2001,000,00050
EnterpriseCustomUnlimitedUnlimited

Best Practices

Indexing

  1. Batch documents - Index in batches of 100-1000 documents for optimal performance
  2. Use primary keys - Always define a primary key for document identification
  3. Configure searchable attributes - Only index fields that need to be searchable
  4. Set filterable attributes - Pre-configure attributes you'll filter on

Searching

  1. Use suggestions - Implement auto-complete for better UX
  2. Apply filters wisely - Filters are fast, use them for category navigation
  3. Track analytics - Log searches and clicks to improve relevance
  4. Handle typos - Typo tolerance is enabled by default

Performance

  1. Debounce queries - Wait 200-300ms between keystrokes before searching
  2. Limit results - Request only needed fields and reasonable result counts
  3. Cache static facets - Cache facet distributions that don't change often
  4. Use pagination - Implement infinite scroll or pagination for large result sets