This is a simple sample app that shows how you can use the Uplift APIs with NodeJS.
To run this sample app, you'll need Node.js installed on your system.
- Minimum Node.js Version: The application requires Node.js 16 or higher.
- Where to Download Node.js:
- Visit the Node.js official website.
- Download and install the LTS (Long-Term Support) version, which is recommended for most users.
The Node.js app organizes the files as follows:
nodejs/
├── app.js # Main script to start the data export process
├── captures.js # Script to list captures and fetch a capture by id
├── athletes.js # Script to list/get and (optionally) create/update/delete athletes
├── api.js # Contains functions for API interaction
├── file.js # Contains file handling and CSV export logic
├── env.example.json # Template for env.json (copy and add your API key)
├── package.json # Node.js dependencies and scripts
└── README.md # This file
Install the dependencies using npm in this directory.
$ npm install
In this directory, copy the provided template env.example.json to a new file named env.json, then fill in your API key:
$ cp env.example.json env.jsonenv.json is git-ignored so your key is never committed. The template looks like this:
{
"UPLIFT_API_KEY": "your_api_key_here",
"UPLIFT_API_URL": "https://api.uplift.ai/v1"
}UPLIFT_API_KEY: Your API key (format starting withsk). Sent as a Bearer token in theAuthorizationheader. Generate one in the Uplift Platform under Settings → API Keys (Developer section); see the Authentication guide.UPLIFT_API_URL: The API base URL (https://api.uplift.ai/v1). Every sample derives its endpoint from this base by appending the resource path (/data/export,/athletes,/captures).
Before starting the app, you need to set the following variables in the app.js file. These are critical for defining the data export process:
-
Categories (Activity and Movement): A list of activities and movements for which you want to retrieve data.
- Example:
const categories = [ { activity: "baseball", movement: "hitting" }, { activity: "baseball", movement: "pitching" } ];
- Example:
-
Time Range (Start and End Time): Set the start and end time in epoch format for the data you wish to retrieve.
- Example:
let startTime = 1604807785; // 24 hours ago let endTime = Math.floor(Date.now() / 1000); // Current time
- Example:
-
Date Mode: Define how to filter the data. You can choose between
last_modifiedorcapture_time.- Example:
let dateMode = "last_modified"; // Use 'capture_time' for the actual capture time
- Example:
-
Pagination Settings: Set the pagination parameters to control how many rows to fetch and skip.
- Example:
let offset = 0; // Default is 0 let limit = 500; // Maximum rows per request (default is 500)
- Example:
Make sure to adjust these variables according to the data you're interested in exporting. The following parameters are passed to the createExportJob function:
const data = {
activity: category.activity,
movement: category.movement,
startTime,
endTime,
dateMode
};The createExportJob function also supports additional data filtering options through the following optional parameters:
athletes: Filter data by specific athlete IDsmetrics: Limit the export to specific metricsrow_filter_column: Specify a column to filter rows
For more details, please refer to the API Reference.
Here is an example that includes these optional parameters:
const data = {
activity: category.activity,
movement: category.movement,
startTime,
endTime,
dateMode,
athletes: [
"athlete_id1",
"athlete_id2"
],
metrics: [
"metric1",
"metric2"
],
row_filter_column: "column_name"
};Once you've installed the dependencies, set up the API key, and configured the variables in app.js, you can start the app by running the following command in your terminal:
$ node app.js
For more details on how the app works, the usage, and the output of the export process, please refer to the Sample App Usage (NodeJS and Python) section in the main README.md.
captures.js demonstrates the read-only Captures endpoints: listing captures for your organization and fetching a single capture by id. A capture represents a single recorded movement (a video and its analysis).
Note: Creating a capture by uploading a video (
POST /captures) is an Enterprise feature and is intentionally not covered by this sample. See Create Capture.
Returns a paginated list of captures, sorted newest first (by capture time). All query parameters are optional:
| Parameter | Description |
|---|---|
athlete_id |
UUID; restrict to captures for that athlete. Returns 400 when the athlete is not in your organization. |
source |
When set to api, only captures with source equal to api are returned. |
status |
One of awaiting_upload, processing, completed, error. |
activity |
Case-insensitive filter on activity (e.g. baseball). |
movement |
Case-insensitive filter on movement (e.g. hitting). |
limit |
Page size; integer 1–500. Defaults to 100. |
offset |
Rows to skip; non-negative integer. Defaults to 0. |
A successful 200 response returns:
{
"captures": [ /* array of Capture objects */ ],
"total_count": 42,
"offset": 0,
"limit": 100
}total_count is the distinct capture count matching your filters. A capture linked to more than one athlete may appear more than once in captures[].
The list scenario pages through all captures — it advances offset by the number of rows returned and keeps requesting until a page comes back empty (the same pagination pattern app.js uses for export results). Add filters inside the scenario's filters object.
Returns a single Capture object. The capture must belong to your organization and must not be deleted, otherwise a 404 is returned.
| Field | Type | Description |
|---|---|---|
session_id |
string | Capture identifier. |
athlete_id |
string | null | Athlete id when the capture is linked to an athlete. |
activity |
string | null | Activity label (casing matches the stored value). |
movement |
string | null | Movement label (casing matches the stored value). |
status |
string | One of awaiting_upload, processing, completed, error. |
error |
string | null | Error summary when applicable. |
capture_time |
string | null | ISO 8601 capture time when available. |
fps_detected |
number | null | Detected frame rate when available. |
Beyond these fixed fields, a capture may include additional top-level movement dimension fields based on its activity and movement (for example, handedness for baseball/hitting). See the movement dimensions catalog.
A capture moves through awaiting_upload → processing → completed (or error). The poll scenario fetches a capture by id every few seconds until it reaches a terminal status — the same polling approach app.js uses to wait for an export job to complete.
captures.js is organized as independent scenarios. Choose one by editing the SCENARIO constant at the top of the file, set CAPTURE_ID when the scenario needs it, then run:
$ node captures.js # or: npm run captures
SCENARIO |
What it does |
|---|---|
list |
Pages through captures in your organization (full pagination loop). |
get |
Fetches a single capture by CAPTURE_ID. |
poll |
Polls CAPTURE_ID until it finishes processing. |
For full endpoint reference, see List Captures and Get Capture.
athletes.js is organized as independent scenarios — one for each thing you'd want to do with the Athletes API (list, get, create, update, delete, or a full lifecycle). Choose one by editing the SCENARIO constant at the top of the file.
Returns the organization's athletes. All query parameters are optional:
| Parameter | Description |
|---|---|
limit |
Page size. Defaults to 100. |
offset |
Rows to skip. Defaults to 0. |
sorted_by |
Sort field: first_name, last_name, or date_of_birth. |
| custom | Any custom organization attribute (e.g. sport, team). Multiple attributes are combined with logical AND; an unmatched value returns an empty array. |
A successful 200 response returns { "athletes": [ /* Athlete objects */ ] }. The list scenario pages through all athletes, advancing offset until a page comes back empty. Add filters inside the scenario's filters object.
Returns { "athlete": { ... } } for a single athlete.
Only first_name is required. Returns { "athlete": { ... } } including the generated id. Supported fields:
| Field | Type | Notes |
|---|---|---|
first_name |
string | Required. |
last_name |
string | |
date_of_birth |
string | YYYY-MM-DD. |
email |
string | |
phone |
string | |
gender |
string | male, female, non-binary, prefer_not_to_say. |
height |
integer | Inches. |
weight |
integer | Pounds (lbs). |
dominant_arm |
string | left, right, both. |
dominant_leg |
string | left, right, both. |
activity |
string | baseball, softball, tennis, basketball, golf. |
positions |
string[] | Requires activity. Valid positions depend on the activity. |
competition_level |
string | youth, high_school, college, professional. |
custom_attributes |
object | String → string key-value pairs. |
Send only the fields you want to change. Returns { "athlete": { ... } }.
Returns { "message": "..." } confirming the deletion.
Choose a scenario by editing the SCENARIO constant at the top of athletes.js, set ATHLETE_ID when the scenario needs it, then run:
$ node athletes.js # or: npm run athletes
SCENARIO |
What it does |
|---|---|
list |
Pages through every athlete (full pagination loop). |
get |
Fetches a single athlete by ATHLETE_ID. |
create |
Creates a sample athlete and prints its new id. |
update |
Updates a field on ATHLETE_ID (shows before/after). |
delete |
Deletes ATHLETE_ID. |
lifecycle |
Runs create → get → update → delete end to end on a throwaway athlete. |
Note:
create,update,delete, andlifecyclemodify your data.lifecycleis self-contained — it creates its own athlete and deletes it at the end — so it's a safe way to see the full write flow.
For full endpoint reference, see Create, List, Get, Update, and Delete.
For additional information and explanations, please refer to the comments within the files.