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_idandclient_secretusing theclient_credentialsgrant. See Obtain an access token. -
An Airtable personal access token with the
data.records:writescope 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 withtbl. -
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, andResult. It also expects the manual-entry fieldsAssignee,Category,Prediction,Mkt Est,Eng Est, andAttachmentsto exist, even though it never sets them. Airtable rejects a write to a field name that doesn’t already exist in the table, andtypecastonly coerces value types for existing fields (it doesn’t create missing fields or select options). Create the table with these fields, and matchingStatusselect options, before you run the script. See step 6 for the value each field receives. -
Python 3.9+ with the
requestslibrary (pip install requests).
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:
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:
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:
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:
5. Select the best-performing variation
The results contain one entry per variation undervariationData, 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:
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.
Example:
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: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_MAPconstant, keyed on the uppercase status tokens the API returns. Adjust the target values if your Status options differ fromRunning/Implementing/Completed/Defunct, and confirm the tokens your account returns with a liveGET /experiments/{experimentId}request before you rely on the mapping. - Probability comes from the measured Bayesian success probability, which requires
bayesian: trueon the results request. If your Probability field is instead a pre-experiment estimate that you enter manually, remove theProbabilityline frombuild_airtable_fields. - Goal selection uses the experiment’s
mainGoalId. To report on a different goal, pass its ID torequest_resultsandpick_best_variation. - Upsert key. Airtable matches the merge field exactly, so differences in case or whitespace in
Experiment Namecreate 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
improvementRatevalue (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 inbuild_airtable_fieldsto 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.