Create analyze

Per-variant conversion, lift and statistical significance against the control arm.

POST /v1/experiment/{id}/analyze

Addresshttps://api.hanzo.ai/v1/experiment/{id}/analyze
MethodPOST
Operationpost_experiment_by_id_analyze
AuthAuthorization: Bearer $HANZO_API_KEY

Per-variant conversion, lift and statistical significance against the control arm.

It reads per-subject outcomes from the analytics plane over a window, folds them into per-variant samples, and returns each arm's exposed count, conversions, rate, lift versus control, two-proportion z, two-tailed p-value and whether it clears alpha. Arms with no data still appear with zero exposed, so the read is complete over the experiment's declared arms; the control arm sorts first. The pooled-variance estimator is used and the p-value is exact; a degenerate comparison (an empty arm, no variance) answers z 0 and p 1 — not significant, never an error.

Only EXPOSED subjects are counted, and each is joined to its arm by re-evaluating the assignment flag AT ANALYSIS TIME — not from what was in force during the window. That is the one rule to get right: analyzing an experiment after its winner has been promoted re-buckets every subject into the promoted arm, collapsing the control to zero exposed and making the result meaningless. Read the analysis before deciding. A subject the flag cannot place is dropped rather than allowed to poison the fold.

The winner in the response is ADVISORY — the significant, control-beating arm with the highest rate, or empty when inconclusive. It promotes nothing; the decision is a separate, explicit act.

Every plane read is scoped to the caller's org. Per-variant samples are also written to the research evidence plane as immutable ab rows, best-effort: the analysis is still returned if that write fails, because the samples are recomputable, and the failure is logged rather than swallowed.

Request

6 fields, body application/json (required).

FieldInTypeRequiredDescription
idpathstringyesID is the experiment the URL names.
alphabodynumber (double)—Alpha overrides the 0.05 two-tailed significance threshold when it lies strictly between 0 and 1; anything else leaves the default in place.
daysbodyinteger (int64)—Days is how far back to read when no start is given: 1 to 365, 30 by default.
endbodystring—End is the window's exclusive end in RFC3339, defaulting to now.
idbodystring—ID is the experiment the URL names.
startbodystring—Start is the window's inclusive start in RFC3339.

Response

StatusBodyMeaning
200experiment.Analysisok
defaultproblem-detailsrefused

200 body — 15 fields.

FieldInTypeAlwaysDescription
alphabodynumber (double)—the two-tailed threshold significance was judged at
experimentbodystring—the experiment that was analysed
exposedTotalbodyinteger (int64)—subjects enrolled across every arm
metricbodystring—the event a conversion is counted from
resultsbodyexperiment.Outcome[]—one row per declared arm, control first
results[].controlbodyboolean—true on the baseline arm; its own lift and stats are zero
results[].convertedbodyinteger (int64)—of those, how many fired the metric event
results[].exposedbodyinteger (int64)—subjects the arm enrolled — the denominator
results[].liftbodynumber (double)—relative to control: (rate-ctrl)/ctrl
results[].pValuebodynumber (double)—two-tailed p vs control
results[].ratebodynumber (double)—converted over exposed
results[].significantbodyboolean—pValue < alpha
results[].variantbodystring—the arm this row measures
results[].zbodynumber (double)—two-proportion z vs control
winnerbodystring—ADVISORY: the significant, control-beating arm with the highest rate, else empty

Failure carries the platform error shape — see Errors.

Examples

hanzo experiment analyze <id>
import { Configuration, ExperimentApi } from 'hanzoai';

const api = new ExperimentApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.postExperimentByIdAnalyze({ id: 'id', alpha: 0, days: 0 });
from hanzoai.cloud import ApiClient, Configuration
from hanzoai.cloud.api import ExperimentApi

client = ApiClient(Configuration(access_token=os.environ["HANZO_API_KEY"]))
result = ExperimentApi(client).post_experiment_by_id_analyze(id='id', alpha=0, days=0)
cfg := hanzoai.NewConfiguration()
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("HANZO_API_KEY"))
client := hanzoai.NewAPIClient(cfg)

resp, _, err := client.ExperimentAPI.PostExperimentByIdAnalyze(context.Background()).Execute()
if err != nil {
	return err
}
use hanzo_client::apis::{configuration::Configuration, experiment_api};

let mut cfg = Configuration::new();
cfg.bearer_access_token = std::env::var("HANZO_API_KEY").ok();

let result = experiment_api::post_experiment_by_id_analyze(&cfg, Default::default()).await?;
import ai.hanzo.cloud.ApiClient;
import ai.hanzo.cloud.api.ExperimentApi;

ApiClient client = new ApiClient();
client.setBearerToken(System.getenv("HANZO_API_KEY"));

var result = new ExperimentApi(client).postExperimentByIdAnalyze();
curl -X POST https://api.hanzo.ai/v1/experiment/<id>/analyze \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
       "alpha": 0,
       "days": 0
     }'

MCP reaches experiment through the experiment tool, which names its 7 operations with its own verbs — this one among them, under a name only MCP declares. describe explains any of them:

curl -X POST https://api.hanzo.ai/v1/mcp \
  -H "Content-Type: application/json" \
  -d '{
       "jsonrpc": "2.0",
       "id": 1,
       "method": "tools/call",
       "params": {
         "name": "describe",
         "arguments": {
           "op": "list_experiments"
         }
       }
     }'

Experiment API · All Hanzo APIs · Interactive reference

Was this page useful?