Running a Server Side Experiment with Pertento
Learn how to run an experiment where your own code assigns variants and renders changes, reporting exposures and conversions to Pertento over HTTP.
3 min read
A Server Side experiment inverts the usual arrangement. Your application decides which variant each visitor gets and renders the change itself; Pertento is used for configuration, measurement and analysis. This suits changes that cannot be made in the browser — pricing logic, search ranking, an API response, a recommendation algorithm — and anything where a client-side change would flicker.
You report two things over HTTP: who was exposed to which variant, and what they subsequently did. Everything on the Statistics page is computed from those two streams.
Setting the experiment up
- 1Create an experiment of type Server Side, with one named variant per branch your code can take.
- 2Note each variant’s numeric id. This is the id you will send, and it is the same id you use in both API calls.
- 3Configure the traffic weights to match what your own allocator actually does. Pertento is not splitting the traffic, but it does check the split you report against the weights you configured, and a mismatch will be reported as a fault.
- 4Start the experiment. This matters more than it looks — see the warning below.
Reporting exposures
When a visitor actually reaches the code being tested, record it:
POST https://tracking.pertento.ai/visitors?websiteId=<websiteId>&exp-<experimentId>=<variantId>
No body and no authentication are required, so this is a straightforward server-to-server call. One request can carry several experiments by repeating the parameter: &exp-8=204&exp-9=311.
Send this at the point of exposure, not on every request. If the tested function runs three times for one visitor, that is one exposure, not three.
Reporting conversions
When the visitor converts, post the event with the same variant mapping:
POST https://tracking.pertento.ai/events?websiteId=<websiteId>
The body carries the events and the map of experiments the visitor is enrolled in:
{ "dataPayload": [["purchase", { "revenue": "49.99", "currency": "EUR" }]], "experimentVariantMap": { "7": "102" } }
The two calls must agree. A conversion attributed to a variant whose exposure was never reported produces a conversion rate above 100%; an exposure without its conversions produces a variant that looks like it never works.
Start the experiment before you send anything
This is the one mistake worth calling out on its own. Pertento creates the per-variant counters when an experiment is started, and exposure aggregation increments those existing counters rather than creating them on demand.
Exposures posted for a Draft experiment are accepted, return a success response, and never appear in the running totals. Start the experiment first, then begin sending.
Deduplication is your responsibility
The tracking endpoints hold no idempotency key and no visitor identity. Nothing on the server can tell a repeated exposure from a genuine one.
Pertento’s browser runtime counts a visitor once per experiment, permanently, using a persistent flag in local storage rather than a per-session one. Match that rule: your deduplication key should be a durable visitor identifier plus the experiment id, not your HTTP session.
Getting this wrong is not a rounding error. Deduplicating per session instead of per visitor counts returning visitors repeatedly, inflating the denominator and pushing every conversion rate down — and those same figures drive significance detection and the automatic weight correction.
If the Pertento script is also on the page
Make sure it is not registering visitors for the same experiment. If both the browser runtime and your server report exposures, every visitor is counted twice and the conversion rate halves.
Notes
- Exposures are stamped when they arrive. There is no backdating parameter, so batching or queueing them distorts the hourly series the charts are drawn from. Send them as they happen.
- A success response means the request was accepted, not that the row was stored. Writes are fire-and-forget and failures are logged server-side only. Verify in the dashboard, allowing about a minute for aggregation.
- Configure the weights to match your allocator. The traffic split check tests what you report against what is configured, and a 50/50 allocator on a 90/10 configuration raises a fault on a perfectly healthy experiment.