Help us improve
Share bugs, ideas, or general feedback.
From algolia
Insights events and search analytics — designing the event taxonomy (eventType, eventName, objectIDs, queryID, userToken), wiring `view`, `click`, and `conversion` events from React / Next.js, the Insights middleware in InstantSearch, attributing search-driven revenue, A/B test setup, reading Search Analytics (top searches, no-result queries, click position, CTR by query), and the patterns that make every other AI feature work. Use this skill any time events are the topic — first integration, fixing missing attribution, debugging Personalization, evaluating relevance changes, or building the analytics dashboard for the engagement.
npx claudepluginhub bpainter/composable-dxp-claude-marketplace --plugin algoliaHow this skill is triggered — by the user, by Claude, or both
Slash command
/algolia:algolia-analytics-eventsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill puts you in the role of an engineer who treats search analytics as production infrastructure, not an afterthought. Default posture: **Insights events are the substrate that everything else stands on. Wire them on day one or accept that Recommend, Personalization, A/B, and Search Analytics will be flying blind.**
Guides technical evaluation of code review feedback: read fully, restate for understanding, verify against codebase, respond with reasoning or pushback before implementing.
Share bugs, ideas, or general feedback.
This skill puts you in the role of an engineer who treats search analytics as production infrastructure, not an afterthought. Default posture: Insights events are the substrate that everything else stands on. Wire them on day one or accept that Recommend, Personalization, A/B, and Search Analytics will be flying blind.
Pair with algolia-instantsearch-react (the click/view middleware), algolia-autocomplete (the Insights plugin), algolia-relevance-tuning (analytics drives every relevance change), and algolia-personalization-ai (Personalization is downstream of these events).
userToken mismatch.view, click, conversion. Don't invent more.queryID is the attribution key. Without it, click and conversion can't be tied to the search.userToken must be stable and consistent. The same token across search, click, view, conversion, Personalization. Otherwise nothing connects.userToken. Document the user-tracking posture.viewFired when a record is rendered to a user.
insightsClient('viewedObjectIDs', {
index: 'articles',
eventName: 'Articles Viewed',
userToken: 'user-abc',
objectIDs: hits.map((h) => h.objectID),
});
Use for:
Don't use for:
clickFired when the user clicks a record.
insightsClient('clickedObjectIDsAfterSearch', {
index: 'articles',
eventName: 'Article Clicked',
userToken: 'user-abc',
objectIDs: [hit.objectID],
positions: [hit.__position], // 1-indexed position in the SERP
queryID: hit.__queryID, // attribution to the search
});
For non-search clicks (e.g., clicking a Recommend item):
insightsClient('clickedObjectIDs', { // no queryID — not a search
index: 'articles',
eventName: 'Recommended Article Clicked',
userToken: 'user-abc',
objectIDs: [hit.objectID],
});
The two API names matter — *AfterSearch carries queryID; the other doesn't. Pick the right one.
conversionFired when the user does the meaningful thing.
// Search-attributed conversion
insightsClient('convertedObjectIDsAfterSearch', {
index: 'articles',
eventName: 'Whitepaper Downloaded',
userToken: 'user-abc',
objectIDs: [hit.objectID],
queryID: hit.__queryID,
});
// Commerce conversion with revenue
insightsClient('purchasedObjectIDsAfterSearch', {
index: 'products',
eventName: 'Order Placed',
userToken: 'user-abc',
objectIDs: orderItems.map((i) => i.objectID),
queryID: lastQueryID,
objectData: orderItems.map((i) => ({
queryID: lastQueryID,
price: i.price,
quantity: i.quantity,
discount: i.discount,
})),
currency: 'USD',
});
Conversion is whatever the engagement defines as success: download, signup, purchase, "request a meeting." Pick 1–3 conversions per surface and instrument them.
Don't let eventName proliferate. A taxonomy that grows past ~25 distinct names becomes unanalyzable. Sample:
| eventName | Type | When |
|---|---|---|
| Articles Viewed | view | SERP render of articles |
| Article Clicked | click | User clicks an article hit |
| Article Read | conversion | Scrolled past 50% on the article |
| Whitepaper Downloaded | conversion | User downloaded a whitepaper |
| Search Submitted | conversion (no objectIDs, but tracked) | User pressed enter on the search box |
| Recommended Item Clicked | click | Click on a Recommend widget item |
| Cart Item Added | conversion (commerce) | addToCart |
| Order Placed | conversion (commerce) | Purchase |
Document the taxonomy in the engagement's analytics doc. New events go through review.
userToken disciplineThe userToken ties everything together. Rules:
user-{hash}.For multi-tenant: include the tenant in or derive from the token (user-${tenantHash}-${userHash}) to avoid cross-tenant Personalization bleed.
import { InstantSearchNext } from 'react-instantsearch-nextjs';
import { Configure } from 'react-instantsearch';
<InstantSearchNext
indexName="articles"
searchClient={client}
insights={true}
routing
>
<Configure clickAnalytics />
...
</InstantSearchNext>
insights={true} enables the middleware. clickAnalytics on <Configure> ensures every search response includes queryID. The middleware automatically:
view events for hits as they render.click events when the user clicks a hit.queryID and userToken (from setUserToken).For setting userToken:
const insights = useInsights();
useEffect(() => {
insights('setUserToken', userToken);
}, [userToken, insights]);
Or via the InstantSearch root:
<InstantSearchNext
insights={{ insightsInitParams: { useCookie: true } }}
...
>
useCookie: true lets Algolia generate a cookie-based anonymous token. Reasonable default for sites that haven't built their own.
InstantSearch instruments view + click. Conversion is the engagement's job:
// On a download link
import { useInsights } from 'react-instantsearch';
function DownloadButton({ hit }: { hit: ArticleHit }) {
const insights = useInsights();
function onClick() {
insights('convertedObjectIDsAfterSearch', {
eventName: 'Whitepaper Downloaded',
index: 'articles',
objectIDs: [hit.objectID],
queryID: hit.__queryID,
});
// continue with download
}
return <button onClick={onClick}>Download</button>;
}
For server-side conversions (form submit, purchase webhook), call the Insights REST API directly:
await fetch('https://insights.algolia.io/1/events', {
method: 'POST',
headers: {
'X-Algolia-Application-Id': APP_ID,
'X-Algolia-API-Key': SEARCH_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
events: [
{
eventType: 'conversion',
eventName: 'Order Placed',
index: 'products',
userToken: order.userToken,
objectIDs: order.items.map((i) => i.objectID),
queryID: order.lastQueryID, // captured during checkout
timestamp: Date.now(),
},
],
}),
});
Server events always fire (no ad-blocker risk). Most engagements should mirror critical conversions server-side.
queryID through a multi-page funnelThe user searches on /search, clicks a hit, lands on /articles/{slug}, then clicks "request a demo," which submits a form. The conversion needs to know the original queryID.
Pattern:
click event (good). It also stores the queryID somewhere — a session cookie or query param.queryID (from ?queryID=... or cookie).queryID to the server.conversion with that queryID.// On clicking a hit — add queryID to the URL
<a href={`${hit.url}?queryID=${hit.__queryID}`}>{hit.title}</a>
// On the detail page
const params = useSearchParams();
const queryID = params.get('queryID');
sessionStorage.setItem('lastSearchQueryID', queryID || '');
// On form submit
const queryID = sessionStorage.getItem('lastSearchQueryID');
await fetch('/api/lead', { method: 'POST', body: JSON.stringify({ ..., queryID }) });
If the funnel is long (>30 minutes between search and conversion), the attribution gets fuzzier. Algolia stops attributing conversions after 24 hours by default.
The Dashboard's Analytics surface (or the Analytics API for piping elsewhere):
| Metric | What it tells you |
|---|---|
| Top searches | What people are looking for. Compare week-over-week. |
| Top searches with no results | Synonym gaps or content gaps. Both fixable. |
| Click-through rate (CTR) | Per-query CTR — low CTR on a frequent query means the SERP isn't compelling. |
| No-click rate | Percentage of searches with zero clicks — a different lens than no-results. |
| Mean click position | Higher = relevance is bad. Top hit isn't the best hit. |
| Top filters | What facets people use. Informs UI emphasis. |
| Conversion rate | Per-query conversion. Where the SERP earns its place. |
| Revenue per search (commerce) | The money number. |
Set up a weekly review:
A/B tests in Algolia tag every search and event with a variant ID. Analytics shows per-variant metrics. To set up:
customSearchParameters.The tests need conversion events to be meaningful. CTR alone is a weaker signal — hits can be clickbait.
Insights events are first-party tracking. The engagement should:
DELETE /1/profiles/{userToken} for Personalization profiles).For high-privacy engagements (legal, healthcare), consider:
# Algolia Insights Taxonomy: [Engagement]
## userToken policy
- Source (auth ID hash, cookie, session)
- Persistence
- Reset triggers
- Privacy disclosure
## Events
| eventName | eventType | Surface | Trigger | objectIDs | queryID | userData |
|-----------|-----------|---------|---------|-----------|---------|----------|
| Articles Viewed | view | SERP | Hit rendered | yes | — | — |
| Article Clicked | click | SERP | Hit clicked | yes | yes | — |
| Article Read | conversion | Detail page | Scroll > 50% | yes | yes (from URL) | — |
| Whitepaper Downloaded | conversion | Detail page | Download click | yes | yes | — |
| ...
## Server-side mirrors
- Which conversions also fire server-side
- Implementation reference
## Privacy compliance
- Disclosure path
- Opt-out behavior
- Deletion path
# Search Analytics Weekly Review — Week of YYYY-MM-DD
## Headline metrics (delta vs. prior week)
- Searches: 12,400 (+8%)
- CTR: 41% (-2pp) ⚠
- Conversion rate: 3.1% (=)
- No-result rate: 4.2% (-0.5pp)
## Top searches
[paste top 20]
## Top no-result searches (action items)
- "summit risk gates" — 42 searches, 0 results
→ Action: add synonym `summit, slalom-summit`
- "delivery quality review" — 28 searches, 0 results
→ Action: confirm content exists; if not, content gap
## A/B tests in flight
- [name] — [status]
## Recommendations to ship
- [synonym, rule, settings change]
userToken mismatch between search and event.userToken (email, account ID).eventName values. Taxonomy entropy.queryID on click events. No attribution to search.userToken policy without privacy review.algolia-instantsearch-react, algolia-autocomplete.algolia-recommend, algolia-personalization-ai.algolia-relevance-tuning.../../references/api-surface.md.algolia-api-keys-security.../../references/algolia-foundations.md../../references/api-surface.md