Experiments API
Create, manage, and query experiments. The active experiments endpoint is public. All others require authentication.
List experiments
/api/experimentsList all experiments for the current workspace
Returns an array of experiment objects. Optionally filter by status.
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status: draft, running, paused, or completed |
Create experiment
/api/experimentsCreate a new experiment in draft status
surface and hypothesis are required. The body is not { name, targetUrl, variants: [{ css }], mode: "ab" }. That shape is rejected.
| Parameter | Type | Description |
|---|---|---|
namerequired | string | Display name |
hypothesisrequired | string | The testable claim |
surfacerequired | string | Where it runs: web, email, push, in_app, mobile, journey_arm, or paid_channel (an Ad holdout). Reserved surfaces (creative, landing_page, feature_flag) are rejected. |
variants | array | Variant objects. Each variant.surface must match the experiment surface. Omit for paid_channel — Apex creates the two lanes (held back / saw the ads) from holdoutPercent. |
paidChannel | object | Required when surface is paid_channel: { network: meta|google|tiktok|linkedin, adAccountId, adSetIds (1–20), holdoutPercent (0.05–0.5), attachMode: apex|manual, washoutDays? }. Admin-only; the workspace must have Ad holdouts switched on, the network connected, and (Meta) Apex's access to Custom Audiences proven on the setup step. Nothing is counted until GET /api/experiments/{id}/ad-exclusion/verify reads the list as attached on every ad set. |
beliefId | string | Belief this experiment tests |
Most organizations create website experiments from Experiments → New Experiment (Apex changes the page, or your code does) rather than this POST.
Update experiment
/api/experimentsUpdate an existing experiment
Send only the fields you want to change. Any other field is refused with a 400 whose code is field_not_editable and whose fields lists what was refused. Nothing is saved when a field is refused. A value of the wrong type or size gets a 400 with code: "invalid_field" naming the field. Results, ids, the workspace and the audit history are never editable here.
The hypothesis, metrics and prediction lock when the experiment starts. Variants, surface and target lock once it has data. The description and minimum sample size lock while it is running. Changing a locked field returns 409 with code: "experiment_locked".
| Parameter | Type | Description |
|---|---|---|
idrequired | string | Experiment ID |
status | string | draft, running, paused, completed, or archived. Starting a code-wired draft with no visits yet holds it at pending_deployment until the first visit. |
force | boolean | With status running: start now instead of waiting for the first visit |
action | string | promote (serve a completed winner to everyone) or dismiss-graduation |
name | string | Display name. Editable anytime. |
hypothesis, prediction, predictionId, beliefId, hypothesisId | various | The science behind the test. Draft only. |
primaryMetric, guardrailMetrics, secondaryMetrics | object / array | What counts as a win and what must not get worse. Draft only. |
evaluationWindow | object | { durationDays?, minPerVariant? }. Merged with the saved window. Draft only. |
attributionWindow | object | How long after a visit a conversion still counts. Draft only. |
variants, surface, targetUrl, targetAnchor, randomizationUnit, allocation | various | Who sees what. Locked once the experiment has data, except allocation weights. |
scheduledStartAt | string | ISO time to start. Send an empty string to clear it. |
tags | string[] | Labels. Editable anytime. |
owner, series | string | Who runs the test, and the series it belongs to. Text, up to 200 characters. Editable anytime. |
description, minSampleSize | string / number | A longer description (plain text, up to 2,000 characters), and the minimum sample size before a result counts (a whole number from 1 to 10,000,000). Locked while the experiment is running. |
dataSourceId | string | A data source in this workspace |
winner, winnerConfidence, completedAt | various | Record the result when you end the experiment yourself |
graduated | boolean | true once the winner is in your code. Needs a winner. Apex records the time. |
deploymentCommit, deploymentStatus, deployedAt, deployedBranch | string | Deploy tracking for code-wired experiments |
Get active experiments (public)
/api/experiments/activeFetch running experiments for a workspace. Used by the snippet.
| Parameter | Type | Description |
|---|---|---|
keyrequired | string | Workspace key (query parameter) |
Info
This endpoint is public. The Apex snippet calls it to apply website experiments on page load.
Experiment object
| Field | Type | Description |
|---|---|---|
id | string | Unique experiment identifier |
name | string | Display name |
hypothesis | string | The testable claim |
surface | string | web, email, push, in_app, mobile, or journey_arm |
status | string | draft, running, paused, or completed |
variants | array | Variant definitions |
beliefId | string | Linked belief, if any |