Algolia's security model is built around scoped API keys. Every Algolia app has three default keys (Admin, Search-Only, Monitoring). For production, create custom keys with minimal permissions and use Secured API Keys for per-user/per-tenant restrictions.
| Key Type | ACL | Expose to Frontend? | Use Case |
|---|---|---|---|
| Admin | All operations | NEVER | Backend indexing, settings, key management |
| Search-Only | search only |
Yes (safe) | Frontend search widgets |
| Monitoring | Read monitoring data | No | Health checks, dashboards |
| Custom | You define ACL | Depends on ACL | Scoped backend services |
| Secured | Derived from parent key | Yes | Per-user filtered search |
# .env (NEVER commit — add to .gitignore)
ALGOLIA_APP_ID=YourApplicationID
ALGOLIA_ADMIN_KEY=admin_api_key_here # Backend only
ALGOLIA_SEARCH_KEY=search_only_key_here # OK for frontend
# .gitignore — MUST include:
.env
.env.local
.env.*.local
import { algoliasearch } from 'algoliasearch';
const client = algoliasearch(process.env.ALGOLIA_APP_ID!, process.env.ALGOLIA_ADMIN_KEY!);
// Create a write-only key for a specific microservice
const { key: indexingKey } = await client.addApiKey({
apiKey: {
acl: ['addObject', 'deleteObject', 'editSettings'],
description: 'Product sync service — write only',
indexes: ['products', 'products_staging'], // Restrict to specific indices
maxQueriesPerIPPerHour: 5000,
referers: [], // Empty = no referer restriction (backend use)
},
});
// Create a search key restricted to specific referers (frontend)
const { key: frontendKey } = await client.addApiKey({
apiKey: {
acl: ['search'],
description: 'Frontend search — domain-restricted',
indexes: ['products'],
referers: ['https://mystore.com/*', 'https://*.mystore.com/*'],
maxQueriesPerIPPerHour: 1000,
maxHitsPerQuery: 50,
},
});
// Secured API keys are generated on YOUR server, not via Algolia API.
// They embed restrictions that the client can't bypass.
function generateUserSearchKey(userId: string, tenantId: string): string {
const client = algoliasearch(process.env.ALGOLIA_APP_ID!, process.env.ALGOLIA_ADMIN_KEY!);
return client.generateSecuredApiKey({
parentApiKey: process.env.ALGOLIA_SEARCH_KEY!,
restrictions: {
// User can only see their tenant's data
filters: `tenant_id:${tenantId}`,
// Key expires in 1 hour
validUntil: Math.floor(Date.now() / 1000) + 3600,
// Restrict to specific indices
restrictIndices: ['products'],
// Optional: restrict sources (IPs)
restrictSources: '',
},
});
}
// Usage in your API endpoint:
// const userKey = generateUserSearchKey(req.user.id, req.user.tenantId);
// return { appId: process.env.ALGOLIA_APP_ID, searchKey: userKey };
async function rotateApiKey(oldKeyDescription: string) {
const client = algoliasearch(process.env.ALGOLIA_APP_ID!, process.env.ALGOLIA_ADMIN_KEY!);
// 1. List keys to find the old one
const { keys } = await client.listApiKeys();
const oldKey = keys.find(k => k.description === oldKeyDescription);
if (!oldKey) throw new Error(`Key not found: ${oldKeyDescription}`);
// 2. Create new key with same ACL
const { key: newKey } = await client.addApiKey({
apiKey: {
acl: oldKey.acl,
description: `${oldKeyDescription} (rotated ${new Date().toISOString().split('T')[0]})`,
indexes: oldKey.indexes || [],
maxQueriesPerIPPerHour: oldKey.maxQueriesPerIPPerHour || 0,
referers: oldKey.referers || [],
},
});
console.log(`New key created: ...${newKey.slice(-8)}`);
console.log('Update your env vars, then delete the old key:');
console.log(` client.deleteApiKey({ key: '${oldKey.value}' })`);
return newKey;
}
.env files in .gitignore
referers set on frontend keys to prevent abusemaxQueriesPerIPPerHour set on all public keysvalidUntil (expiration)| Security Issue | Detection | Mitigation |
|---|---|---|
| Admin key exposed in frontend | Code review, git scanning | Rotate immediately, restrict referers |
| Key in git history | git log -S 'ALGOLIA' |
Rotate key, use git-secrets or gitleaks |
| Excessive ACL on key | Audit key permissions | Create scoped replacement key |
| Expired secured key | validUntil in the past |
Generate fresh secured key |
For production deployment, see algolia-prod-checklist.