Home· Features· Search Analytics
📊 Search Analytics

Every query, every click, every AI rescue.

An append-only event table feeds seven dashboard tabs, a live activity feed, a latency histogram with real percentiles, and the AI Coach that turns the data into one-click fixes.

Starter Growth Scale Enterprise
Live activity · last 30 s
"wireless headphones" hybrid 18 ms
"laptop for editing" semantic 62 ms
"trainers" 0 hits
"sneekers" → "sneakers" typo_fix 14 ms
What gets captured

One row per search. No PII, ever.

Every query writes one row to search_events — an append-only table. Skryx never stores IPs, user agents, or user IDs; the data is aggregated at query time, not pre-hashed. Click signal arrives as a nullable update on the same row.

// search_events row
{
  "tenant_id":           42,
  "index_id":            7,
  "query":               "wireless headphones",
  "results_count":       142,
  "clicked_doc_id":      "sku-001",
  "clicked_position":    2,
  "search_mode":         "hybrid",
  "ai_enhanced":         true,
  "ai_interventions":    {
    "typo_fix":     false,
    "query_rewrite":false,
    "synonym_used": true
  },
  "response_time_ms":    18,
  "embed_time_ms":       3,
  "dwell_time_ms":       8200,
  "quality_score":       0.87,
  "occurred_at":         "2026-05-25T17:14:02Z"
}

Twelve columns.
Designed for triage, not surveillance.

The shape is deliberately small: query text + result count + click position + mode + AI intervention flags + latency. Anything beyond that isn't worth storing. Click events are not a separate table — they arrive as a post-fact update on the original search row, so query and outcome stay correlated without a join.

  • Append-only: UPDATED_AT is null on the model — clicks update via explicit UPDATE, not Eloquent magic
  • Indexed: on (tenant, occurred_at), (index, results_count), (tenant, ai_enhanced, occurred_at), (tenant, search_mode)
  • No IP, no UA, no user ID — aggregation happens at SQL GROUP BY time
The dashboard

Seven tabs. Each one tells a different story.

📊 Overview

The four numbers your boss asks for.

Total searches, zero-result count, average latency, AI-enhanced count. Plus a volume timeseries chart, granularity auto-picked: hourly for ranges ≤ 24h, daily for longer. No pre-computed rollups — Skryx aggregates on demand, so the data is always current.

  • Range presets: 24h / 7d / 30d / 90d
  • JSON API endpoint: /api/dashboard/timeseries
// Last 24h
Searches: 412,118
Zero-result: 3,202 (0.78%)
Avg latency: 18 ms
AI-enhanced: 31,640 (7.7%)
⚡ Performance

Real p50 / p75 / p95 / p99.
Histogram with 10ms buckets.

Latency percentiles computed from the actual response_time_ms column at query time — no sampling, no approximation. Histogram covers 0–500 ms in 10 ms buckets plus a tail bucket for everything above 500 ms. Slowest queries listed separately with their query text for inspection.

  • p50, p75, p95, p99 + mean latency
  • 50-bucket histogram (0–500 ms) + tail_500ms_plus count
  • Top 20 slowest queries with average latency and last-seen timestamp
// /api/analytics/latency-histogram
p50: 11 ms
p75: 22 ms
p95: 38 ms
p99: 48 ms
tail (>500ms): 14
🔍 Queries

Sortable, paginated, drillable.

Paginated table (25/page) of unique queries: volume, average results, average latency, CTR clicks, last seen. Sort by any column. Click into any query to see the result set Skryx returned during the period. Plus a day-of-week × hour heatmap (7 × 24) so you can spot traffic patterns at a glance.

  • Sort: count / latency / zero-results / recent
  • Heatmap: weekly traffic shape, 168 cells
  • JSON: /api/analytics/queries, /api/analytics/heatmap
Query · Vol · Hits · CTR · Avg ms
sneakers 1,420 142 42% 17
headphones 982 214 38% 21
smartwatch 671 88 21% 19
laptop bag 418 62 19% 16
desk lamp 302 74 44% 14
🤖 AI Impact

Skryx AI's contribution, quantified.

A dedicated tab counts every AI intervention by family: typo_fix, synonym_used, query_rewrite, semantic_dispatched, hybrid_dispatched. The headline metric — queries rescued from zero results — counts every search where keyword would have returned nothing but the AI pipeline produced hits.

  • Per-intervention breakdown (5 families)
  • AI-rescued zero-results count
  • JSON: /api/analytics/ai
// AI interventions · last 7d
typo_fix: 1,204
synonym_used: 8,941
query_rewrite: 312
semantic_dispatched: 4,108
hybrid_dispatched: 22,317
─────────────────
Rescued from 0 hits: 847
🕳️ Zero Results

The queries you're losing customers on.

Volume-sorted list of zero-result queries with AI-rescued count split out. This is the table AI Coach reads to suggest synonyms and catalog gaps — so every recommendation has a direct link back to the underlying queries.

  • Zero-result rate (%) + count
  • AI-rescued count (how many became non-zero via Skryx AI)
  • One-click jump to "Add synonym" with the query pre-filled
Zero-result · last 7 days
"trainers" 24
"smartwatch bands" 11
"4k webcam" 8
"vegan leather" 6
─────────────────
AI rescued 312 of 3,202 (9.7%)
🎨 Visual Curator metrics

Curated rules + their impressions / CTR.

Every Visual Curator rule (pin / replace / hide) records impressions, clicks, and which trigger fired. A separate tab in the analytics surface shows per-rule CTR plus last-fired timestamps so editorial decisions stay accountable to traffic data.

  • Per-rule: impressions, clicks, CTR, last fired, active flag
  • Built-in A/B comparison for rule variants
  • Same data via the per-rule analytics API endpoint
// Pin rule · "sneakers"
impressions: 1,420
clicks: 682
CTR: 48%
last fired: 2 min ago
status: active
Search → Purchase attribution

Know the euro figure search earned you. Last week, this month, last year.

First-search-wins attribution per session — every order tied back to the FIRST search that started the buyer's journey. Funnel widget breaks down searches → cart adds → purchases. Revenue attributed in your display currency. Compare to previous period. Drill down to which queries earned the most.

💰 Funnel + revenue per search

What customers did after they searched.

ConversionAttributionService watches order.completed events, walks back through the session events strictly before the order, finds the FIRST search.performed, and credits that query with the revenue. Cart adds get the same treatment via cart.add.

  • Search-attributed revenue per period with vs-previous delta
  • Conversion rate, AOV, revenue per search — at a glance
  • Top queries by revenue, top products by search-driven sales
  • Daily series chart, per-tenant currency conversion at write time
  • Inflation-safe: an order with no preceding search is NOT attributed
GET /api/analytics/conversion?period=30d

{ "funnel": {
    "searches":               583,707,
    "cart_adds":              10,716,
    "purchases":               3,491,
    "search_to_purchase_pct":    0.60 },
  "revenue": {
    "search_attributed": 1062035.01,
    "vs_previous_pct":       +14.3,
    "search_share_pct":       60.8,
    "aov_attributed":        304.21 },
  "top_queries": […],
  "top_products": […] }
📤 CSV export · Skryx engine timings

Per-stage latency. Streamed CSV. Weekly email digest.

Every search response now carries a _meta.timings block with per-stage millis: ai_understanding, keyword, vector, merge, rerank. Lets you see exactly WHERE the time went on slow queries.

CSV exports stream chunked from every analytics endpoint — queries, conversions, zero-results — without OOM on large windows. UTF-8 BOM so Excel opens accents cleanly. Pro+ plans get a weekly digest mail with the Search → Purchase summary.

  • GET /api/analytics/export.csv?type=conversions&period=30d
  • Per-stage timing in every search response under _meta.timings
  • Weekly Search → Purchase digest email · Pro+
  • Plan-aware retention clamp — your window matches what you pay for
// Per-search timing breakdown
{ "_meta": {
    "timings": {
      "ai_understanding": 12,
      "keyword":           8,
      "vector":           24,
      "merge":             3,
      "rerank":            6
    },
    "corrected_query": "alternator",
    "used_synonyms": [
      {"matched":"alternator",
       "expanded_to":["dinam","generator auto"]}
    ]
  }
}
Detection details

Two clever bits worth calling out.

📏 Levenshtein-based typo detection

Distinguishes typos from rewrites.

If the engine's final query differs from the original, Skryx checks Levenshtein distance. ≤ 2 edits and not already marked as an LLM rewrite → flagged as typo_fix with from/to recorded. Anything farther afield is a query_rewrite. That separation makes the AI Impact tab honest about what's helping.

{
  "original_query": "sneekers",
  "final_query":    "sneakers",
  "levenshtein":    1,
  "ai_interventions": {
    "typo_fix": {
      "from": "sneekers",
      "to":   "sneakers"
    }
  }
}
📤 JSON-first APIs

Every chart is a queryable endpoint.

Five endpoints cover the dashboard: timeseries, latency histogram, queries, heatmap, AI breakdown. All return JSON, all accept a period parameter. Pipe them into Metabase, Looker, or a homegrown report — Skryx doesn't try to be your BI tool.

  • /api/dashboard/timeseries
  • /api/analytics/latency-histogram
  • /api/analytics/queries (paginated)
  • /api/analytics/heatmap (7 × 24)
  • /api/analytics/ai
GET /api/analytics/queries
    ?period=7d&page=1&sort=count

{ "rows": [
    { "q": "sneakers",
      "vol": 1420,
      "hits": 142,
      "ctr": 0.42,
      "avg_ms": 17,
      "last_seen": "2026-05-25T17:12Z" },
    …
  ],
  "page": 1,
  "per_page": 25 }
Privacy & data residency

EU-hosted. No PII. No third-party trackers.

Storage in Frankfurt. No IP, user-agent, or user-id captured. Click data arrives as a nullable update on the existing row, not a separate event broadcast. Aggregation happens at SQL GROUP BY time, so individual searches never appear in dashboard counters.

7 tabs
Overview, Performance, Queries, AI Impact, Zero Results, User Journeys, Curator
5 APIs
JSON endpoints under /api/analytics/* for every dashboard view
All plans
Analytics itself is unlimited; AI Coach (which reads it) is Growth+
Keep exploring

Other things Skryx does

Try it on your own catalog.

Free tier, no credit card. EU-hosted from day one.