Skip to main content

Goal

This tutorial describes how the kameleoon_to_airtable.py script works, step by step. Given a Kameleoon experiment ID, an Airtable base ID, and an Airtable table ID, the script retrieves the experiment metadata and statistical results, determines the best-performing variation, maps the data onto the Airtable Experiments schema, and writes the record back to Airtable. Re-running the script for the same experiment updates the existing row instead of creating a duplicate. It follows the previous tutorial on retrieving experiment results using the Automation API and reuses the same request and poll flow.

Requirements

  • Kameleoon API credentials. The Automation API requires an access token. The script obtains one programmatically from a client_id and client_secret using the client_credentials grant. See Obtain an access token.
  • An Airtable personal access token with the data.records:write scope on the target base.
  • The Airtable base ID and table ID. Both appear in the table’s URL, or in the base’s API documentation. The base ID starts with app, the table ID with tbl.
  • An Airtable table with the Experiments schema already built. The script writes to these fields: Experiment Name, Status, Start date, End date, Notes, Actual, Probability, and Result. It also expects the manual-entry fields Assignee, Category, Prediction, Mkt Est, Eng Est, and Attachments to exist, even though it never sets them. Airtable rejects a write to a field name that doesn’t already exist in the table, and typecast only coerces value types for existing fields (it doesn’t create missing fields or select options). Create the table with these fields, and matching Status select options, before you run the script. See step 6 for the value each field receives.
  • Python 3.9+ with the requests library (pip install requests).
Store all credentials in environment variables. Never hard-code secrets in the script.
The tutorial uses the example experiment Product Page Redesign (ID 188308), with two variations in addition to the original: Redesign 1 (ID 828220) and Redesign 2 (ID 828221).

1. Authenticate with the Automation API

Endpoint: Obtain an access token by sending a POST request to the token endpoint.
Example:
Response:
Send the returned access_token as a Bearer token on every subsequent Automation API request. Access tokens remain valid for 2 hours by default.

2. Retrieve the experiment

Endpoint: Fetch the experiment metadata by sending a GET request to the Get an experiment endpoint.
Example:
Response (truncated):
The script reads name, status, dateStarted, dateEnded, and description for the Airtable record, and mainGoalId to scope the results request in the next step.
The API returns mainGoalId by default, so you don’t need an optionalFields parameter to read it. The Automation API does not publish a fixed enum for the status field, but other status-type fields across the API consistently use uppercase tokens (for example, STOPPED, ACTIVE, DRAFT). Step 6 maps the status field on that assumption. Confirm the exact tokens your account returns with a live request before you rely on the mapping.

3. Request the experiment’s results

Endpoint: Trigger the generation of the results report by sending a POST request to the Request experiment’s results endpoint.
Example:
Response:
Kameleoon generates the report asynchronously. The endpoint returns a dataCode, which step 4 uses to poll for the result.
This script requires bayesian: true. With Bayesian enabled, the report’s reliability value carries the Bayesian success probability (the probability that a variation beats the reference), which the script maps to the Probability field. Confirm the value against the same report in the Kameleoon app if your account uses a different default statistical method. The Automation API’s spec marks dateIntervals as required, but the example above omits it and still returns a valid report. The spec doesn’t document what an omitted dateIntervals defaults to; this tutorial assumes it covers the full run of the experiment, so confirm that behavior against your own account before relying on it. Pass a dateIntervals array to bound the report to a specific period instead.

4. Poll for the results

Endpoint: Retrieve the report by sending GET requests to the Poll results endpoint until it is ready.
The response status is WAITING while Kameleoon computes the report, READY when the data is available, or ERROR / TIMEOUT on failure. When the status is ERROR or TIMEOUT, the response includes a top-level errorDescription. The script polls on a fixed interval until the status is READY. Example:
Response (truncated):

5. Select the best-performing variation

The results contain one entry per variation under variationData, plus the _reference line for the original page. For each variation, the metrics for the requested goal sit under breakdownData._reference.generalData.goalsData[goalId]. The script skips the _reference entry, reads each variation’s improvementRate and reliability (the Bayesian success probability), and selects the variation with the highest improvement rate as the best performer. The Result field mapped in the next step records whether that variation actually won: whether it reached a high enough success probability with a positive uplift. Example:
In the example, both variations reach a 100% Bayesian success probability, but Redesign 1 (828220) shows a +211.48% improvement against Redesign 2’s -43.33%. Redesign 1 is therefore the best performer and, with a probability above 95% and a positive uplift, a genuine winner.

6. Map the data to Airtable fields

The script transforms the experiment metadata and the best-performing variation’s metrics into the Airtable Experiments schema. The script does not set fields with no Kameleoon source: Assignee, Category, Prediction, Mkt Est, Eng Est, and Attachments. These remain available for manual entry in Airtable. The script also omits empty values, so it never overwrites an existing cell with a blank. Example:
The Automation API does not publish a fixed enum for the experiment status field, and the tokens can evolve. map_status matches case-insensitively and returns None for an unrecognized status, which drops the Status cell rather than writing a wrong value. Confirm the tokens your account returns with a single GET /experiments/{experimentId} and extend STATUS_MAP if needed.

7. Upsert the record into Airtable

The Airtable Update table endpoint (PATCH /v0/meta/bases/{baseId}/tables/{tableId}) only changes a table’s name and description; it cannot write data into rows. To populate a record, use the records endpoint with the performUpsert option.
Endpoint: Create or update the record by sending a PATCH request to the records endpoint.
Example:
Response (truncated):
The response reports the outcome per record: a returned ID under createdRecords means Airtable created a new row, while an ID under updatedRecords means Airtable updated an existing row.

8. Run the script

Pass the experiment ID and the Airtable base and table IDs as arguments:
The script prints each step: authentication, the experiment fetched, the best-performing variation, the mapped fields, and whether it created or updated the Airtable record.

Full script

The complete script below matches kameleoon_to_airtable.py function for function. Copy it directly, or download the file from that link.

Customization notes

  • Status mapping lives in the STATUS_MAP constant, keyed on the uppercase status tokens the API returns. Adjust the target values if your Status options differ from Running / Implementing / Completed / Defunct, and confirm the tokens your account returns with a live GET /experiments/{experimentId} request before you rely on the mapping.
  • Probability comes from the measured Bayesian success probability, which requires bayesian: true on the results request. If your Probability field is instead a pre-experiment estimate that you enter manually, remove the Probability line from build_airtable_fields.
  • Goal selection uses the experiment’s mainGoalId. To report on a different goal, pass its ID to request_results and pick_best_variation.
  • Upsert key. Airtable matches the merge field exactly, so differences in case or whitespace in Experiment Name create a new row instead of updating the existing one. Keep experiment names stable, or merge on a dedicated stable identifier field.
  • Actual field format. The script writes the raw improvementRate value (for example, 211.48) into Actual. If Actual is an Airtable Percent field, set it to expect a plain number rather than a fraction, or divide the value by 100 in build_airtable_fields to match a fraction-based Percent field.
  • Rate limits. The Automation API allows up to 50 requests per 10 seconds and 1,000 per hour, but Kameleoon recommends staying under 12 calls per minute per account and advises against using the Automation API for high-frequency tracking. If you batch many experiments, cache tokens, throttle requests, and consider the Data API for high-volume needs.