openai-cookbook/examples/completions_usage_api.ipynb

1839 lines
439 KiB
Plaintext
Raw Normal View History

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# OpenAI Completions Usage API Extended Example\n",
"\n",
"For most of our users, the [default usage and cost dashboards](https://platform.openai.com/usage) are sufficient. However, if you need more detailed data or a custom dashboard, you can use the Completions Usage API.\n",
"\n",
"This notebook demonstrates how to retrieve and visualize usage data from the OpenAI Completions Usage API and Costs API. We'll:\n",
"- Call the API to get completions usage data.\n",
"- Parse the JSON response into a pandas DataFrame.\n",
"- Visualize token usage over time using matplotlib.\n",
"- Use grouping by model to analyze token usage across different models.\n",
"- Display model distribution with a pie chart.\n",
"\n",
"We also include placeholders for all possible API parameters for a comprehensive overview."
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"# Install required libraries (if not already installed)\n",
"!pip install requests pandas numpy matplotlib --quiet\n",
"\n",
"# Import libraries\n",
"import requests\n",
"import pandas as pd\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import matplotlib.patches as mpatches\n",
"import time\n",
"import json\n",
"\n",
"# For inline plotting in Jupyter\n",
"%matplotlib inline\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Setup API Credentials and Parameters\n",
"\n",
"Set up an Admin Key - https://platform.openai.com/settings/organization/admin-keys\n",
"\n",
"Replace `'PLACEHOLDER'` with your actual ADMIN API key. It's best practice to load the key from an environment variable for security.\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Data retrieved successfully!\n"
]
}
],
"source": [
"# Set up the API key and headers\n",
"OPENAI_ADMIN_KEY = '<PLACEHOLDER>' \n",
"\n",
"headers = {\n",
" \"Authorization\": f\"Bearer {OPENAI_ADMIN_KEY}\",\n",
" \"Content-Type\": \"application/json\"\n",
"}\n",
"\n",
"# Define the API endpoint\n",
"url = \"https://api.openai.com/v1/organization/usage/completions\"\n",
"\n",
"# Calculate start time: n days ago from now\n",
"days_ago = 30\n",
"start_time = int(time.time()) - (days_ago * 24 * 60 * 60)\n",
"\n",
"# Define parameters with placeholders for all possible options\n",
"params = {\n",
" \"start_time\": start_time, # Required: Start time (Unix seconds)\n",
" # \"end_time\": end_time, # Optional: End time (Unix seconds)\n",
" \"bucket_width\": \"1d\", # Optional: '1m', '1h', or '1d' (default '1d')\n",
" # \"project_ids\": [\"proj_example\"], # Optional: List of project IDs\n",
" # \"user_ids\": [\"user_example\"], # Optional: List of user IDs\n",
" # \"api_key_ids\": [\"key_example\"], # Optional: List of API key IDs\n",
" # \"models\": [\"gpt-4o-mini-2024-07-18\"], # Optional: List of models\n",
" # \"batch\": False, # Optional: True for batch jobs, False for non-batch\n",
" # \"group_by\": [\"model\"], # Optional: Fields to group by\n",
" \"limit\": 7, # Optional: Number of buckets to return, this will chunk the data into 7 buckets\n",
" # \"page\": \"cursor_string\" # Optional: Cursor for pagination\n",
"}\n",
"\n",
"# Initialize an empty list to store all data\n",
"all_data = []\n",
"\n",
"# Initialize pagination cursor\n",
"page_cursor = None\n",
"\n",
"# Loop to handle pagination\n",
"while True:\n",
" if page_cursor:\n",
" params[\"page\"] = page_cursor\n",
"\n",
" response = requests.get(url, headers=headers, params=params)\n",
"\n",
" if response.status_code == 200:\n",
" data_json = response.json()\n",
" all_data.extend(data_json.get(\"data\", [])) \n",
"\n",
" page_cursor = data_json.get(\"next_page\")\n",
" if not page_cursor:\n",
" break \n",
" else:\n",
" print(f\"Error: {response.status_code}\")\n",
" break \n",
"\n",
"if all_data:\n",
" print(\"Data retrieved successfully!\")\n",
"else:\n",
" print(\"Issue: No data available to retrieve.\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Inspect the JSON Response\n",
"\n",
"Let's take a look at the raw JSON response from the API to understand its structure.\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1734345977,\n",
" \"end_time\": 1734393600,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 300245,\n",
" \"output_tokens\": 534874,\n",
" \"num_model_requests\": 298,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 53120,\n",
" \"input_audio_tokens\": 0,\n",
" \"output_audio_tokens\": 0\n",
" }\n",
" ]\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1734393600,\n",
" \"end_time\": 1734480000,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 8,\n",
" \"output_tokens\": 9,\n",
" \"num_model_requests\": 1,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 0,\n",
" \"input_audio_tokens\": 0,\n",
" \"output_audio_tokens\": 0\n",
" }\n",
" ]\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1734480000,\n",
" \"end_time\": 1734566400,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 19287,\n",
" \"output_tokens\": 1770,\n",
" \"num_model_requests\": 24,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 15104,\n",
" \"input_audio_tokens\": 47248,\n",
" \"output_audio_tokens\": 6403\n",
" }\n",
" ]\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1734566400,\n",
" \"end_time\": 1734652800,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 19162,\n",
" \"output_tokens\": 5115,\n",
" \"num_model_requests\": 38,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 3584,\n",
" \"input_audio_tokens\": 21218,\n",
" \"output_audio_tokens\": 12483\n",
" }\n",
" ]\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1734652800,\n",
" \"end_time\": 1734739200,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 50882,\n",
" \"output_tokens\": 24867,\n",
" \"num_model_requests\": 28,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 0,\n",
" \"input_audio_tokens\": 0,\n",
" \"output_audio_tokens\": 0\n",
" }\n",
" ]\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1734739200,\n",
" \"end_time\": 1734825600,\n",
" \"results\": []\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1734825600,\n",
" \"end_time\": 1734912000,\n",
" \"results\": []\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1734912000,\n",
" \"end_time\": 1734998400,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 14642697,\n",
" \"output_tokens\": 164700,\n",
" \"num_model_requests\": 11300,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 151296,\n",
" \"input_audio_tokens\": 0,\n",
" \"output_audio_tokens\": 0\n",
" }\n",
" ]\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1734998400,\n",
" \"end_time\": 1735084800,\n",
" \"results\": []\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1735084800,\n",
" \"end_time\": 1735171200,\n",
" \"results\": []\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1735171200,\n",
" \"end_time\": 1735257600,\n",
" \"results\": []\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1735257600,\n",
" \"end_time\": 1735344000,\n",
" \"results\": []\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1735344000,\n",
" \"end_time\": 1735430400,\n",
" \"results\": []\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1735430400,\n",
" \"end_time\": 1735516800,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 84150,\n",
" \"output_tokens\": 502,\n",
" \"num_model_requests\": 232,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 0,\n",
" \"input_audio_tokens\": 0,\n",
" \"output_audio_tokens\": 0\n",
" }\n",
" ]\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1735516800,\n",
" \"end_time\": 1735603200,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 118904,\n",
" \"output_tokens\": 57094,\n",
" \"num_model_requests\": 331,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 0,\n",
" \"input_audio_tokens\": 0,\n",
" \"output_audio_tokens\": 0\n",
" }\n",
" ]\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1735603200,\n",
" \"end_time\": 1735689600,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 175320,\n",
" \"output_tokens\": 67638,\n",
" \"num_model_requests\": 515,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 0,\n",
" \"input_audio_tokens\": 0,\n",
" \"output_audio_tokens\": 0\n",
" }\n",
" ]\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1735689600,\n",
" \"end_time\": 1735776000,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 1515,\n",
" \"output_tokens\": 11,\n",
" \"num_model_requests\": 5,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 0,\n",
" \"input_audio_tokens\": 0,\n",
" \"output_audio_tokens\": 0\n",
" }\n",
" ]\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1735776000,\n",
" \"end_time\": 1735862400,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 527323,\n",
" \"output_tokens\": 40396,\n",
" \"num_model_requests\": 283,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 1152,\n",
" \"input_audio_tokens\": 0,\n",
" \"output_audio_tokens\": 0\n",
" }\n",
" ]\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1735862400,\n",
" \"end_time\": 1735948800,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 31027,\n",
" \"output_tokens\": 242,\n",
" \"num_model_requests\": 106,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 0,\n",
" \"input_audio_tokens\": 12,\n",
" \"output_audio_tokens\": 29\n",
" }\n",
" ]\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1735948800,\n",
" \"end_time\": 1736035200,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 77053,\n",
" \"output_tokens\": 51801,\n",
" \"num_model_requests\": 255,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 0,\n",
" \"input_audio_tokens\": 0,\n",
" \"output_audio_tokens\": 0\n",
" }\n",
" ]\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1736035200,\n",
" \"end_time\": 1736121600,\n",
" \"results\": []\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1736121600,\n",
" \"end_time\": 1736208000,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 598758,\n",
" \"output_tokens\": 575020,\n",
" \"num_model_requests\": 1941,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 12480,\n",
" \"input_audio_tokens\": 23843,\n",
" \"output_audio_tokens\": 2441\n",
" }\n",
" ]\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1736208000,\n",
" \"end_time\": 1736294400,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 81383,\n",
" \"output_tokens\": 38568,\n",
" \"num_model_requests\": 284,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 0,\n",
" \"input_audio_tokens\": 0,\n",
" \"output_audio_tokens\": 0\n",
" }\n",
" ]\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1736294400,\n",
" \"end_time\": 1736380800,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 81272,\n",
" \"output_tokens\": 208562,\n",
" \"num_model_requests\": 265,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 0,\n",
" \"input_audio_tokens\": 0,\n",
" \"output_audio_tokens\": 0\n",
" }\n",
" ]\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1736380800,\n",
" \"end_time\": 1736467200,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 873554,\n",
" \"output_tokens\": 31703,\n",
" \"num_model_requests\": 413,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 691584,\n",
" \"input_audio_tokens\": 0,\n",
" \"output_audio_tokens\": 0\n",
" }\n",
" ]\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1736467200,\n",
" \"end_time\": 1736553600,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 129753,\n",
" \"output_tokens\": 65335,\n",
" \"num_model_requests\": 184,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 0,\n",
" \"input_audio_tokens\": 0,\n",
" \"output_audio_tokens\": 0\n",
" }\n",
" ]\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1736553600,\n",
" \"end_time\": 1736640000,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 141874,\n",
" \"output_tokens\": 9831,\n",
" \"num_model_requests\": 473,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 0,\n",
" \"input_audio_tokens\": 0,\n",
" \"output_audio_tokens\": 0\n",
" }\n",
" ]\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1736640000,\n",
" \"end_time\": 1736726400,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 45949,\n",
" \"output_tokens\": 282,\n",
" \"num_model_requests\": 150,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 0,\n",
" \"input_audio_tokens\": 0,\n",
" \"output_audio_tokens\": 0\n",
" }\n",
" ]\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1736726400,\n",
" \"end_time\": 1736812800,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 3718360,\n",
" \"output_tokens\": 97756,\n",
" \"num_model_requests\": 3053,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 76544,\n",
" \"input_audio_tokens\": 5776,\n",
" \"output_audio_tokens\": 3166\n",
" }\n",
" ]\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1736812800,\n",
" \"end_time\": 1736899200,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 52786,\n",
" \"output_tokens\": 38204,\n",
" \"num_model_requests\": 157,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 5440,\n",
" \"input_audio_tokens\": 4066,\n",
" \"output_audio_tokens\": 1097\n",
" }\n",
" ]\n",
" },\n",
" {\n",
" \"object\": \"bucket\",\n",
" \"start_time\": 1736899200,\n",
" \"end_time\": 1736937980,\n",
" \"results\": [\n",
" {\n",
" \"object\": \"organization.usage.completions.result\",\n",
" \"input_tokens\": 9588,\n",
" \"output_tokens\": 224,\n",
" \"num_model_requests\": 34,\n",
" \"project_id\": null,\n",
" \"user_id\": null,\n",
" \"api_key_id\": null,\n",
" \"model\": null,\n",
" \"batch\": null,\n",
" \"service_tier\": null,\n",
" \"input_cached_tokens\": 192,\n",
" \"input_audio_tokens\": 349,\n",
" \"output_audio_tokens\": 499\n",
" }\n",
" ]\n",
" }\n",
"]\n"
]
}
],
"source": [
"print(json.dumps(all_data, indent=2))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Parse the API Response and Create a DataFrame\n",
"\n",
"Now we will parse the JSON data, extract relevant fields, and create a pandas DataFrame for easier manipulation and analysis."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>start_datetime</th>\n",
" <th>end_datetime</th>\n",
" <th>start_time</th>\n",
" <th>end_time</th>\n",
" <th>input_tokens</th>\n",
" <th>output_tokens</th>\n",
" <th>input_cached_tokens</th>\n",
" <th>input_audio_tokens</th>\n",
" <th>output_audio_tokens</th>\n",
" <th>num_model_requests</th>\n",
" <th>project_id</th>\n",
" <th>user_id</th>\n",
" <th>api_key_id</th>\n",
" <th>model</th>\n",
" <th>batch</th>\n",
" <th>service_tier</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>2024-12-16 10:46:17</td>\n",
" <td>2024-12-17</td>\n",
" <td>1734345977</td>\n",
" <td>1734393600</td>\n",
" <td>300245</td>\n",
" <td>534874</td>\n",
" <td>53120</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>298</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2024-12-17 00:00:00</td>\n",
" <td>2024-12-18</td>\n",
" <td>1734393600</td>\n",
" <td>1734480000</td>\n",
" <td>8</td>\n",
" <td>9</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>1</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>2024-12-18 00:00:00</td>\n",
" <td>2024-12-19</td>\n",
" <td>1734480000</td>\n",
" <td>1734566400</td>\n",
" <td>19287</td>\n",
" <td>1770</td>\n",
" <td>15104</td>\n",
" <td>47248</td>\n",
" <td>6403</td>\n",
" <td>24</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>2024-12-19 00:00:00</td>\n",
" <td>2024-12-20</td>\n",
" <td>1734566400</td>\n",
" <td>1734652800</td>\n",
" <td>19162</td>\n",
" <td>5115</td>\n",
" <td>3584</td>\n",
" <td>21218</td>\n",
" <td>12483</td>\n",
" <td>38</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>2024-12-20 00:00:00</td>\n",
" <td>2024-12-21</td>\n",
" <td>1734652800</td>\n",
" <td>1734739200</td>\n",
" <td>50882</td>\n",
" <td>24867</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>28</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" start_datetime end_datetime start_time end_time input_tokens \\\n",
"0 2024-12-16 10:46:17 2024-12-17 1734345977 1734393600 300245 \n",
"1 2024-12-17 00:00:00 2024-12-18 1734393600 1734480000 8 \n",
"2 2024-12-18 00:00:00 2024-12-19 1734480000 1734566400 19287 \n",
"3 2024-12-19 00:00:00 2024-12-20 1734566400 1734652800 19162 \n",
"4 2024-12-20 00:00:00 2024-12-21 1734652800 1734739200 50882 \n",
"\n",
" output_tokens input_cached_tokens input_audio_tokens \\\n",
"0 534874 53120 0 \n",
"1 9 0 0 \n",
"2 1770 15104 47248 \n",
"3 5115 3584 21218 \n",
"4 24867 0 0 \n",
"\n",
" output_audio_tokens num_model_requests project_id user_id api_key_id \\\n",
"0 0 298 None None None \n",
"1 0 1 None None None \n",
"2 6403 24 None None None \n",
"3 12483 38 None None None \n",
"4 0 28 None None None \n",
"\n",
" model batch service_tier \n",
"0 None None None \n",
"1 None None None \n",
"2 None None None \n",
"3 None None None \n",
"4 None None None "
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Initialize a list to hold parsed records\n",
"records = []\n",
"\n",
"# Iterate through the data to extract bucketed data\n",
"for bucket in all_data: \n",
" start_time = bucket.get(\"start_time\")\n",
" end_time = bucket.get(\"end_time\")\n",
" for result in bucket.get(\"results\", []):\n",
" records.append({\n",
" \"start_time\": start_time,\n",
" \"end_time\": end_time,\n",
" \"input_tokens\": result.get(\"input_tokens\", 0),\n",
" \"output_tokens\": result.get(\"output_tokens\", 0),\n",
" \"input_cached_tokens\": result.get(\"input_cached_tokens\", 0),\n",
" \"input_audio_tokens\": result.get(\"input_audio_tokens\", 0),\n",
" \"output_audio_tokens\": result.get(\"output_audio_tokens\", 0),\n",
" \"num_model_requests\": result.get(\"num_model_requests\", 0),\n",
" \"project_id\": result.get(\"project_id\"),\n",
" \"user_id\": result.get(\"user_id\"),\n",
" \"api_key_id\": result.get(\"api_key_id\"),\n",
" \"model\": result.get(\"model\"),\n",
" \"batch\": result.get(\"batch\"),\n",
" \"service_tier\": result.get(\"service_tier\")\n",
" })\n",
"\n",
"# Create a DataFrame from the records\n",
"df = pd.DataFrame(records)\n",
"\n",
"# Convert Unix timestamps to datetime for readability\n",
"df['start_datetime'] = pd.to_datetime(df['start_time'], unit='s')\n",
"df['end_datetime'] = pd.to_datetime(df['end_time'], unit='s')\n",
"\n",
"# Reorder columns for better readability\n",
"df = df[\n",
" [\n",
" \"start_datetime\", \"end_datetime\", \"start_time\", \"end_time\",\n",
" \"input_tokens\", \"output_tokens\", \"input_cached_tokens\",\n",
" \"input_audio_tokens\", \"output_audio_tokens\", \"num_model_requests\",\n",
" \"project_id\", \"user_id\", \"api_key_id\", \"model\", \"batch\", \"service_tier\"\n",
" ]\n",
"]\n",
"\n",
"# Display the DataFrame\n",
"df.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Visualize Token Usage Over Time\n",
"\n",
"We'll create a bar chart to visualize input and output token usage for each time bucket.\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABKUAAAJOCAYAAABm7rQwAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAffNJREFUeJzt3QncTPX////XZd9C9kjWFBGyRZ92UmnRquUTCe0JJVT2ChVp0aJIm9K+R/JJi5QlWpAWimSN7FnP//Z8//5nvnNdBtfFNWfmzDzut9twzZkzM885c+bMOa95v98nw/M8zwAAAAAAAIAA5QnyyQAAAAAAAAChKAUAAAAAAIDAUZQCAAAAAABA4ChKAQAAAAAAIHAUpQAAAAAAABA4ilIAAAAAAAAIHEUpAAAAAAAABI6iFAAAAAAAAAJHUQoAAAAAAACBoygFAEgb48aNs4yMDPv9998j00455RR3AcKgatWqds455yQ6BgAAQK6gKAUASMrCkX8pVKiQVaxY0Vq3bm2PPPKIbdy40ZKZMt98882WLMaPH28jR460ZLJ582YbPHiwHXvssVakSBErUaKEnXjiifb888+b53kH/LgffvihDRgwwIKwZcsW91xTp07NViEpep3e20XrfpiomFu3bt2Yt61Zs8a9pqDej3hRfr0OvZ4gfPXVV+45//nnn2zN/9Zbb7lto7aRBQsWtMMPP9wuvvhi+/HHH2PO/+6779pxxx3ntqtHHHGE9e/f33bu3Lnf59F6Hr2u6rnKly/v1oH77rvPVq9enePXCgCA5GMxAACS0aBBg6xatWq2Y8cOW7FihTso6tatm40YMcIdWKmgkVNXXXWVXXbZZe6AKl2oKKUDVC27ZLBy5Uo7/fTTbcGCBe69UAHv33//tTfeeMM6dOjgCksvvfSS5c2bN8ePrfuOGjUqkEKIilIDBw50f++vpZ2Kgps2bcqU8+WXX7aHHnrIypQpE5neokWLOCZGGKgopfXq6quvtpIlS+53/h9++MEOPfRQu/XWW926pG3l2LFjrWnTpjZ9+nSrX79+ZN6PPvrI2rZt69bXRx991N33nnvusVWrVtkTTzyRrXxdu3a1Jk2a2K5du1whSnlV2NJ2+dVXX7XTTjvtoF4/ACD9UJQCACSls846yxo3bhy53qdPH/vf//7nui6dd955rqhRuHDhHD2mCh0HUuxA7lHhSe+dWnjofYw+2O3Zs6c9+OCD1rBhQ+vVq5elChUCoqlwoKKUpqsVFXCg+vXrt8e0zp07uxZTKjQ9+eSTkem33367K+Z//PHHli/f/zsEKF68uGvppKLW0Ucfvd/nU4tGtcSK9t1339kZZ5xhF110kc2fP98OO+ywXHltAID0QPc9AEBo6Ff4vn372h9//GEvvvhiZPr333/vWhZUr17ddUupUKGCXXPNNfb333/vd0ypaGrNUrRoUXeAltWff/7pClpDhgzJUWa/24taEdx7773uYFEZ1Vro119/jdkdavbs2a7VjIpuai0WfWC5r9fhP5ffpUyP98EHH7jl5Xe72VcRRM996qmn7jF99+7dVqlSpUwHo6+88oo1atTIDjnkEHdgW69ePXv44Yf3uSy+/vprmzRpknuvogtSPi3bI4880oYNG2Zbt26N+Zp8eu3RXd70mGolJdHdjKLnVcFLrZOqVKnilu3JJ5+8RzenvY0xpsf3l50er2zZsu5vtWrxn+tgWmipC5W6NNaoUcO15NNz3XnnnbZt27b93ve5555zRQYV9XzffPONnXnmma5rpLpI6rVOmzYtZtc0rYd+yxzN37FjR9cSLLep661a7Om16TWWK1fOWrVqZd9++21kni+++MIuueQS17VM81SuXNm6d+8eWR+ivfbaa1anTh33edK6q0Jn9PsUvf6qtdoxxxzj5lW3s+uuu87WrVuXK69r7dq1ruCjz0CxYsXc50FFdRVrslILJeXQe6IWTiq8qzWj/37476E+9/56tbft1d5ouerxo7sAqliky7XXXhspSMmNN97ousy+/vrrB/z61RpLy1fP99hjj0Wma7ujxz/qqKPc56106dLuvY1+PYsWLXKvUZ/LrNQKS7epgJvd9QcAED60lAIAhIq64OlgXb/2d+nSxU2bPHmyO7jRwbQKUvPmzbPRo0e7/1UI8YsT+6MDygsuuMAmTJjguqNEt6rSgZEO3q688soDyj106FDLkyePO3hdv3693X///e6xVDyIpgPls88+2y699FK7/PLLXTHrhhtusAIFCrhCW07cdddd7rlUUPMP+vQa96Zdu3buwFgtebQcfV9++aX99ddfrrudv7yVTYU1FZBErZ9U9IhV0PO999577v/27dvHvF0Hy1dccYUr9OixWrZsme3XqiKDMirbCy+8EHMejVmlA9ubbrrJdRlUEU2FTnVjUqEiu1SQUisUvS9aXy688EI3/UC6lEa3blFxSYW/2267za0XKtL5rcr2Ruv59ddf7z4T6oolalGoooiKhupapfXu2Wefda9VRR917YqmdU1FED2fDvCfeeYZd8Dvv7e5RTlV/FCXTRWTVDTWuqXXqHGO/EKTCmJatipizJgxwxVytA7rNp+KrVpfVQhSbn1uOnXq5IqnsdYNFS+1fVCLvMWLF7viyZw5c9x6lj9//oN6Xdr2vP32267gouWoLqpPPfWUKwSqEKTxnuTpp592z6/3WJ8TrYMqqOu91nqv9ejnn3/eo2unXwDdFxWE/K7OKhBt2LDBfT59eq0S3fpUlE2Fcv/2A6XXpOWv7bKK7zJz5kxXWNJ2Q8+hYpQ+Nyr6armocKYfEk444QTXZVfFx2iapqL3+eefn+31BwAQQh4AAEnk2Wef1UjX3syZM/c6T4kSJbyGDRtGrm/ZsmWPeV5++WX3OJ9//vkej7148eLItJNPPtldfJMmTXLzfPTRR5ke79hjj800397ovjfddFPk+qeffuqm1a5d29u2bVtk+sMPP+ym//DDD5myaNrw4cMj03SfBg0aeOXKlfO2b9++19cR/Vz639emTRuvSpUqXnYsXLjQ3f/RRx/NNP3GG2/0ihUrFlnOt956q1e8eHFv586dXk60bdvWPf66dev2Os+bb77p5nnkkUf2+ppEr13TtSx8Wu6xdm38eQsXLuz9+eefkenffPONm969e/e9rg++Dh06ZFqOq1evdvft37+/l1MPPPBApvdv7ty57nrnzp0zzXf77be76f/73/8i05RB76m/DmVkZHiDBw+O3L57927vyCOP9Fq3bu3+9um9q1atmteqVavINGXX419zzTWZnveCCy7wSpcuvd/XoeV0zDHHxLwt1vLR5zb6sxFLrM/ykCFD3Ov8448/ItPq1avnHX744d7GjRsj06ZOneqeM/p9+uKLL9y0l156KdNjTpw4Meb0rPxlpNezN//++6+3a9euTNP03hYsWNAbNGhQZNr555+/1+W1t3Uju4466ih3P130Wb377rszZfIfd8mSJXvct0mTJt7xxx+/z8f3P4evvfbaXuepX7++d+ihh+7zvZw+fbp7nOeffz4y7amnnnLTFixYEJmmbV2ZMmXc5y4n6w8AIHzovgcACB219ok+C1/02FJqfaAzZR1//PHuek67dqh1jloP6Fd6n7p4qUXDf//73wPOrFYaau0UPTaL38oia2shtezw6T66rsGI1a0vnmrVqmUNGjRwLcV8GtBYrRPOPffcyHJWNy+dQU+tknLCf8/U+mFv/NvU0iO3aQyn6JY0ajHUrFkzN/B4IvnP36NHj0zT1WLKbxWUlVraqbWNWjPdfffdkelz5861X375xbW8UUsSfRZ00fulljOff/65684WTS1Qomnd1H1z+z3QeqNWQWrRtjfRn2VlVnZ1ZVW912/No/urdZta3EW3/FPLJLWciqbWVeqSqG5e/rLQRa3IdN9PP/30oF+XupKpNZr/edGy02Or21r09kevXy2+1IIot6kl3MSJE+3xxx+32rVru+6OyuLzuz/GOsmDujTG6h6Zm9tlteLScqlZs6ZbDtHLRS31lCF6m6tuvnqfore52Vl/AADhk9ZFKe2
"text/plain": [
"<Figure size 1200x600 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"if not df.empty:\n",
" plt.figure(figsize=(12, 6))\n",
" \n",
" # Create bar charts for input and output tokens\n",
" width = 0.35 # width of the bars\n",
" indices = range(len(df))\n",
" \n",
" plt.bar(indices, df['input_tokens'], width=width, label='Input Tokens', alpha=0.7)\n",
" plt.bar([i + width for i in indices], df['output_tokens'], width=width, label='Output Tokens', alpha=0.7)\n",
" \n",
" # Set labels and ticks\n",
" plt.xlabel('Time Bucket')\n",
" plt.ylabel('Number of Tokens')\n",
" plt.title('Daily Input vs Output Token Usage Last 30 Days')\n",
" plt.xticks([i + width/2 for i in indices], [dt.strftime('%Y-%m-%d') for dt in df['start_datetime']], rotation=45)\n",
" plt.legend()\n",
" plt.tight_layout()\n",
" plt.show()\n",
"else:\n",
" print(\"No data available to plot.\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Visual Example: Grouping by Model\n",
"\n",
"In this section, we retrieve and visualize usage data grouped by model and project_id. This can help you see the total tokens used by each model over the specified period.\n",
"\n",
"### Note on Grouping Parameter\n",
"\n",
"- If you do not specify a `group_by` parameter, fields such as `project_id`, `model`, and others will return as `null`. \n",
" Although the `group_by` parameter is optional, it is recommended to include it in most cases to retrieve meaningful data.\n",
" \n",
"- You can specify multiple group fields by separating them with commas. For example: `group_by=[\"model\", \"project_id\"]`."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Data retrieved successfully!\n"
]
},
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>start_datetime</th>\n",
" <th>end_datetime</th>\n",
" <th>start_time</th>\n",
" <th>end_time</th>\n",
" <th>input_tokens</th>\n",
" <th>output_tokens</th>\n",
" <th>input_cached_tokens</th>\n",
" <th>input_audio_tokens</th>\n",
" <th>output_audio_tokens</th>\n",
" <th>num_model_requests</th>\n",
" <th>project_id</th>\n",
" <th>user_id</th>\n",
" <th>api_key_id</th>\n",
" <th>model</th>\n",
" <th>batch</th>\n",
" <th>service_tier</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>2024-12-16 10:46:29</td>\n",
" <td>2024-12-17</td>\n",
" <td>1734345989</td>\n",
" <td>1734393600</td>\n",
" <td>22483</td>\n",
" <td>15488</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>32</td>\n",
" <td>proj_frFrNmknEESBPFLqlnYutIA9</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>gpt-4o-2024-08-06</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2024-12-16 10:46:29</td>\n",
" <td>2024-12-17</td>\n",
" <td>1734345989</td>\n",
" <td>1734393600</td>\n",
" <td>22454</td>\n",
" <td>4399</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>32</td>\n",
" <td>proj_frFrNmknEESBPFLqlnYutIA9</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>gpt-3.5-turbo-0125</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>2024-12-16 10:46:29</td>\n",
" <td>2024-12-17</td>\n",
" <td>1734345989</td>\n",
" <td>1734393600</td>\n",
" <td>380</td>\n",
" <td>848</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>24</td>\n",
" <td>proj_VV4ZAjd6ALfFd9uh0vY8joR1</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>gpt-4o-mini-2024-07-18</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>2024-12-16 10:46:29</td>\n",
" <td>2024-12-17</td>\n",
" <td>1734345989</td>\n",
" <td>1734393600</td>\n",
" <td>372</td>\n",
" <td>368</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>13</td>\n",
" <td>proj_VV4ZAjd6ALfFd9uh0vY8joR1</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>gpt-4o-2024-08-06</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>2024-12-16 10:46:29</td>\n",
" <td>2024-12-17</td>\n",
" <td>1734345989</td>\n",
" <td>1734393600</td>\n",
" <td>1343</td>\n",
" <td>1468</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>7</td>\n",
" <td>proj_L67gOme4S2nBA8aQieEOwLy7</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>gpt-4o-2024-08-06</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" start_datetime end_datetime start_time end_time input_tokens \\\n",
"0 2024-12-16 10:46:29 2024-12-17 1734345989 1734393600 22483 \n",
"1 2024-12-16 10:46:29 2024-12-17 1734345989 1734393600 22454 \n",
"2 2024-12-16 10:46:29 2024-12-17 1734345989 1734393600 380 \n",
"3 2024-12-16 10:46:29 2024-12-17 1734345989 1734393600 372 \n",
"4 2024-12-16 10:46:29 2024-12-17 1734345989 1734393600 1343 \n",
"\n",
" output_tokens input_cached_tokens input_audio_tokens \\\n",
"0 15488 0 0 \n",
"1 4399 0 0 \n",
"2 848 0 0 \n",
"3 368 0 0 \n",
"4 1468 0 0 \n",
"\n",
" output_audio_tokens num_model_requests project_id \\\n",
"0 0 32 proj_frFrNmknEESBPFLqlnYutIA9 \n",
"1 0 32 proj_frFrNmknEESBPFLqlnYutIA9 \n",
"2 0 24 proj_VV4ZAjd6ALfFd9uh0vY8joR1 \n",
"3 0 13 proj_VV4ZAjd6ALfFd9uh0vY8joR1 \n",
"4 0 7 proj_L67gOme4S2nBA8aQieEOwLy7 \n",
"\n",
" user_id api_key_id model batch service_tier \n",
"0 None None gpt-4o-2024-08-06 None None \n",
"1 None None gpt-3.5-turbo-0125 None None \n",
"2 None None gpt-4o-mini-2024-07-18 None None \n",
"3 None None gpt-4o-2024-08-06 None None \n",
"4 None None gpt-4o-2024-08-06 None None "
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Calculate start time: n days ago from now\n",
"days_ago = 30\n",
"start_time = int(time.time()) - (days_ago * 24 * 60 * 60)\n",
"\n",
"# Define parameters with grouping by model and project_id\n",
"params = {\n",
" \"start_time\": start_time, # Required: Start time (Unix seconds)\n",
" \"bucket_width\": \"1d\", # Optional: '1m', '1h', or '1d' (default '1d')\n",
" \"group_by\": [\"model\", \"project_id\"], # Group data by model and project_id\n",
" \"limit\": 7 # Optional: Number of buckets to return\n",
"}\n",
"\n",
"# Initialize an empty list to store all data\n",
"all_group_data = []\n",
"\n",
"# Initialize pagination cursor\n",
"page_cursor = None\n",
"\n",
"# Loop to handle pagination\n",
"while True:\n",
" if page_cursor:\n",
" params[\"page\"] = page_cursor\n",
"\n",
" response = requests.get(url, headers=headers, params=params)\n",
"\n",
" if response.status_code == 200:\n",
" data_json = response.json()\n",
" all_group_data.extend(data_json.get(\"data\", []))\n",
"\n",
" page_cursor = data_json.get(\"next_page\")\n",
" if not page_cursor:\n",
" break \n",
" else:\n",
" print(f\"Error: {response.status_code}\")\n",
" break \n",
"\n",
"if all_group_data:\n",
" print(\"Data retrieved successfully!\")\n",
"else:\n",
" print(\"Issue: No data available to retrieve.\")\n",
"\n",
"# Initialize a list to hold parsed records\n",
"records = []\n",
"\n",
"# Iterate through the data to extract bucketed data\n",
"for bucket in all_group_data:\n",
" start_time = bucket.get(\"start_time\")\n",
" end_time = bucket.get(\"end_time\")\n",
" for result in bucket.get(\"results\", []):\n",
" records.append({\n",
" \"start_time\": start_time,\n",
" \"end_time\": end_time,\n",
" \"input_tokens\": result.get(\"input_tokens\", 0),\n",
" \"output_tokens\": result.get(\"output_tokens\", 0),\n",
" \"input_cached_tokens\": result.get(\"input_cached_tokens\", 0),\n",
" \"input_audio_tokens\": result.get(\"input_audio_tokens\", 0),\n",
" \"output_audio_tokens\": result.get(\"output_audio_tokens\", 0),\n",
" \"num_model_requests\": result.get(\"num_model_requests\", 0),\n",
" \"project_id\": result.get(\"project_id\", \"N/A\"),\n",
" \"user_id\": result.get(\"user_id\", \"N/A\"),\n",
" \"api_key_id\": result.get(\"api_key_id\", \"N/A\"),\n",
" \"model\": result.get(\"model\", \"N/A\"),\n",
" \"batch\": result.get(\"batch\", \"N/A\"),\n",
" \"service_tier\": result.get(\"service_tier\", \"N/A\")\n",
" })\n",
"\n",
"# Create a DataFrame from the records\n",
"df = pd.DataFrame(records)\n",
"\n",
"# Convert Unix timestamps to datetime for readability\n",
"df['start_datetime'] = pd.to_datetime(df['start_time'], unit='s', errors='coerce')\n",
"df['end_datetime'] = pd.to_datetime(df['end_time'], unit='s', errors='coerce')\n",
"\n",
"# Reorder columns for better readability\n",
"df = df[\n",
" [\n",
" \"start_datetime\", \"end_datetime\", \"start_time\", \"end_time\",\n",
" \"input_tokens\", \"output_tokens\", \"input_cached_tokens\",\n",
" \"input_audio_tokens\", \"output_audio_tokens\", \"num_model_requests\",\n",
" \"project_id\", \"user_id\", \"api_key_id\", \"model\", \"batch\", \"service_tier\"\n",
" ]\n",
"]\n",
"\n",
"# Display the DataFrame\n",
"df.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Parse the API Response into DataFrame and render a stacked bar chart\n",
"\n",
"Now we will parse the JSON data, extract relevant fields, and create a pandas DataFrame for easier manipulation and analysis."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 1200x600 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Group data by model and project_id and aggregate model request counts\n",
"grouped_by_model_project = (\n",
" df.groupby([\"model\", \"project_id\"])\n",
" .agg({\n",
" \"num_model_requests\": \"sum\",\n",
" })\n",
" .reset_index()\n",
")\n",
"\n",
"# Determine unique models and project IDs for plotting and color mapping\n",
"models = sorted(grouped_by_model_project['model'].unique())\n",
"project_ids = sorted(grouped_by_model_project['project_id'].unique())\n",
"distinct_colors = [\n",
" \"#1f77b4\", \"#ff7f0e\", \"#2ca02c\", \"#d62728\", \"#9467bd\",\n",
" \"#8c564b\", \"#e377c2\", \"#7f7f7f\", \"#bcbd22\", \"#17becf\"\n",
"]\n",
"project_color_mapping = {pid: distinct_colors[i % len(distinct_colors)] for i, pid in enumerate(project_ids)}\n",
"\n",
"# Calculate total number of requests per project_id for legend\n",
"project_totals = (\n",
" grouped_by_model_project.groupby(\"project_id\")[\"num_model_requests\"].sum()\n",
" .sort_values(ascending=False) # Sort by highest total first\n",
")\n",
"\n",
"# Set up bar positions\n",
"n_models = len(models)\n",
"bar_width = 0.6\n",
"x = np.arange(n_models)\n",
"\n",
"plt.figure(figsize=(12, 6))\n",
"\n",
"# Plot stacked bars for each model\n",
"for model_idx, model in enumerate(models):\n",
" # Filter data for the current model\n",
" model_data = grouped_by_model_project[grouped_by_model_project['model'] == model]\n",
" \n",
" bottom = 0\n",
" # Stack segments for each project ID within the bars\n",
" for _, row in model_data.iterrows():\n",
" color = project_color_mapping[row['project_id']]\n",
" plt.bar(x[model_idx], row['num_model_requests'], width=bar_width,\n",
" bottom=bottom, color=color)\n",
" bottom += row['num_model_requests']\n",
"\n",
"# Labeling and styling\n",
"plt.xlabel('Model')\n",
"plt.ylabel('Number of Model Requests')\n",
"plt.title('Total Model Requests by Model and Project ID Last 30 Days')\n",
"plt.xticks(x, models, rotation=45, ha=\"right\")\n",
"\n",
"# Create a sorted legend with totals\n",
"handles = [\n",
" mpatches.Patch(\n",
" color=project_color_mapping[pid],\n",
" label=f\"{pid} (Total: {total})\"\n",
" )\n",
" for pid, total in project_totals.items()\n",
"]\n",
"plt.legend(handles=handles, bbox_to_anchor=(1.05, 1), loc='upper left')\n",
"\n",
"plt.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Visual Example: Model Distribution Pie Chart\n",
"\n",
"This section visualizes the distribution of token usage across different models using a pie chart.\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAA90AAAMkCAYAAAClU0NXAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAvEFJREFUeJzs3Qd4W+X1x/Gf9x7Ze+9FEsLemwJlzzLKKnv+S2mhAzqg7BFaoGW3tMyWUfaGBEgIkITsxNl7OLEd7yHp/5wbHGzHTjwkvxrfz/OI2LJ0dSRdiXvued/zxgUCgYAAAAAAAEDQxQd/kwAAAAAAwJB0AwAAAAAQIiTdAAAAAACECEk3AAAAAAAhQtINAAAAAECIkHQDAAAAABAiJN0AAAAAAIQISTcAAAAAACFC0g0AAAAAQIiQdAMIW59++qni4uL0+9//3snj9+/f37vUZbFYTBabC8uXL/ce/4ILLlCkqq6u9l7HIUOGKCUlxXs+r732miJFsN6DQw45xNsOwl8svleuv+sAIJqQdANolwSl7iU9PV09e/bU4YcfrltuuUVLliwJyWNH6oFyY8l+NLnvvvv0hz/8wdsHfvGLX+jWW2/V8OHDd3ofez1q9585c+Y0ehufz6devXptv53te9Gk4ecoMTFR3bp1049//GN9+OGHimWR+llvbuJb95KRkaHddtvN+1tpaamixTPPPOM9P/u3Nfe78847G90nai9JSUnq1KmTxo0bp4svvljvvvuu/H5/kJ8FADQusYnrASCoBg0apHPPPdf7ubKyUhs3btS0adP0pz/9SX/+85/1y1/+Urfffnu9A+e99tpL8+fPV+fOnZ3E/NFHHyncWFJpr0lOTo4i1ZtvvqnMzEx98MEHSk5Obvb94uO3nSd+6qmndP/99+/w93feeUdr1671ktGamhpFI0sarr76au/niooKzZ07V2+99ZZ3ee655/STn/zEdYgIgVNPPVWjR4/2fl63bp3+97//eSeu3njjDU2ZMqVFn6Pmsv3srLPOUt++fRXJbrjhBu/7xhLswsJC7/vz3//+t/c9st9+++n555+P+OcIIPyRdANoF4MHD250mPjnn3+u8847T3fccYcSEhK8JLyWVcR3VQEN9YmCcGPVGpevSTBYYmzJY0sTBXvuBx10kP71r3/prrvu8n6vyw6i7WTE2LFjNWnSJEUjOwHV8HP0wgsveMn2zTffTNIdpU477TQvAa517733eiclp0+f7p1sCcV0E9vXXJ3wDCYbTdO9e/d61+Xn5+vaa6/1Eu6jjz5a33zzjTeCAABCheHlAJw64IADvGF+Nrf37rvv1qpVq3Y5pzsvL08XXnihBgwY4N2vY8eOXqJ1/fXXKxAIeLex+3322Wfbf6691B6c1p2Xa5WPk08+2UsE6w5L3tUw7yeffFJjxoxRamqqV4H+v//7PxUXFzd7XnrDucG1v69YscK71I279v47m09s97FhkxaLJbS9e/f2fl+5cuUOt60delk7v9qep72WQ4cO1SOPPKKWevrpp7X33nt7FSW72M8Nh4nWDpVdtmxZvefXkqH0F110kTZt2uRV+Oqy66yCbklnWlpam+KsO1zdkns7YWTvsf1rJ4d2NiTVRnDYfmC3tdfTkharUjY1JD4YzjzzTC9hsNfUkom67PNgJyP2339/ZWdneyey9thjD++6xmzZskWXX365N2zdbrvnnnvq1VdfbXTob0v27da+RsH4rJtPPvlExxxzjDelwbZjz+/AAw/UY4891qLX2kYX3HTTTV5l1PaJESNG6C9/+cv2WMwTTzzhPb59nzXm448/9v5+2WWXqbWysrK2P7+vv/7a+7fue2SfD3vP7XZ1P1+2f9hrV/t6du3aVWeccUajr/3O5nTPmjXLOwnQo0cP77umX79+uuaaa7R58+ZG4/3uu+90zjnneN9J9rh2vx/96EfbP8f2XOx9NvZv3fcxFGyfs5N3hx12mBYsWKCHH344JI8DALWodANwbtiwYd6B37PPPus11LKDt51VSa3CY3MZjzvuOC/hsJ/t4NySRasA2fBimydsB5+WiNjPtWw+X12LFy/WPvvs4yXPduBnB43NqcDa8GYbfm6Pb3HYnNoHH3xQU6dO9aqsDauwzZGbm+vFatsxdnBcN0nemUWLFnknMCz5PP744zVq1CjvQNqSKzuwtREFllA3ZEmqDfO3hMRGGrz00ku66qqrvPgvueSSZsVtFSNLPCzZtyTf/Pe///UOnmfMmKGJEyfWew4Nn5897+aykyMdOnTwkudTTjll+/W279gJBEvKf/e737UpzlqXXnqp9/pZgmKviSVc9r5/+eWXjW7fehPYc1y9erWOOuoonXTSSV6CaY/x3nvvefuLJfmhZPt+LUsELdGxap41rTv77LO9fduG9dvznzdvnvd5qVVWVubFP3v2bO277746+OCDvZNgto/b8wmGlrxGwfqs29B7+0zYfnbiiSd6CZ99TiwRtP3G3ufmsu8p21fsJIGxuG2/spMM1qug9jNlQ5rtpJxNm2no8ccf9/5t7udrVxompi+//LLef/99b67/lVdeqa1bt3rX23O297X2PbCk2U6A/ec///FeI3v97TtkV2xou70ONt3DXs8+ffp4+9Jf//pXbxtfffWV9xmtZa+R7Xu2P9r7YN/39p7b7ew1sutsP7Ch36+//rq3zYbf06Fg8f/mN7/xToK8+OKLjb5XABA0AQAIoWXLllkJKHD00Ufv9HZPPvmkd7vzzjtv+3WffPKJd92tt966/bqHHnrIu+7BBx/cYRubN2+u9/vBBx/s3XZncdnllltuafQ2/fr18y51WSx2n+Tk5MB33323/Xq/3x84++yzvb/de++9O30ODWM4//zzd/m4u7rPoYce6l3/97//vd71Dz/8sHf9YYcd1uhrs/feeweKioq2X79gwYJAYmJiYNiwYYHm+Oyzz7ztjBgxIlBYWLj9+i1btgSGDh3q/W3SpEnNfn5NsdunpKR4P1999dVejOvWrdv+91GjRgXGjBnj/Wz7mj2uvVatjbP2fRs7dmygpKRk+/WrV68OdO7cudH3YL/99gskJCQE3n333XrXL1y4MJCVlbU9vubsn42x2zb2vjz33HPe3+w1qOuxxx7zrr/wwgsDVVVV26+vrKwMHH/88d7fvvnmmx327UsuuaTeduz51H5Wnn766Tbt2y15jYL1WT/llFO8v82cOXOHv+Xn5weao3b79vrX3X/sZ7suLi4u8PXXX2+//oorrvBu/+mnn+4Qt+3H48aNa9bj1r4nzz//fL3ri4uLAyNHjvT+9o9//MO7zt4b+z0+Pj7wwQcf7LAt2w/s7zfffHO969966y3v+sGDBwd8Pt8Oj23vc93XKzs7O9CrV6/A8uXL623HYrTb2+ez1vr16wMZGRneZfr06TvEtGrVqu0/18Zfdx9rjtr73XHHHY2+Z3W/JxqqqKjwvkvsNauurm7R4wJASzC8HEBYsGGfpuHw2KY0NoTYhp62lM31s2pHS/30pz/1OgjXrTZZQzirFre0+25b2fBxGz47cuTIHapnNlTY5oBbNafu0P1aNlzahh3XsiqUDUtduHDhDkPlG/OPf/xj+1DUus3drNJVW3UM9uth1WxrlFb72FYxs4Zidn2w4vznP//p/Wvd9evO9bQq+XXXXbfD9q36aRXw888/35sjWpeNMLD3xSrIbR1mbp8Pew52sWHOViW0arYNlX/00Ufr3dYqjxa7DZ2tO/LCqt3WtNBYFbzuc7a//fGPf6y3HXs+ttJAW7X2NQrWZ72x7diUkpawURR19x/7+be//a1Xxa3dx2o/d7VDzeuyyro1kmxplduq0bXv+xVXXOF9Tq26bFMF6s71NlYpPuKII+pdV1VV5b3X9nwt3rqOPfZYHXnkkd6ony+++GKncdg+YpVz+96wIeV1WRy7776712Oglr0mNjrBKv/jx4/fYXs23NwlG+pur4lNGbGpFQAQKgwvBxBRLMmwhlE23NeGotq8QBsGO3DgwFZtz+aHtqbzr80HbcgOQm2opSWAdpAbio7CjZk5c6b
"text/plain": [
"<Figure size 1000x800 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABHYAAAMVCAYAAAAS9nP6AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3QV4VGf6BfAzcVcSggT34lKgBeotdaGuW3fb3dr+t+12d7v1bt2FFupGDaeUIgWKu0tC3N1G/s+57ISJkkCSm5k5v+eZlpm5mfnm3pkk9+T93s/icDgcEBERERERERERt+Nj9gBEREREREREROTIKNgREREREREREXFTCnZERERERERERNyUgh0RERERERERETelYEdERERERERExE0p2BERERERERERcVMKdkRERERERERE3JSCHRERERERERERN6VgR0RERERERETETSnYERGRJvn1119hsVjwj3/846gfq0ePHsbFm73yyis45phjEBISYuzXl156Ce2ZjtmR2bdvn3F8//SnP8FT8PWceOKJ8HYtsR/4vuDj8H0iIiJypBTsiIiYeLJX+xIaGoqhQ4fiiSeeQHFxsdnDlFby+eef495770VgYKDx/8cffxzjxo1r9Gt4AukMVpzvn4ZCtvT0dDz00EPGeyk8PNwIj/r164c77rgDO3furPdr+Fh8TAZ47dHUqVNrjM91fxzJ5y0gIACJiYm48sorsWHDBngSTw1e+Jqcx++nn35qcLuxY8dWb9de389Hiu951/exr68vYmNjccopp+Crr76CN1NIJiLezM/sAYiIeLPevXvj6quvNv7tcDiQlZWFWbNmGSfZs2fPxpIlS4xf3MWzOE9K+f/OnTu36GPPnDkTl19+OYqKioyw6Oabb4afnx/WrVuHt99+G++99x7efPNN3HjjjfDmzxuD0+XLl+Ozzz7Dt99+iwULFuD4449v8efs0qULtm7disjIyBZ/bG/F9/MHH3yAc845p859mzdvxsqVK41trFYrPBF/Jvz97383/l1VVYVdu3bhu+++wy+//IL//Oc/eOSRR8weooiItDEFOyIiJurTp0+dqouKigqMHz/eOOlctGgRTj75ZNPGJ60jNTXV+H9LhzqrV6/GRRddBB8fH8yYMQPnn39+jft///13nHfeeUbYk5CQgLPPPhve/nnjCfKTTz6J//u/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
"text/plain": [
"<Figure size 1000x800 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"records = []\n",
"for bucket in all_group_data:\n",
" for result in bucket.get(\"results\", []):\n",
" records.append({\n",
" \"project_id\": result.get(\"project_id\", \"N/A\"),\n",
" \"num_model_requests\": result.get(\"num_model_requests\", 0),\n",
" })\n",
"\n",
"# Create a DataFrame\n",
"df = pd.DataFrame(records)\n",
"\n",
"# Aggregate data by project_id\n",
"grouped_by_project = (\n",
" df.groupby(\"project_id\")\n",
" .agg({\"num_model_requests\": \"sum\"})\n",
" .reset_index()\n",
")\n",
"\n",
"# Visualize Pie Chart\n",
"if not grouped_by_project.empty:\n",
" # Filter out rows where num_model_requests == 0\n",
" filtered_grouped_by_project = grouped_by_project[grouped_by_project['num_model_requests'] > 0]\n",
" \n",
" # Calculate the total model requests after filtering\n",
" total_requests = filtered_grouped_by_project['num_model_requests'].sum()\n",
" \n",
" if total_requests > 0:\n",
" # Calculate percentage of total for each project\n",
" filtered_grouped_by_project['percentage'] = (\n",
" filtered_grouped_by_project['num_model_requests'] / total_requests\n",
" ) * 100\n",
" \n",
" # Separate \"Other\" projects (below 5%)\n",
" other_projects = filtered_grouped_by_project[filtered_grouped_by_project['percentage'] < 5]\n",
" main_projects = filtered_grouped_by_project[filtered_grouped_by_project['percentage'] >= 5]\n",
" \n",
" # Sum up \"Other\" projects\n",
" if not other_projects.empty:\n",
" other_row = pd.DataFrame({\n",
" \"project_id\": [\"Other\"],\n",
" \"num_model_requests\": [other_projects['num_model_requests'].sum()],\n",
" \"percentage\": [other_projects['percentage'].sum()]\n",
" })\n",
" filtered_grouped_by_project = pd.concat([main_projects, other_row], ignore_index=True)\n",
" \n",
" # Sort by number of requests for better legend organization\n",
" filtered_grouped_by_project = filtered_grouped_by_project.sort_values(by=\"num_model_requests\", ascending=False)\n",
" \n",
" # Main pie chart for distribution of model requests by project_id\n",
" plt.figure(figsize=(10, 8))\n",
" plt.pie(\n",
" filtered_grouped_by_project['num_model_requests'], \n",
" labels=filtered_grouped_by_project['project_id'], \n",
" autopct=lambda p: f'{p:.1f}%\\n({int(p * total_requests / 100):,})',\n",
" startangle=140,\n",
" textprops={'fontsize': 10}\n",
" )\n",
" plt.title('Distribution of Model Requests by Project ID', fontsize=14)\n",
" plt.axis('equal') # Equal aspect ratio ensures pie chart is circular.\n",
" plt.tight_layout()\n",
" plt.show()\n",
" \n",
" # If there are \"Other\" projects, generate a second pie chart for breakdown\n",
" if not other_projects.empty:\n",
" other_total_requests = other_projects['num_model_requests'].sum()\n",
" \n",
" plt.figure(figsize=(10, 8))\n",
" plt.pie(\n",
" other_projects['num_model_requests'], \n",
" labels=other_projects['project_id'], \n",
" autopct=lambda p: f'{p:.1f}%\\n({int(p * other_total_requests / 100):,})',\n",
" startangle=140,\n",
" textprops={'fontsize': 10}\n",
" )\n",
" plt.title('Breakdown of \"Other\" Projects by Model Requests', fontsize=14)\n",
" plt.axis('equal') # Equal aspect ratio ensures pie chart is circular.\n",
" plt.tight_layout()\n",
" plt.show()\n",
" else:\n",
" print(\"Total model requests is zero. Pie chart will not be rendered.\")\n",
"else:\n",
" print(\"No grouped data available for pie chart.\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Costs API Example\n",
"\n",
"In this section, we'll work with the OpenAI Costs API to retrieve and visualize cost data. Similar to the completions data, we'll:\n",
"- Call the Costs API to get aggregated cost data.\n",
"- Parse the JSON response into a pandas DataFrame.\n",
"- Visualize costs grouped by line item using a bar chart."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Costs data retrieved successfully!\n"
]
}
],
"source": [
"# Calculate start time: n days ago from now\n",
"days_ago = 30\n",
"start_time = int(time.time()) - (days_ago * 24 * 60 * 60)\n",
"\n",
"# Define the Costs API endpoint\n",
"costs_url = \"https://api.openai.com/v1/organization/costs\"\n",
"\n",
"# Initialize an empty list to store all data\n",
"all_costs_data = []\n",
"\n",
"# Initialize pagination cursor\n",
"page_cursor = None\n",
"\n",
"# Loop to handle pagination\n",
"while True:\n",
" costs_params = {\n",
" \"start_time\": start_time, # Required: Start time (Unix seconds)\n",
" \"bucket_width\": \"1d\", # Optional: Currently only '1d' is supported\n",
" \"limit\": 30, # Optional: Number of buckets to return\n",
" }\n",
"\n",
" if page_cursor:\n",
" costs_params[\"page\"] = page_cursor\n",
"\n",
" costs_response = requests.get(costs_url, headers=headers, params=costs_params)\n",
"\n",
" if costs_response.status_code == 200:\n",
" costs_json = costs_response.json()\n",
" all_costs_data.extend(costs_json.get(\"data\", []))\n",
"\n",
" page_cursor = costs_json.get(\"next_page\")\n",
" if not page_cursor:\n",
" break\n",
" else:\n",
" print(f\"Error: {costs_response.status_code}\")\n",
" break\n",
"\n",
"if all_costs_data:\n",
" print(\"Costs data retrieved successfully!\")\n",
"else:\n",
" print(\"No costs data found.\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Parse the Costs API Response and Create a DataFrame\n",
"\n",
"We will now parse the JSON data from the Costs API, extract relevant fields, and create a pandas DataFrame for further analysis.\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>start_time</th>\n",
" <th>end_time</th>\n",
" <th>amount_value</th>\n",
" <th>currency</th>\n",
" <th>line_item</th>\n",
" <th>project_id</th>\n",
" <th>start_datetime</th>\n",
" <th>end_datetime</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1734307200</td>\n",
" <td>1734393600</td>\n",
" <td>55.358578</td>\n",
" <td>usd</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>2024-12-16</td>\n",
" <td>2024-12-17</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>1734393600</td>\n",
" <td>1734480000</td>\n",
" <td>0.000110</td>\n",
" <td>usd</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>2024-12-17</td>\n",
" <td>2024-12-18</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>1734480000</td>\n",
" <td>1734566400</td>\n",
" <td>0.016204</td>\n",
" <td>usd</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>2024-12-18</td>\n",
" <td>2024-12-19</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>1734566400</td>\n",
" <td>1734652800</td>\n",
" <td>2.121425</td>\n",
" <td>usd</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>2024-12-19</td>\n",
" <td>2024-12-20</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>1734652800</td>\n",
" <td>1734739200</td>\n",
" <td>3.771420</td>\n",
" <td>usd</td>\n",
" <td>None</td>\n",
" <td>None</td>\n",
" <td>2024-12-20</td>\n",
" <td>2024-12-21</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" start_time end_time amount_value currency line_item project_id \\\n",
"0 1734307200 1734393600 55.358578 usd None None \n",
"1 1734393600 1734480000 0.000110 usd None None \n",
"2 1734480000 1734566400 0.016204 usd None None \n",
"3 1734566400 1734652800 2.121425 usd None None \n",
"4 1734652800 1734739200 3.771420 usd None None \n",
"\n",
" start_datetime end_datetime \n",
"0 2024-12-16 2024-12-17 \n",
"1 2024-12-17 2024-12-18 \n",
"2 2024-12-18 2024-12-19 \n",
"3 2024-12-19 2024-12-20 \n",
"4 2024-12-20 2024-12-21 "
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Initialize a list to hold parsed cost records\n",
"cost_records = []\n",
"\n",
"# Extract bucketed cost data from all_costs_data\n",
"for bucket in all_costs_data:\n",
" start_time = bucket.get(\"start_time\")\n",
" end_time = bucket.get(\"end_time\")\n",
" for result in bucket.get(\"results\", []):\n",
" cost_records.append({\n",
" \"start_time\": start_time,\n",
" \"end_time\": end_time,\n",
" \"amount_value\": result.get(\"amount\", {}).get(\"value\", 0),\n",
" \"currency\": result.get(\"amount\", {}).get(\"currency\", \"usd\"),\n",
" \"line_item\": result.get(\"line_item\"),\n",
" \"project_id\": result.get(\"project_id\")\n",
" })\n",
"\n",
"# Create a DataFrame from the cost records\n",
"cost_df = pd.DataFrame(cost_records)\n",
"\n",
"# Convert Unix timestamps to datetime for readability\n",
"cost_df['start_datetime'] = pd.to_datetime(cost_df['start_time'], unit='s')\n",
"cost_df['end_datetime'] = pd.to_datetime(cost_df['end_time'], unit='s')\n",
"\n",
"# Display the first few rows of the DataFrame\n",
"cost_df.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Visualize Costs by Line Item\n",
"\n",
"We'll create a bar chart to visualize the total costs aggregated by line item. This helps identify which categories (e.g., models or other services) contribute most to the expenses.\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 1200x600 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"if not cost_df.empty:\n",
" # Ensure datetime conversion for 'start_datetime' column\n",
" if 'start_datetime' not in cost_df.columns or not pd.api.types.is_datetime64_any_dtype(cost_df['start_datetime']):\n",
" cost_df['start_datetime'] = pd.to_datetime(cost_df['start_time'], unit='s', errors='coerce')\n",
"\n",
" # Create a new column for just the date part of 'start_datetime'\n",
" cost_df['date'] = cost_df['start_datetime'].dt.date\n",
" \n",
" # Group by date and sum the amounts\n",
" cost_per_day = cost_df.groupby('date')['amount_value'].sum().reset_index()\n",
" \n",
" # Plot the data\n",
" plt.figure(figsize=(12, 6))\n",
" plt.bar(cost_per_day['date'], cost_per_day['amount_value'], width=0.6, color='skyblue', alpha=0.8)\n",
" plt.xlabel('Date')\n",
" plt.ylabel('Total Cost (USD)')\n",
" plt.title('Total Cost per Day (Last 30 Days)')\n",
" plt.xticks(rotation=45, ha='right')\n",
" plt.tight_layout()\n",
" plt.show()\n",
"else:\n",
" print(\"No cost data available to plot.\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Additional Visualizations (Optional)\n",
"\n",
"You can extend this notebook with more visualizations for both the Completions and Costs APIs. For example:\n",
"\n",
"**Completions API:**\n",
"- Group by user, project, or model to see which ones consume the most tokens.\n",
"- Create line plots for time series analysis of token usage over days or hours.\n",
"- Use pie charts to visualize distribution of tokens across models, users, or projects.\n",
"- Experiment with different `group_by` parameters (e.g., `[\"model\", \"user_id\"]`) to gain deeper insights.\n",
"\n",
"**Costs API:**\n",
"- Group by project or line item to identify spending patterns.\n",
"- Create line or bar charts to visualize daily cost trends.\n",
"- Use pie charts to show how costs are distributed across projects, services, or line items.\n",
"- Try various `group_by` options (e.g., `[\"project_id\"]`, `[\"line_item\"]`) for granular analysis.\n",
"\n",
"Experiment with different parameters and visualization techniques using `pandas` and `matplotlib` (or libraries like Plotly/Bokeh) to gain deeper insights, and consider integrating these visualizations into interactive dashboards for real-time monitoring.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Integrating with Third-Party Dashboarding Platforms\n",
"\n",
"To bring OpenAI usage and cost data into external dashboarding tools like Tableau, Power BI, or custom platforms (e.g., Plotly Dash, Bokeh), follow these steps:\n",
"\n",
"1. **Data Collection & Preparation:**\n",
" - Use Python scripts to regularly fetch data from the Completions and Costs APIs.\n",
" - Process and aggregate the data with pandas, then store it in a database, data warehouse, or export it as CSV/JSON files.\n",
"\n",
"2. **Connecting to a Dashboard:**\n",
" - **BI Tools (Tableau, Power BI):**\n",
" - Connect directly to the prepared data source (SQL database, CSV files, or web APIs).\n",
" - Use built-in connectors to schedule data refreshes, ensuring dashboards always display current information.\n",
" - **Custom Dashboards (Plotly Dash, Bokeh):**\n",
" - Embed API calls and data processing into the dashboard code.\n",
" - Build interactive visual components that automatically update as new data is fetched.\n",
"\n",
"3. **Real-Time & Automated Updates:**\n",
" - Schedule scripts using cron jobs, task schedulers, or workflow tools (e.g., Apache Airflow) to refresh data periodically.\n",
" - Implement webhooks or streaming APIs (if available) for near real-time data updates.\n",
"\n",
"By integrating API data into third-party platforms, you can create interactive, real-time dashboards that combine OpenAI metrics with other business data, offering comprehensive insights and automated monitoring.\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}