Experiments API

Create, manage, and query experiments. The active experiments endpoint is public. All others require authentication.

List experiments

GET/api/experiments

List all experiments for the current workspace

Returns an array of experiment objects. Optionally filter by status.

ParameterTypeDescription
statusstringFilter by status: draft, running, paused, or completed
curl https://app.apex.inc/api/experiments?status=running \ -H "x-api-key: apex_sk_..."

Create experiment

POST/api/experiments

Create 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.

ParameterTypeDescription
namerequiredstringDisplay name
hypothesisrequiredstringThe testable claim
surfacerequiredstringWhere 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.
variantsarrayVariant 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.
paidChannelobjectRequired 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.
beliefIdstringBelief this experiment tests
curl -X POST https://app.apex.inc/api/experiments \ -H "Content-Type: application/json" \ -H "x-api-key: apex_sk_..." \ -d '{ "name": "Pricing CTA", "hypothesis": "A clearer CTA lifts signup", "surface": "web", "variants": [ { "key": "control", "label": "Control", "surface": "web" }, { "key": "variant_b", "label": "New CTA", "surface": "web" } ] }'

Most organizations create website experiments from Experiments → New Experiment (Apex changes the page, or your code does) rather than this POST.

Update experiment

PATCH/api/experiments

Update 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".

ParameterTypeDescription
idrequiredstringExperiment ID
statusstringdraft, running, paused, completed, or archived. Starting a code-wired draft with no visits yet holds it at pending_deployment until the first visit.
forcebooleanWith status running: start now instead of waiting for the first visit
actionstringpromote (serve a completed winner to everyone) or dismiss-graduation
namestringDisplay name. Editable anytime.
hypothesis, prediction, predictionId, beliefId, hypothesisIdvariousThe science behind the test. Draft only.
primaryMetric, guardrailMetrics, secondaryMetricsobject / arrayWhat counts as a win and what must not get worse. Draft only.
evaluationWindowobject{ durationDays?, minPerVariant? }. Merged with the saved window. Draft only.
attributionWindowobjectHow long after a visit a conversion still counts. Draft only.
variants, surface, targetUrl, targetAnchor, randomizationUnit, allocationvariousWho sees what. Locked once the experiment has data, except allocation weights.
scheduledStartAtstringISO time to start. Send an empty string to clear it.
tagsstring[]Labels. Editable anytime.
owner, seriesstringWho runs the test, and the series it belongs to. Text, up to 200 characters. Editable anytime.
description, minSampleSizestring / numberA 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.
dataSourceIdstringA data source in this workspace
winner, winnerConfidence, completedAtvariousRecord the result when you end the experiment yourself
graduatedbooleantrue once the winner is in your code. Needs a winner. Apex records the time.
deploymentCommit, deploymentStatus, deployedAt, deployedBranchstringDeploy tracking for code-wired experiments
curl -X PATCH https://app.apex.inc/api/experiments \ -H "Content-Type: application/json" \ -H "x-api-key: apex_sk_..." \ -d '{ "id": "exp_abc123", "status": "running" }'

Get active experiments (public)

GET/api/experiments/active

Fetch running experiments for a workspace. Used by the snippet.

ParameterTypeDescription
keyrequiredstringWorkspace key (query parameter)

Info

This endpoint is public. The Apex snippet calls it to apply website experiments on page load.

Experiment object

FieldTypeDescription
idstringUnique experiment identifier
namestringDisplay name
hypothesisstringThe testable claim
surfacestringweb, email, push, in_app, mobile, or journey_arm
statusstringdraft, running, paused, or completed
variantsarrayVariant definitions
beliefIdstringLinked belief, if any