Reference

REST API reference

Authentication, every endpoint, request and response shapes, errors and examples.

All endpoints live under the base URL shown on the experiment Setup tab, written here as https://YOUR-HOST/api/.... Requests and responses are JSON. Errors always have the shape { "error": "message" }.

Authentication#

Used forHow to authenticate
SDK endpoints (/api/assign, /api/track)An SDK key in the X-API-Key header (or a ?key= query parameter). Secret keys are rejected here.
Management endpoints (/api/experiments...)A signed-in dashboard session, or a secret key: Authorization: Bearer cro_sk_.... A secret key only sees experiments in its own project.
Key management (/api/keys...)Dashboard session only.

Keep secret keys on the server

A secret key can read and change every experiment in its project. Store it in an environment variable, never in client code or a repository.

Errors#

StatusMeaning
400Invalid input. The message names the field, for example variations: Experiment must have at least 2 variations.
401Missing, malformed, unknown or revoked key, or not signed in.
403The organization is suspended, a plan limit was reached, or the action is not allowed for this kind of caller.
404Not found. Experiments in other projects also return 404.
429Rate limit exceeded on SDK endpoints. A Retry-After header says how many seconds to wait.

SDK endpoints#

These accept cross-origin requests from any site (CORS *) and answer OPTIONS preflights. They are rate limited per key and client IP.

POST /api/assign#

Returns the visitor's variation, creating the assignment if needed. The data is recorded in the environment of the key.

curl
curl -X POST https://YOUR-HOST/api/assign \
  -H 'Content-Type: application/json' \
  -H 'X-API-Key: cro_pk_prod_...' \
  -d '{"experiment_id": "exp_123", "user_id": "user_42"}'
200 response
{
  "experiment_id": "exp_123",
  "variation_id": "var_456",
  "variation_name": "Variant B",
  "variation_url": "https://example.com/b",
  "assigned": true
}

assigned is false when the experiment is not running, the visitor is outside the traffic allocation, or the organization reached its monthly visitor limit (then reason is "quota_exceeded"). Visitors who already have an assignment keep it while the experiment runs, even after the limit is reached.

POST /api/track#

FieldTypeNotes
experiment_idstringRequired.
variation_idstringRequired. Must belong to the experiment.
user_idstringRequired. 1 to 200 printable ASCII characters.
event_typepage_view or conversionRequired.
event_namestringOptional, up to 200 characters.
metadataobjectOptional, under 4096 bytes as JSON.
curl
curl -X POST https://YOUR-HOST/api/track \
  -H 'Content-Type: application/json' \
  -H 'X-API-Key: cro_pk_prod_...' \
  -d '{"experiment_id":"exp_123","variation_id":"var_456","user_id":"user_42","event_type":"conversion","event_name":"purchase"}'

Returns 201 with { "success": true }.

Experiments#

GET /api/experiments#

Lists experiments you can access: { "experiments": [...] }.

curl
curl https://YOUR-HOST/api/experiments \
  -H 'Authorization: Bearer cro_sk_prod_...'

POST /api/experiments#

Creates a draft experiment. Weights must add up to 100 and exactly one variation must be the control. Requires a dashboard session: creating experiments with a secret key returns 403 for now.

Request body
{
  "name": "Pricing page headline",
  "description": "A benefit-led headline will increase plan selections.",
  "project_id": "prj_...",          // optional, defaults to the selected project
  "traffic_allocation": 100,        // optional, 0 to 100, default 100
  "variations": [
    { "name": "Control",   "weight": 50, "is_control": true },
    { "name": "Variant B", "weight": 50, "is_control": false, "url": "https://example.com/b" }
  ]
}

Returns 201 with { "experiment": {...}, "variations": [...] }.

GET /api/experiments/{id}#

Returns { "experiment": {...}, "variations": [...] }.

PATCH /api/experiments/{id}#

Updates name, description, traffic_allocation or status. Any other field is rejected. Status changes must follow the allowed transitions (draft to running, running to paused or completed, paused to running or completed).

Start an experiment
curl -X PATCH https://YOUR-HOST/api/experiments/exp_123 \
  -H 'Authorization: Bearer cro_sk_prod_...' \
  -H 'Content-Type: application/json' \
  -d '{"status": "running"}'

DELETE /api/experiments/{id}#

Deletes a draft. Any other status returns 400.

GET /api/experiments/{id}/results#

Optional query parameter environment=dev|staging|prod. Without it, all environments are combined.

200 response
{
  "experiment_id": "exp_123",
  "experiment_name": "Pricing page headline",
  "status": "running",
  "sample_size": 2400,
  "winner": "var_456",               // present only when a variant is significantly better
  "variations": [
    {
      "variation_id": "var_111", "variation_name": "Control", "is_control": true,
      "total_users": 1200, "conversions": 60, "conversion_rate": 0.05,
      "confidence_interval": { "lower": 0.039, "upper": 0.064 }
    }
  ],
  "statistical_significance": {
    "is_significant": true, "p_value": 0.012, "confidence_level": 0.95, "z_score": -2.51
  }
}

z_score is computed as control minus variant, so a negative value means the variant converts better.

API keys#

Dashboard session only. GET /api/keys lists environments and keys (prefix only). POST /api/keys with { "environment_id": "env_...", "type": "sdk" | "secret", "label": "optional" } creates a key and returns the plaintext once. DELETE /api/keys/{id} revokes it immediately.