Skip to content

Commit 3e74f6c

Browse files
authored
Merge pull request anthropics#189 from anthropics/jkim/fix-timestamp-bucket-alignment
Align timestamps to bucket boundaries in Usage & Cost API cookbook
2 parents e8641d1 + a029bcc commit 3e74f6c

File tree

1 file changed

+56
-50
lines changed

1 file changed

+56
-50
lines changed

observability/usage_cost_api.ipynb

Lines changed: 56 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,14 @@
5151
},
5252
{
5353
"cell_type": "code",
54-
"execution_count": 5,
54+
"execution_count": 11,
5555
"id": "edd50a16",
5656
"metadata": {
5757
"colab": {
5858
"base_uri": "https://localhost:8080/"
5959
},
6060
"id": "edd50a16",
61-
"outputId": "eaa831da-2621-410a-92c3-25b853ee6ddf"
61+
"outputId": "68f38db3-48ee-429f-cd6e-2d2ae74e009a"
6262
},
6363
"outputs": [
6464
{
@@ -73,7 +73,7 @@
7373
"import os\n",
7474
"import requests\n",
7575
"import pandas as pd\n",
76-
"from datetime import datetime, timedelta\n",
76+
"from datetime import datetime, timedelta, time\n",
7777
"from typing import Dict, List, Optional, Any\n",
7878
"\n",
7979
"class AnthropicAdminAPI:\n",
@@ -115,10 +115,12 @@
115115
" try:\n",
116116
" client = AnthropicAdminAPI()\n",
117117
"\n",
118-
" # Simple test query\n",
118+
" # Simple test query - snap to start of day to align with bucket boundaries\n",
119119
" params = {\n",
120-
" 'starting_at': (datetime.utcnow() - timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ'),\n",
121-
" 'ending_at': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ'),\n",
120+
" 'starting_at': (datetime.combine(datetime.utcnow(), time.min) -\n",
121+
"timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ'),\n",
122+
" 'ending_at': datetime.combine(datetime.utcnow(),\n",
123+
"time.min).strftime('%Y-%m-%dT%H:%M:%SZ'),\n",
122124
" 'bucket_width': '1d',\n",
123125
" 'limit': 1\n",
124126
" }\n",
@@ -158,23 +160,23 @@
158160
},
159161
{
160162
"cell_type": "code",
161-
"execution_count": null,
163+
"execution_count": 12,
162164
"id": "f9b26143",
163165
"metadata": {
164166
"colab": {
165167
"base_uri": "https://localhost:8080/"
166168
},
167169
"id": "f9b26143",
168-
"outputId": "00e0f555-c79d-4892-8be4-2cc965259e06"
170+
"outputId": "c60b91c0-084d-4629-eddd-cf0fb941e042"
169171
},
170172
"outputs": [
171173
{
172174
"output_type": "stream",
173175
"name": "stdout",
174176
"text": [
175177
"📊 Usage Summary:\n",
176-
"Uncached input tokens: 3,420\n",
177-
"Output tokens: 43,662\n",
178+
"Uncached input tokens: 267,751\n",
179+
"Output tokens: 2,848,746\n",
178180
"Cache creation: 0\n",
179181
"Cache reads: 0\n",
180182
"Cache efficiency: 0.0%\n",
@@ -185,7 +187,7 @@
185187
"source": [
186188
"def get_daily_usage(client, days_back=7):\n",
187189
" \"\"\"Get usage data for the last N days.\"\"\"\n",
188-
" end_time = datetime.utcnow()\n",
190+
" end_time = datetime.combine(datetime.utcnow(), time.min)\n",
189191
" start_time = end_time - timedelta(days=days_back)\n",
190192
"\n",
191193
" params = {\n",
@@ -283,7 +285,7 @@
283285
"source": [
284286
"def get_daily_costs(client, days_back=7):\n",
285287
" \"\"\"Get cost data for the last N days.\"\"\"\n",
286-
" end_time = datetime.utcnow()\n",
288+
" end_time = datetime.combine(datetime.utcnow(), time.min)\n",
287289
" start_time = end_time - timedelta(days=days_back)\n",
288290
"\n",
289291
" params = {\n",
@@ -345,30 +347,22 @@
345347
"base_uri": "https://localhost:8080/"
346348
},
347349
"id": "SP4zefdtF0ft",
348-
"outputId": "251a98e6-94a0-48cc-a761-8471c0c946ee"
350+
"outputId": "a1daa8d0-f36f-474b-9967-6fe00a0efe52"
349351
},
350352
"id": "SP4zefdtF0ft",
351-
"execution_count": null,
353+
"execution_count": 13,
352354
"outputs": [
353355
{
354356
"output_type": "stream",
355357
"name": "stdout",
356358
"text": [
357359
"💰 Cost Summary:\n",
358-
"Total cost: $1.4447\n",
359-
"Average daily cost: $0.2408\n"
360+
"Total cost: $83.7574\n",
361+
"Average daily cost: $11.9653\n"
360362
]
361363
}
362364
]
363365
},
364-
{
365-
"cell_type": "markdown",
366-
"source": [],
367-
"metadata": {
368-
"id": "XQZx3F_vPpw3"
369-
},
370-
"id": "XQZx3F_vPpw3"
371-
},
372366
{
373367
"cell_type": "markdown",
374368
"id": "6ce76fc4",
@@ -393,14 +387,14 @@
393387
},
394388
{
395389
"cell_type": "code",
396-
"execution_count": 6,
390+
"execution_count": 14,
397391
"id": "87f1a7ac",
398392
"metadata": {
399393
"colab": {
400394
"base_uri": "https://localhost:8080/"
401395
},
402396
"id": "87f1a7ac",
403-
"outputId": "c22b2928-e200-4a0e-b3f5-db10cdf12bd7"
397+
"outputId": "3266318c-8af4-4647-9360-b133ffa82452"
404398
},
405399
"outputs": [
406400
{
@@ -414,14 +408,14 @@
414408
" claude-sonnet-4-20250514: 356,766 tokens\n",
415409
" claude-opus-4-20250514: 308,223 tokens\n",
416410
" claude-opus-4-1-20250805: 199,201 tokens\n",
417-
"Found 6 days of filtered usage data\n"
411+
"Found 7 days of filtered usage data\n"
418412
]
419413
}
420414
],
421415
"source": [
422416
"def get_usage_by_model(client, days_back=7):\n",
423417
" \"\"\"Get usage data grouped by model, handling pagination automatically.\"\"\"\n",
424-
" end_time = datetime.utcnow()\n",
418+
" end_time = datetime.combine(datetime.utcnow(), time.min)\n",
425419
" start_time = end_time - timedelta(days=days_back)\n",
426420
"\n",
427421
" params = {\n",
@@ -483,16 +477,19 @@
483477
" print(f\" No usage data found in the last {days_back} days\")\n",
484478
" print(\" 💡 Try increasing the time range or check if you have recent API usage\")\n",
485479
" else:\n",
486-
" for model, tokens in sorted(model_usage.items(), key=lambda x: x[1], reverse=True):\n",
480+
" for model, tokens in sorted(model_usage.items(), key=lambda x: x[1],\n",
481+
"reverse=True):\n",
487482
" print(f\" {model}: {tokens:,} tokens\")\n",
488483
"\n",
489484
" return model_usage\n",
490485
"\n",
491486
"def filter_usage_example(client):\n",
492487
" \"\"\"Example of filtering usage data.\"\"\"\n",
493488
" params = {\n",
494-
" 'starting_at': (datetime.utcnow() - timedelta(days=7)).strftime('%Y-%m-%dT%H:%M:%SZ'),\n",
495-
" 'ending_at': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ'),\n",
489+
" 'starting_at': (datetime.combine(datetime.utcnow(), time.min) -\n",
490+
"timedelta(days=7)).strftime('%Y-%m-%dT%H:%M:%SZ'),\n",
491+
" 'ending_at': datetime.combine(datetime.utcnow(),\n",
492+
"time.min).strftime('%Y-%m-%dT%H:%M:%SZ'),\n",
496493
" 'models[]': ['claude-3-5-sonnet-20241022'], # Filter to specific model\n",
497494
" 'service_tiers[]': ['standard'], # Filter to standard tier\n",
498495
" 'bucket_width': '1d'\n",
@@ -520,14 +517,14 @@
520517
},
521518
{
522519
"cell_type": "code",
523-
"execution_count": null,
520+
"execution_count": 18,
524521
"id": "d7cc5437",
525522
"metadata": {
526523
"colab": {
527524
"base_uri": "https://localhost:8080/"
528525
},
529526
"id": "d7cc5437",
530-
"outputId": "b2b1a80a-e13c-427d-ed9f-ccaa3e478eee"
527+
"outputId": "e7875fb4-09ae-44d2-f5fc-12a4cbdcce5a"
531528
},
532529
"outputs": [
533530
{
@@ -538,10 +535,9 @@
538535
" Page 1: 24 time buckets\n",
539536
" Page 2: 24 time buckets\n",
540537
" Page 3: 24 time buckets\n",
541-
" Page 4: 24 time buckets\n",
542-
" Page 5: 24 time buckets\n",
543-
"📊 Total retrieved: 120 time buckets\n",
544-
"📈 Total tokens across all data: 0\n"
538+
"✅ Complete: Retrieved all data in 3 pages\n",
539+
"📊 Total retrieved: 72 time buckets\n",
540+
"📈 Total tokens across all data: 1,336,287\n"
545541
]
546542
}
547543
],
@@ -586,11 +582,15 @@
586582
" print(f\"📊 Total retrieved: {len(all_data)} time buckets\")\n",
587583
" return all_data\n",
588584
"\n",
589-
"def large_dataset_example(client, days_back=30):\n",
585+
"def large_dataset_example(client, days_back=3):\n",
590586
" \"\"\"Example of handling a large dataset with pagination.\"\"\"\n",
587+
" # Use recent time range to ensure we have data\n",
588+
" start_time = datetime.combine(datetime.utcnow(), time.min) - timedelta(days=days_back)\n",
589+
" end_time = datetime.combine(datetime.utcnow(), time.min)\n",
590+
"\n",
591591
" params = {\n",
592-
" 'starting_at': (datetime.utcnow() - timedelta(days=days_back)).strftime('%Y-%m-%dT%H:%M:%SZ'),\n",
593-
" 'ending_at': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ'),\n",
592+
" 'starting_at': start_time.strftime('%Y-%m-%dT%H:%M:%SZ'),\n",
593+
" 'ending_at': end_time.strftime('%Y-%m-%dT%H:%M:%SZ'),\n",
594594
" 'bucket_width': '1h', # Hourly data for more buckets\n",
595595
" 'group_by[]': ['model'],\n",
596596
" 'limit': 24 # One day per page\n",
@@ -609,9 +609,9 @@
609609
"\n",
610610
" return all_buckets\n",
611611
"\n",
612-
"# Example usage\n",
612+
"# Example usage - use shorter time range to find recent data\n",
613613
"if client:\n",
614-
" large_dataset = large_dataset_example(client, days_back=14)"
614+
" large_dataset = large_dataset_example(client, days_back=3)"
615615
]
616616
},
617617
{
@@ -628,22 +628,22 @@
628628
},
629629
{
630630
"cell_type": "code",
631-
"execution_count": null,
631+
"execution_count": 16,
632632
"id": "f365296a",
633633
"metadata": {
634634
"colab": {
635635
"base_uri": "https://localhost:8080/"
636636
},
637637
"id": "f365296a",
638-
"outputId": "42120afa-d6a9-4789-fcfd-aaa1e9f19032"
638+
"outputId": "87b4a733-6f90-4b8d-a109-7a23281b2a4d"
639639
},
640640
"outputs": [
641641
{
642642
"output_type": "stream",
643643
"name": "stdout",
644644
"text": [
645-
"✅ Exported 6 rows to my_usage_data.csv\n",
646-
"✅ Exported 12 cost records to my_cost_data.csv\n"
645+
"✅ Exported 36 rows to my_usage_data.csv\n",
646+
"✅ Exported 72 cost records to my_cost_data.csv\n"
647647
]
648648
}
649649
],
@@ -654,9 +654,12 @@
654654
"def export_usage_to_csv(client, output_file=\"usage_data.csv\", days_back=30):\n",
655655
" \"\"\"Export usage data to CSV for external analysis.\"\"\"\n",
656656
"\n",
657+
" end_time = datetime.combine(datetime.utcnow(), time.min)\n",
658+
" start_time = end_time - timedelta(days=days_back)\n",
659+
"\n",
657660
" params = {\n",
658-
" 'starting_at': (datetime.utcnow() - timedelta(days=days_back)).strftime('%Y-%m-%dT%H:%M:%SZ'),\n",
659-
" 'ending_at': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ'),\n",
661+
" 'starting_at': start_time.strftime('%Y-%m-%dT%H:%M:%SZ'),\n",
662+
" 'ending_at': end_time.strftime('%Y-%m-%dT%H:%M:%SZ'),\n",
660663
" 'group_by[]': ['model', 'service_tier', 'workspace_id'],\n",
661664
" 'bucket_width': '1d'\n",
662665
" }\n",
@@ -721,9 +724,12 @@
721724
"def export_costs_to_csv(client, output_file=\"cost_data.csv\", days_back=30):\n",
722725
" \"\"\"Export cost data to CSV.\"\"\"\n",
723726
"\n",
727+
" end_time = datetime.combine(datetime.utcnow(), time.min)\n",
728+
" start_time = end_time - timedelta(days=days_back)\n",
729+
"\n",
724730
" params = {\n",
725-
" 'starting_at': (datetime.utcnow() - timedelta(days=days_back)).strftime('%Y-%m-%dT%H:%M:%SZ'),\n",
726-
" 'ending_at': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ'),\n",
731+
" 'starting_at': start_time.strftime('%Y-%m-%dT%H:%M:%SZ'),\n",
732+
" 'ending_at': end_time.strftime('%Y-%m-%dT%H:%M:%SZ'),\n",
727733
" 'group_by[]': ['workspace_id', 'description']\n",
728734
" }\n",
729735
"\n",

0 commit comments

Comments
 (0)