Using the Jam API for Data Export
Using the Jam API for Data Export

Using the Jam API for Data Export

This page describes how to set up the Jam API. The Jam API allows you to pull all training-related data from Jam so you can use it for your custom reporting in your business intelligence (BI) tool, LMS, or other systems.
Note: For LMS integration, you can also use the xAPI standard as an alternative.

Create an API Key for your Organization

The first step is to create an API key for your organization.
To create an API key, you need to be an “Owner” of your organization in Jam.
As an Owner, you can go to the following page to create an API key:
notion image

API Documentation - data-export Endpoint

Once you have created an API key, you can pull data from the data-export endpoint of the Jam API.
You find documentation of what you can get from this endpoint here:
The most relevant entities for your reporting are:
  • Users: userteams
  • Training content: missionstracks
  • Training activity: 
    • sprintstrack-assignmentsmission-assignments created by managers to drive engagement
    • the resulting sessions played by learners.
Sessions are defined as a single role-play round (audio interaction + AI feedback) played by one user of a specific mission. The session object includes the most important performance-related information:
  • the overall session score 0–100 — the main performance metric in Jam
  • breakdown per “task” in the scorecard — underlying itemScore 0 to 6, mapped to status “solved”, “partly solved”, or “not yet solved” for the user
 

Example Use Cases

Here are two Python examples illustrating of the kind of queries you can write with the data available from the data-export endpoint.

Example 1: Get all sessions played by all users

What we do here

In this Python example, we use the Jam data-export endpoint to get all role-play sessions played by all users to track each users activity.
For this we
  • Start from the sessions object
  • Extract the score (0-100) for the session and whether it was successfully completed
  • Add user names from the users object
  • Add mission titles from the missions object
  • Export the result as a csv that can be shared with stakeholders to do computations on
This example is designed for Google Colab - an environment which makes it easy for you to run code and work with the results. For this to work, you need to store your API key in the “Secrets” section of the Colab notebook. You refer to this stored API key in the Configuration.

Python Code

import pandas as pd import requests from google.colab import userdata from typing import Any # --- Configuration --- API_KEY: str = userdata.get('JAM-API') BASE_URL: str = "https://api.wejam.ai/api/v1/data-exports" HEADERS: dict[str, str] = {"accept": "application/json", "X-API-KEY": API_KEY} def fetch_paginated(endpoint: str) -> pd.DataFrame: """Fetches all pages for a given endpoint and returns a DataFrame.""" page: int = 1 results: list[dict[str, Any]] = [] while True: url: str = f"{BASE_URL}/{endpoint}?page={page}&limit=100" response: requests.Response = requests.get(url, headers=HEADERS, timeout=10) response.raise_for_status() data: dict[str, Any] = response.json() results.extend(data["data"]) if not data["meta"].get("hasNext"): break page += 1 return pd.DataFrame(results) def process_data(df_sessions: pd.DataFrame, df_users: pd.DataFrame, df_missions: pd.DataFrame) -> pd.DataFrame: """Handles all data cleaning, merging, and restructuring logic (DRY).""" # 1. Clean Sessions: Extract score early to keep logic localized # Using .get safely and ensuring integer type for the 'Score' column df_sessions["score"] = df_sessions["analysis"].apply( lambda x: x.get("score") if isinstance(x, dict) else 0 ).fillna(0).astype(int) # 2. Enrich Sessions with User Data # We merge only the columns we need (id, names) to keep the DataFrame lean df_enriched = df_sessions.merge( df_users[["id", "firstName", "lastName"]], left_on="learnerUserId", right_on="id", how="left" ) # 3. Enrich with Mission Titles # We use suffixes to avoid name collisions if 'id' or other meta-cols exist df_enriched = df_enriched.merge( df_missions[["id", "title"]], left_on="missionId", right_on="id", how="left", suffixes=('', '_mission') ) # 4. Final Transformation: Create derived columns and select output # Concatenating name strings into a single column as requested df_enriched["UserName"] = df_enriched["firstName"].fillna("") + " " + df_enriched["lastName"].fillna("") # Selecting and renaming in one step to finalize the schema output = df_enriched[[ "UserName", "title", "createdAt", "score", "completed" ]].rename(columns={ "title": "MissionTitle", "createdAt": "SessionDate", "score": "Score", "completed": "Completed" }) return output.sort_values("SessionDate", ascending=False) def main() -> None: # Step 1: I/O - Fetch raw data print("Fetching data from API...") df_u = fetch_paginated("users") df_s = fetch_paginated("sessions") df_m = fetch_paginated("missions") # Step 2: Logic - Process and transform df_final = process_data(df_s, df_u, df_m) # Step 3: I/O - Export results to csv df_final.to_csv("sessions_with_users.csv", index=False) print(f"\nSuccess. Processed {len(df_final)} sessions.") display(df_final.head(20)) if __name__ == "__main__": main()

Sample output

index
UserName
MissionTitle
SessionDate
Score
Completed
0
Emily Carter
Full Sales Call: “How did you get my number?”
2026-06-03T13:43:48.354Z
0
false
1
Emily Carter
Warm Outreach: Following Up with Existing Leads
2026-06-03T13:34:23.313Z
83
true
2
Emily Carter
Building Rapport: The Passionate Restaurant Owner
2026-06-03T10:08:53.948Z
87
true
3
Sophia Bennett
Negotiating a Price Adjustment
2026-06-03T09:49:56.301Z
100
true
4
Sophia Bennett
Handling Customer Objections
2026-06-03T09:43:03.122Z
94
true
5
Sophia Bennett
Qualifying a Private Seller
2026-06-03T09:38:16.523Z
88
true
6
Sophia Bennett
Warm Outreach: Following Up with Existing Leads
2026-06-03T09:32:55.634Z
80
true
7
Sophia Bennett
Cold Outreach: First Call with Broker Leads
2026-06-03T09:26:15.737Z
92
true
8
Oliver Brooks
Discovery Call: The Customer Who Wants to Explore Options
2026-06-03T08:26:32.375Z
88
true
9
Oliver Brooks
Next Steps: “That Could Be Interesting”
2026-06-03T08:22:21.249Z
78
true

Example 2: Get the most Active User by Number of Sessions

What we do here

The goal here is to get the most active user (in terms of number of sessions played).
For this we create the following file most_active_user.py.
For this we
  • Start from the sessions table
  • Count the number of role-play sessions played by each user.
  • Add the users information from the users table
# /// script # dependencies = [ # "requests<3", # "pandas", # ] # /// from typing import Any import pandas as pd import requests API_KEY: str = "YOUR_JAM_API_KEY" BASE_URL: str = "https://api.wejam.ai/api/v1/data-exports" HEADERS: dict[str, str] = {"accept": "application/json", "X-API-KEY": API_KEY} def fetch_paginated(endpoint: str) -> list[dict[str, Any]]: page: int = 1 results: list[dict[str, Any]] = [] while True: url: str = f"{BASE_URL}/{endpoint}?page={page}&limit=100" response: requests.Response = requests.get(url, headers=HEADERS, timeout=10) response.raise_for_status() data: dict[str, Any] = response.json() results.extend(data["data"]) if not data["meta"].get("hasNext"): break page += 1 return results def main() -> None: users_data: list[dict[str, Any]] = fetch_paginated("users") sessions_data: list[dict[str, Any]] = fetch_paginated("sessions") # Convert to DataFrames df_sessions: pd.DataFrame = pd.DataFrame(sessions_data) df_users: pd.DataFrame = pd.DataFrame(users_data) # Count sessions per user session_counts: pd.DataFrame = ( df_sessions["learnerUserId"].value_counts().rename_axis("userId").reset_index(name="sessionCount") ) # Join with users df_users["userId"] = df_users["id"] df_merged: pd.DataFrame = session_counts.merge(df_users, on="userId", how="left") # Get most active user most_active_user: pd.Series = df_merged.sort_values(by="sessionCount", ascending=False).iloc[0] print("Most Active User:") print(f"Name: {most_active_user['firstName']} {most_active_user['lastName']}") print(f"Email: {most_active_user['email']}") print(f"Sessions: {most_active_user['sessionCount']}") if __name__ == "__main__": main()

Sample output

We execute the script using uv (https://docs.astral.sh/uv/)
uv run most_active_user.py
notion image