Docs
De docs doorbladeren

Cookieless on-site surveys

Ask your visitors a question in a small card on your own site, with no cookie, no device storage and no visitor id. NPS, ratings, choices and free text.

Als Markdown bekijken

A survey is one question or a few, shown in a small card on your own site and answered without leaving the page. Create and read them in Surveys in a site's sidebar.

What makes it cookieless:

  • The card sets no cookie and writes nothing to localStorage, sessionStorage, IndexedDB or anywhere else on the device.
  • There is no visitor id, in the browser or in the stored answer. A response holds the page, the device type and the country, and nothing else.
  • A survey can never ask for an email address or a name. There is no question type for one. The six answer types are open text, one choice, several choices, a rating, an NPS score, and yes or no.
  • Anything typed into an open-text box goes through your site's PII scrubber before it is stored, so an email address someone types into a comment box is redacted first.
  • Targeting is decided on our servers, so your rules are not readable from your page source, and the card is only ever sent the survey it should show.

Asked at most once a day, with nothing stored#

Each visitor is asked at most once a day, and at most one answer a day is kept, without any identifier.

When the card is shown, dismissed or answered, we set a marker in our in-memory store (Redis) under the daily visit key, HMAC(daily salt, site, IP, User-Agent), the same key that counts visitors. The key is computed while the request is handled and never stored, the salt it is made from lives only in Redis and rotates every day, and the marker expires at the end of the UTC day. Nothing links today's marker to yesterday's, and nothing links either to a person.

A survey that was offered but never shown (the visitor did not scroll far enough, or left from another page) sets no marker, so they can still be asked on the next page.

Create a survey#

The empty page offers five templates, each a complete survey you can then change: Exit survey, NPS, Satisfaction (CSAT), Was this page helpful? and Pricing page.

Option What it does
Name For your own list, up to 120 characters. Visitors never see it
Status Draft collects nothing, Collecting offers the card, Paused keeps the answers and stops offering it. The results page has Start collecting and Pause buttons too
Questions Up to 5, one per screen, each with a prompt of up to 200 characters. Leave a question's prompt empty to drop it
An answer is needed before moving on Makes the question required: Next and Send do nothing until it is answered. The visitor can still close the card
When it appears One of the five triggers below
Where it appears Path rules, devices and the share of visitors below
Position Bottom right, bottom left or bottom center, 24 px from the edge
Thank-you message Up to 200 characters, shown after the last answer for 3 seconds, then the card closes itself. The default is "Thank you."
Starts and Ends Inclusive dates in UTC. Outside them the survey is not offered, even while it is Collecting

Rewording a question keeps the answers it already has: each question has an id of its own, so a breakdown is never split by an edit.

Your prompts, choices and thank-you message appear exactly as you wrote them, so write them in your visitors' language. The card's own words (Next, Send, Yes, No and the default thank-you) are always English, whatever language you use in the app.

The six answer types#

Answer What the visitor sees What is stored
Open text A text box The text, up to 2,000 characters, PII scrubbed
One choice One button per choice. Picking one moves on The choice's label
Several choices The same buttons, which toggle, then Next One row per picked choice
Rating Buttons from 1 to 3, 5, 7 or 10 (your scale) The number
NPS (0 to 10) Buttons from 0 to 10 The number
Yes or no Two buttons yes or no

A choice question needs 2 to 10 choices, one per line in the form, each up to 100 characters. An answer that was not offered, a rating outside the scale or a question that is not on the survey is dropped on the way in.

The five triggers#

When it appears Exactly when
As soon as the page loads As soon as our answer arrives
After a few seconds 0 to 600 seconds after our answer arrives
After scrolling down At 1% to 100% of the scrollable height. The depth is checked once right away, so a page with nothing to scroll counts as 100%
When someone is about to leave With a mouse, the pointer leaving through the top of the window. A touch screen gives no such signal, so there it is a scroll heuristic: after reaching at least halfway down, scrolling back up into the top 15%
When you ask for it Waits for a privatus.ask() call from your own code

Where it appears#

Path rules decide which pages ask. Each rule is a match type and a pattern:

Match What it matches
Path is The path exactly, for example /pricing
Path starts with Every path under it, so /docs covers /docs/install
Path matches A glob, where * stands for any part of a path, including slashes: /blog/*/comments

Leave the list empty to ask on every page. Rules are an "or": one match is enough. Patterns are compared without case, against the path after your path masks are applied, and the page's hostname has to be one of the site's domains, or a subdomain of one.

Option What it does
Devices Desktop, Phone, Tablet. Leave all unchecked for every device. The device type is read from the User-Agent in memory and then dropped
Share of visitors 1 to 100 percent. The share is rolled per request, so 20% is a sample of traffic and not a group of people followed around the site

Only one card is ever shown at a time. When two surveys match the same page, the oldest one wins.

Turn on the ask module#

The card only appears on pages whose tracking code loads the ask module:

html
<script defer src="https://privatusanalytics.com/js/pa.js" data-site="pa_YOURSITEID" data-modules="engage,auto,vitals,clicks,ask"></script>

The snippet in Site settings → Tracking adds ask for you as soon as the site has a survey that is Collecting, so copy the snippet again after you start your first one. The module asks us once per page load (not again on route changes in a single-page app), and a card appears at most once a day per visitor.

Leaving ask out of data-modules is how you turn surveys off at the page level: without it, nothing is loaded and nothing is requested. With it, the one request per page load is answered from Redis with no database query, it is a few hundred bytes, and it loads with defer like the tracker itself, so it never holds up your page. See what the request costs.

Read the results#

Open a survey for its report. The period picker has 7 days, 28 days, 90 days and All, and the report opens on the last 28 days until you pick one. The header counts responses and how many were finished: someone who answered the first question and then closed the card counts as a response, not as finished. A card closed with nothing answered stores nothing at all.

Each question gets a card with its answer type, its number of answers and:

Answer type What the report shows
One choice, Several choices Every offered choice with its count and its share of the answers, largest first
Yes or no Yes and no, with counts and shares
Rating The average out of the scale, to two decimals, and a bar per value
NPS The score, the counts of promoters (9 and 10), passives (7 and 8) and detractors (0 to 6), and a bar for each value from 0 to 10. The score is the share of promoters minus the share of detractors, rounded to a whole number
Open text The 100 most recent answers, newest first, each with its date

Below the questions, Pages, Devices and Countries list the top 10 of each by number of responses.

Themes with AI (Business)#

On the Business plan an open-text question has a Find themes with AI button once it has at least 5 written answers. It groups the answers into up to 12 themes, each with a short label in the words of the answers, a count, a sentiment (positive, neutral or negative) and one example answer copied from the set.

  • It reads the 300 most recent answers to that question, each cut to 300 characters.
  • It uses Claude Haiku 4.5. The answers are given to the model as data to group, never as instructions to follow.
  • Each generation costs one AI question from your workspace's monthly AI allowance, the same meter Ask uses (100 a month on Business). A generation that fails gives the question back.
  • The result is cached against the answers it was made from, so opening the page again, or ten of your team opening it, costs nothing. New answers mean the next run is a new summary.

Plans and limits#

Free Pro Business
Surveys in the workspace 1 Unlimited Unlimited
Survey responses a month 10 Unlimited Unlimited
Themes with AI No No Yes

Both limits count across the whole workspace, not per site. At the monthly response limit the survey stops being offered, so nobody is shown a card whose answer we would throw away, and nothing is stored past the limit (the allowance is checked again when the answer is written). The count resets on the 1st of the month.

Survey responses are not events. They never count toward your event allowance, and neither do the module's own two requests, or API and MCP calls.

Privacy signals and opt-out#

  • Do Not Track and Global Privacy Control are honored according to your site's settings (Site settings → Privacy, where GPC is on by default). When you honor a signal and the browser sends it, no survey is offered and no answer is stored. See DNT and GPC.
  • A visitor who has opted out is not asked. The module stops before it asks us, and again before it shows a card.
  • A page your tracking code excludes with data-exclude is not asked, and while a workspace is over its monthly event limit and collection is paused, no survey is offered.

What is stored#

One response row per submission:

Field What it holds
Survey, site, workspace Which survey the answer belongs to
Finished Whether the last question was answered, or the card was closed part way
Path The page it was answered on, with your path masks applied and the query string already removed, up to 2,048 characters
Device desktop, mobile or tablet, up to 16 characters
Country The two-letter code from Cloudflare's CF-IPCountry header. No region or city
Time When it arrived

One answer row per answered question (one per picked choice for a several-choices question):

Field What it holds
Response, survey Which submission it belongs to
Question The question's id, so a reworded prompt keeps its answers
Text value A choice label, yes or no, or the scrubbed free text
Number value A rating or an NPS score
Time When it arrived

That is the whole record. There is no visitor id, no IP address, no raw User-Agent, no referrer, no screen size and no name or email address. See the data inventory for everything else we store.

API and MCP#

Surveys are REST resources and the MCP tools surveys_list, surveys_get, surveys_create, surveys_update, surveys_delete, surveys_start, surveys_pause and surveys_themes. surveys_get returns the report for a period, and surveys_themes generates the themes for one question (Business). The API reference lists every input.

sh
curl -X POST https://privatusanalytics.com/sites/pa_7Q2K9XH3AB/surveys.json \
  -H "Authorization: Bearer $PRIVATUS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Pricing page", "status": "active",
       "trigger_kind": "delay", "delay_seconds": 15,
       "questions": [{"kind": "choice", "required": true,
                      "prompt": "Is anything unclear about our pricing?",
                      "choices": ["I cannot tell which plan I need",
                                  "It costs more than I expected",
                                  "No, it is clear"]}],
       "paths": [{"match_type": "prefix", "pattern": "/pricing"}]}'

A survey created over the API is a Draft unless you send "status": "active".

Ask from your own code#

Give a survey the When you ask for it trigger and show it yourself, after a failed search or a canceled signup:

js
privatus.ask('svy_ABC123')

It shows the survey we offered for this page, and returns true when there was one to show. It returns false when nothing was offered (no survey matched, the visitor was asked already today, or they have opted out) and when the id you pass is not the one offered, so your own code can never show a survey that targeting ruled out. Call it with no argument to show whatever was offered. A card that has already been shown and closed on this page is not shown again.

The offer is fetched when the page loads, so a call in the first instants of a page load can return false before the answer arrives.