---
title: "Real-time search personalization | Snowplow Signals"
description: "Your ranker sees the query string and a profile from last night. Signals hands it what this visitor has been searching and skipping, inside the query budget."
source: https://signals.snowplow.io/lp/search-personalization
---

Real-time search personalization

# Rank on the last three searches, not the last three months.

Signals computes what this visitor has been searching, viewing and skipping, and serves it to your ranker in 6ms, inside the budget the query already has.

* **6ms** p50 read, in-region
* **<1s** event to attribute current
* **Free** tier, no card

## Start free

5M events a month · unlimited attributes · no card 

Work email Continue

A work address, please. We cannot provision on a personal domain.

Or [talk to an engineer](https://signals.snowplow.io/start?intent=engineer&lp=search-personalization).

[ Try it on yourself ↓ ](#live-panel) 

Below is your own visit to this page, computed by Signals as you read.

### Signals, about you 

connecting 

See live events firing as you browse this page on the left; what Signals computed about you on the right, refreshed every few seconds.

Implement this

this page · events 0 writes 

Product Solutions Developers Pricing Start free 

Start free Engineer 

this panel

the rest of the page

nothing sent yet

write 

attribute store · your session no read yet 

reading\_now 

pages\_last\_5\_min 

seconds\_since\_last\_action 

sections\_read 

seconds\_engaged 

pricing\_views 

menus\_explored 

features\_wanted 

cta\_clicks 

arrived\_from 

waiting for the first computed value… 

Your first events are in flight through a real Snowplow pipeline. Attributes appear as the stream computes them, usually within a few seconds.

hand it to your agent one prompt 

Add the Signals MCP server to Claude Code, Cursor or any MCP client, then run this. The agent adds the SDK, defines the attribute and wires the read into your own app.

$ npx plugins add snowplow/skills

> Use the Snowplow MCP server and Signals skill, then help me plan and implement a use case. Make sure everything is tested and works as intended.

Copy the prompt[How to connect the MCP server](https://docs.snowplow.io/docs/llms-support/snowplow-mcp/) 

or 

write it yourself three steps 

01 Track the event once, in your app 

trackPageView(); // browser tracker, already on this page

02 Define the attribute once, in Signals 

Attribute(name="pages_last_5_min", events=["page_view"],
  aggregation="category_count", property="page_urlpath",
  period=timedelta(minutes=5))

03 Read it back every request 

await signals.getServiceAttributes({
  name: "signals_site_session_service",
  attribute_key: "domain_sessionid",
  identifier: "…", // your session id, once set
});

Why the second search is no better

## Your ranker sees the words they typed. Not the two searches before it. 

Before 

### The query string and a nightly profile

Two failed searches a minute ago, the category they keep circling, the result they opened and bounced straight back from. None of it reaches the ranker in time to matter.

`q = waterproof jacket` 

With Signals 

### Session context at query time

One call returns what this visitor has been doing in the last few minutes, current to the event, fast enough to sit inside a query that already has a latency budget.

`failed_searches_session = 2` 

Result 

### The second search is better than the first

The ranker boosts the category they have been circling and demotes what they already rejected, while they are still typing.

`same session, better results` 

What the ranker can do with it

## Signals that only exist while the visitor is still searching. 

A search session is short. Anything computed overnight arrives after the visitor has either found it or left.

### Recover a failed search before they leave

Two queries with no results is the clearest intent signal you get, and it is worthless tomorrow. Rank the third query differently, or swap the zero-result page for something they might actually want.

`null result rate · search exit rate` 

### Rank on what they skipped

The results they scrolled past say as much as the ones they opened. Both are in the stream already, and neither survives a nightly aggregate.

`search click-through · position of first click` 

### Give an anonymous visitor a profile

Most searchers are not logged in. Keying on the session rather than the person means the ranker has context from the second query onward, with no identity to resolve first.

`coverage of ranked sessions` 

### One context for search, browse and the recommender

The same definitions feed the search ranker, the category pages and whatever recommends alongside them, so the three surfaces stop disagreeing about what the visitor wants.

`surfaces per definition` 

How it works

## One call, inside the query you already run. 

Signals does the knowing. Your search engine keeps doing the ranking, with inputs that are seconds old instead of a day old.

1. 01 **Collect.** Add a Snowplow SDK to your application and search, view and click events start flowing in.
2. 02 **Define.** Say what the ranker should know about the session, in the console or in code.
3. 03 **Serve.** An event updates its attributes in <1s, and your ranker reads them at 6ms p50, on the way to the query.

Signals → your app 6ms p50 

failed\_searches\_session 2 

query\_refinements 4 

categories\_viewed\_10m 3 

results\_clicked\_session 0 

seconds\_since\_last\_query 12 

Trigger search\_abandonment delivered to your app 

Questions

## Before you sign up

### Does this replace my search engine?

No. Signals does the knowing. Elasticsearch, OpenSearch, Vespa, Algolia or the ranker you wrote yourself keeps doing the ranking, with inputs that are seconds old instead of a day old.

### Will it fit inside my query budget?

6ms p50 and 10ms p95 for an in-region round trip, and one call returns a whole service rather than one attribute at a time. Whether that fits is your call, but it is a number with a boundary on it rather than a claim.

### What about visitors who are not logged in?

Attributes can be keyed on the session rather than the person, so an anonymous searcher has context from their second query onward. Pair it with identity resolution later if you want the history to follow them.

### Do I need an event stream already?

No. Collection is part of Signals. Add a Snowplow SDK to your web, mobile or server application and events start flowing in. If you already run Snowplow, Signals reads the pipeline you have.

### What does the free tier include?

The full product against your own traffic, up to 5 million events a month, with no card and no sales call. You should see an attribute updating against your own traffic in the first session.

## Search your own site and watch the context change.

Add the SDK, define one attribute, then run two searches on your own product and watch the second one arrive with the first one attached.

[Start free](https://signals.snowplow.io/start?lp=search-personalization) [Talk to an engineer](https://signals.snowplow.io/start?intent=engineer&lp=search-personalization) 

The real engine · no card · no sales call 

Or hand it to your coding agent 

$ `npx plugins add snowplow/skills` Copy Copied 

Then: Let's add Snowplow Signals to this app. Understand the search flow first, then define an attribute for failed searches this session and read it back before the query runs.
