From algolia-pack
Audits Algolia usage, optimizes costs via virtual replicas, batching/caching, and Analytics API monitoring. For high bills on records/searches.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin algolia-packThis skill is limited to using the following tools:
Algolia pricing is based on **search requests** and **records**. A search request is one API call (which may contain multiple queries via `search({ requests: [...] })`). Records are counted across all indices including replicas.
Optimizes Algolia search performance via record size reduction, searchable attributes tuning, replicas, caching, and query parameters. For slow searches and high latency.
Provides patterns for Algolia search implementation using React InstantSearch hooks, indexing strategies, relevance tuning, and Next.js SSR integration.
Automates Algolia operations like indexing, searching, and record management via Composio toolkit and Rube MCP. Discovers current tool schemas before execution.
Share bugs, ideas, or general feedback.
Algolia pricing is based on search requests and records. A search request is one API call (which may contain multiple queries via search({ requests: [...] })). Records are counted across all indices including replicas.
| Plan | Records Included | Search Requests | Additional Cost |
|---|---|---|---|
| Build (Free) | 1M records | 10K requests/mo | N/A |
| Grow | 100K free, then $0.40/1K | 10K free, then $0.50/1K | Pay as you go |
| Grow Plus | 100K free, then $0.40/1K | 10K free, then $1.75/1K | + AI features |
| Premium | Custom | Custom | Volume discounts |
searchSingleIndex() = 1 requestsearch({ requests: [q1, q2, q3] }) = 1 request (multi-query)browse() = 1 request per pagesaveObjects() = NOT a search request (indexing operations are free)import { algoliasearch } from 'algoliasearch';
const client = algoliasearch(process.env.ALGOLIA_APP_ID!, process.env.ALGOLIA_ADMIN_KEY!);
// Check total records across all indices
const { items } = await client.listIndices();
let totalRecords = 0;
let replicaRecords = 0;
items.forEach(idx => {
const records = idx.entries || 0;
console.log(`${idx.name}: ${records.toLocaleString()} records, ${(idx.dataSize || 0 / 1024).toFixed(0)}KB`);
if (idx.name.includes('_replica') || idx.primary) {
replicaRecords += records;
}
totalRecords += records;
});
console.log(`\nTotal: ${totalRecords.toLocaleString()} records (${replicaRecords.toLocaleString()} in replicas)`);
// Standard replicas: duplicate all records (doubles cost)
// Virtual replicas: share records with primary (no extra cost)
// BEFORE: 3 standard replicas = 4x record count
await client.setSettings({
indexName: 'products',
indexSettings: {
replicas: [
// 'products_price_asc', // Standard: costs records
// 'products_price_desc', // Standard: costs records
'virtual(products_price_asc)', // Virtual: FREE
'virtual(products_price_desc)', // Virtual: FREE
],
},
});
// Virtual replica limitation: can only customize ranking and customRanking
// If you need different searchableAttributes or attributesForFaceting, use standard
// BAD: 3 separate requests = 3 search operations billed
const results1 = await client.searchSingleIndex({ indexName: 'products', searchParams: { query: 'laptop' } });
const results2 = await client.searchSingleIndex({ indexName: 'articles', searchParams: { query: 'laptop' } });
const results3 = await client.searchSingleIndex({ indexName: 'faq', searchParams: { query: 'laptop' } });
// GOOD: 1 multi-query request = 1 search operation billed
const { results } = await client.search({
requests: [
{ indexName: 'products', query: 'laptop', hitsPerPage: 5 },
{ indexName: 'articles', query: 'laptop', hitsPerPage: 3 },
{ indexName: 'faq', query: 'laptop', hitsPerPage: 3 },
],
});
import { LRUCache } from 'lru-cache';
// Cache popular searches — Algolia's own CDN caches are limited
const searchCache = new LRUCache<string, any>({
max: 1000,
ttl: 5 * 60 * 1000, // 5 minutes for product search
});
async function cachedSearch(query: string, filters?: string) {
const key = JSON.stringify({ query, filters });
const cached = searchCache.get(key);
if (cached) {
console.log('Cache hit — saved 1 search request');
return cached;
}
const result = await client.searchSingleIndex({
indexName: 'products',
searchParams: { query, filters },
});
searchCache.set(key, result);
return result;
}
// Audit and clean up test/development indices
const { items } = await client.listIndices();
const devIndices = items.filter(i =>
i.name.startsWith('test_') ||
i.name.startsWith('dev_') ||
i.name.startsWith('ci_')
);
for (const idx of devIndices) {
console.log(`Deleting unused index: ${idx.name} (${idx.entries} records)`);
await client.deleteIndex({ indexName: idx.name });
}
// Track search volume trends
const { count: searchCount } = await client.getSearchesCount({
index: 'products',
startDate: '2025-01-01',
endDate: '2025-01-31',
});
console.log(`Search requests this month: ${searchCount.toLocaleString()}`);
// Identify no-result queries (wasted searches users will retry)
const { searches } = await client.getSearchesNoResults({
index: 'products',
startDate: '2025-01-01',
endDate: '2025-01-31',
});
console.log('Top no-result searches (fix these to reduce retries):');
searches.slice(0, 10).forEach(s => console.log(` "${s.search}" — ${s.count} times`));
| Strategy | Savings | Effort |
|---|---|---|
| Virtual replicas | 50-75% record cost | Low |
| Multi-query search | 60-80% fewer requests | Low |
| Client-side caching | 30-50% fewer requests | Low |
| Delete unused indices | Variable | Low |
| Fix no-result queries (synonyms) | 10-20% fewer retries | Medium |
| Reduce record size | Indirect (faster = cheaper) | Medium |
| Issue | Cause | Solution |
|---|---|---|
| Unexpected bill spike | Uncached bot traffic | Add rate limiting, cache layer |
| Record count higher than expected | Standard replicas | Switch to virtual replicas |
| Search requests over budget | No caching | Add LRU cache in API layer |
| Analytics API returns empty | Wrong date range or region | Check region parameter matches your app |
For architecture patterns, see algolia-reference-architecture.