Skip to main content
Retrieve an experiment and its results with the Automation API, transform them into Notion page properties, and upsert the record into a Notion database using a single Python script.

Goal

This tutorial describes how the kameleoon_to_notion.py script works, step by step. Given a Kameleoon experiment ID and a Notion database ID, the script retrieves the experiment metadata and statistical results, determines the best-performing variation, maps the data onto a Notion Experiments database, and writes the record back to Notion. Re-running the script for the same experiment updates the existing page instead of creating a duplicate. Steps 1–5 reuse the same request and poll flow as the Airtable export tutorial; only the destination changes.
Notion’s API has no native upsert. The script emulates one: it queries the database’s data source for a page whose title matches the experiment name, then updates that page if found or creates a new one otherwise. This tutorial targets Notion API version 2025-09-03, which organizes each database around one or more data sources.

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.
  • A Notion internal integration token. Create an integration at notion.so/my-integrations and copy its token.
  • A Notion database with an Experiments schema and the following properties: Experiment Name (title), Status (select), Start date (date), End date (date), Notes (rich text), Actual (number), Probability (select), and Result (select).
  • The Notion database ID. Open the database as a full page (the ID is the 32-character string in its URL, before the ?v= view parameter).
  • Python 3.9+ with the requests library (pip install requests).
Share the database with your integration, or every request returns object_not_found. Open the database, go to ••• → Connections → Add connections, and select your integration.
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:
The returned access_token is sent as a Bearer token on every subsequent Automation API request. Access tokens are 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 Notion page, and mainGoalId to scope the results request in the next step.
The API returns mainGoalId by default, so the script doesn’t need an optionalFields parameter to read it. The Automation API doesn’t publish a fixed enum for the status field, and the tokens can evolve. Step 7 matches status case-insensitively, so casing differences between accounts don’t break 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 used to poll for the result in the next step.
This script requires bayesian: true and sets sequentialTesting: false. bayesian and sequentialTesting are alternative methods for computing significance, and this tutorial reports the Bayesian success probability. 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 property. Confirm the value against the same report in the Kameleoon app if your account uses a different default statistical method.

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 the report is being computed, 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. If a variation’s goalsData doesn’t contain the requested goal ID, the script falls back to whichever goal is present; since step 3 already scopes the request to a single goal with goalsIds, this fallback normally has nothing else to select from. The Result property mapped later records whether that variation reached a high enough success probability with a positive uplift to count as a genuine win. 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. Resolve the Notion data source

Since version 2025-09-03, a Notion database is a container for one or more data sources, and page writes and queries target a data source ID rather than the database ID. The two IDs are not interchangeable. Endpoint: Retrieve the database to discover its data sources by sending a GET request to the Retrieve a database endpoint.
Every Notion request sends the integration token as a Bearer token and the Notion-Version header. Example:
Response (truncated):
The script uses the first data source. If your database exposes several, pick the one whose schema matches the Experiments properties.

7. Map the data to Notion properties

The script transforms the experiment metadata and the best-performing variation’s metrics into Notion property values. Each property type has its own JSON shape. The script omits empty values so that existing property values are never overwritten with blanks on update. Notion creates any missing select options automatically, but the properties themselves must already exist in the data source schema with the correct types. Example:
Notion allows only one title property per data source. The script keys the upsert on the property named Experiment Name; if your title property has a different name, rename it here and in the query filter in step 8.

8. Upsert the page into Notion

Notion has no upsert endpoint, so the script queries the data source for a page whose Experiment Name matches, then updates it or creates a new one. Find: Query the data source with a title filter using the Query a data source endpoint.
Create: Add a page parented by the data source using the Create a page endpoint.
Update: Overwrite the matched page’s properties using the Update page properties endpoint.
Example:
Response (truncated):

9. Run the script

Pass the experiment ID and the Notion database ID as arguments:
The script prints each step: authentication, the experiment fetched, the best-performing variation, the resolved data source, the mapped properties, and whether the Notion page was created or updated.

Customization notes

  • Status mapping lives in the STATUS_MAP constant, keyed on the status tokens the API returns and matched case-insensitively. Adjust the target values if your Status options differ from Running / Implementing / Completed / Defunct, and confirm the tokens your account returns with a single GET /experiments/{experimentId}.
  • Probability maps from the measured Bayesian success probability, which requires bayesian: true on the results request. If your Probability property is instead a pre-experiment estimate that you enter manually, remove the Probability block from build_notion_properties.
  • 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. The title match is exact, so differences in case or whitespace in Experiment Name create a new page instead of updating the existing one. Because the find-then-write flow is not atomic, avoid running two exports for the same experiment concurrently.
  • API version. The script pins Notion-Version: 2025-09-03. If you later add a second data source to the database, update get_data_source_id to select the correct one by name.
  • Rate limits. The Automation API allows up to 50 requests per 10 seconds and 1,000 per hour; the Notion API averages about 3 requests per second. If you batch many experiments, cache tokens and add throttling.