Experiments API
Create, manage, and query experiments. The active experiments endpoint is public. All others require authentication.
List experiments
GET
/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 |
curl https://app.apex.inc/api/experiments?status=running \
-H "x-api-key: apex_sk_..."
Create experiment
POST
/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 |
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 shops create website experiments from Experiments → New Experiment (Apex changes the page, or your code does) rather than this POST.
Update experiment
PATCH
/api/experimentsUpdate an existing experiment
| Parameter | Type | Description |
|---|---|---|
idrequired | string | Experiment ID |
status | string | draft, running, paused, or completed |
name | string | Updated display name |
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/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 |