ZIPPY DOCS

A/B split testing

Race 2–4 destinations against each other on one Zippy link — and see which variant actually OPENED THE NATIVE APP, not just which one got clicked.

A/B split testing

An A/B split test turns one short link into 2–4 weighted destinations. Zippy hands each visitor one of them, remembers which one they got, and then reports the number no other link tool can give you: which variant actually opened the native app.

A landing page can win on taps and still lose on app opens. That's the whole reason this exists.

Split testing is available on Hero and up, behind the ab-testing feature flag.

How the split works

Each variant is a destination (u) and a relative weight (w). Weights are relative, so 50/50 and 1/1 are the same split — the editor shows the resolved percentage as you type.

VariantDestinationWeightShare of taps
Ahttps://shop.com/drop150%
Bhttps://instagram.com/p/abc123150%

Minimum 2 variants (one variant isn't a test), maximum 4 — a set that doesn't survive those bounds is treated as no test at all rather than silently truncated at redirect time.

The number nobody else shows you

Every other link tool tells you which variant got more clicks. Zippy stamps the variant index on the click, and the outcome pipeline records whether that tap ended in the real native app, a browser, or a broken hand-off. So you get the app-open rate per variant.

GET /api/links/:id/ab-stats

{
  "sampled": true,
  "window": 90,
  "configured": true,
  "variants": [
    {
      "url": "https://shop.com/drop",
      "weight": 1,
      "variant": 0,
      "clicks": 1840,
      "outcomes": 412,
      "opened": 119,
      "appOpenRate": 0.288
    },
    {
      "url": "https://instagram.com/p/abc123",
      "weight": 1,
      "variant": 1,
      "clicks": 1612,
      "outcomes": 388,
      "opened": 291,
      "appOpenRate": 0.75
    }
  ]
}

That's variant A winning on clicks and losing badly on the thing you actually wanted.

sampled: true is load-bearing. clicks comes from the redirects dataset and appOpenRate is computed inside the outcomes dataset — the two are sampled independently. Read it as a trend, never as an exact per-click ratio. A variant with no traffic yet still appears, as a row of zeroes, so nothing silently vanishes from the report.

The report covers a rolling 90-day window and is Hero and up (the same tier as the feature). On a lower plan the endpoint answers with an upgrade note instead of variant rows.

Set it up

Dashboard (web): in the ZIP form (or a link's editor), switch the destination mode to split test, then add 2–4 rows of destination + weight. A live split bar shows the percentage each variant will get.

Mobile app: the same fields live on a link's detail screen.

Via the API

POST /api/links
Content-Type: application/json
 
{
  "url": "https://shop.com/drop",
  "slug": "summer-drop",
  "ab": [
    { "u": "https://shop.com/drop", "w": 1 },
    { "u": "https://instagram.com/p/abc123", "w": 1 }
  ]
}
{
  "id": "0f1c…",
  "slug": "summer-drop",
  "destination": "https://shop.com/drop",
  "routing": null,
  "ab": [
    { "u": "https://shop.com/drop", "w": 1 },
    { "u": "https://instagram.com/p/abc123", "w": 1 }
  ],
  "sched": null,
  "createdAt": "2026-07-19T14:05:40.301Z",
  "updatedAt": "2026-07-19T14:05:40.301Z"
}

Change the split later with PATCH /api/links/:id — send a new ab array to replace it, or null to end the test. The short link never changes either way.

PATCH /api/links/0f1c…
Content-Type: application/json
 
{ "ab": null }

Every variant must be a valid http(s) URL:

{ "error": "Every A/B variant must be a valid http(s) URL" }

Split test OR route — never both

A link can split-test or route by audience, not both. Routing sends a visitor somewhere based on who they are; a split test sends them somewhere at random so you can compare. Doing both at once would make the results meaningless, so Zippy rejects the combination instead of silently ignoring one:

{
  "error": "ab_routing_conflict",
  "message": "A link can split-test OR route by audience, not both. Clear one before setting the other."
}

The check runs on the effective values, so setting ab on a link that already routes is the conflict — not just sending both fields in one request. Clear one, set the other; both switches are instant and the link keeps redirecting throughout.

UTMs ride every variant

If the link also has UTM tracking, Zippy composes your utm_* parameters onto every variant automatically — the same walker that handles routed and scheduled destinations. Your stored variant URLs stay clean and editable; the composed ones are what the engine serves. No variant can lose its campaign tags.

A split test only decides which URL a tap resolves to. It cannot stop the link redirecting, cap its clicks, or 404 anyone — not when the test ends, not when you clear it, not ever. End a test and the link falls back to its default destination. That's the permanence guarantee, and A/B is built inside it, not around it.

Notes

  • Hero and up, behind the ab-testing flag.
  • 2–4 variants; weights must be positive numbers.
  • Variants are http(s); each one still springs the native app where one exists, in-app browser or not.
  • Per-variant results are a sampled trend over a 90-day window.

FAQ

Don't make two links — make one link with two destinations. Open the link's editor, switch the destination mode to split test, and add both URLs with a weight each (1 and 1 for an even split). Share the single short link. Zippy hands each visitor one of the two destinations and reports how each one performed, including the app-open rate per variant.

Which variant is winning — clicks or app opens?

Both are in the report, and they often disagree. clicks is how many taps a variant got; appOpenRate is the share of those taps that ended up inside the real native app. A variant can take more clicks and still lose, because half its visitors got stranded in a webview. The app-open rate is the number to optimise.

No. Zippy returns an ab_routing_conflict error instead of letting both run. Random assignment and audience-based assignment can't share one link without ruining the results. Clear one before setting the other — switching is instant and the link never goes down.

Nothing bad, ever. Clear the test ("ab": null) and the link goes back to serving its default destination. The short URL, the QR code you printed, and every caption you already posted keep working. A split test can't expire a link.

How many destinations can I test at once?

Between 2 and 4. One isn't a test; more than four is a cap the redirect engine enforces, so Zippy refuses the set rather than quietly dropping variants.

Read more