Last active
February 27, 2026 19:44
-
-
Save Seklfreak/99ec4c357017eb9943de3ddaa2e588d0 to your computer and use it in GitHub Desktop.
Analyze claude cost
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "encoding/csv" | |
| "fmt" | |
| "io" | |
| "os" | |
| "sort" | |
| "strconv" | |
| "strings" | |
| ) | |
| func extractUsername(apiKey string) string { | |
| // Strip prefix "claude_code_key_" | |
| s := strings.TrimPrefix(apiKey, "claude_code_key_") | |
| // Strip the last "_XXXX" suffix (4-char random segment) | |
| idx := strings.LastIndex(s, "_") | |
| if idx != -1 { | |
| s = s[:idx] | |
| } | |
| // "sebastian.winkler" -> "Sebastian Winkler" | |
| parts := strings.Split(s, ".") | |
| for i, p := range parts { | |
| if len(p) > 0 { | |
| parts[i] = strings.ToUpper(p[:1]) + p[1:] | |
| } | |
| } | |
| return strings.Join(parts, " ") | |
| } | |
| func main() { | |
| if len(os.Args) < 2 { | |
| fmt.Fprintln(os.Stderr, "Usage: analyze_costs <file.csv>") | |
| os.Exit(1) | |
| } | |
| f, err := os.Open(os.Args[1]) | |
| if err != nil { | |
| fmt.Fprintf(os.Stderr, "Error opening file: %v\n", err) | |
| os.Exit(1) | |
| } | |
| defer f.Close() | |
| r := csv.NewReader(f) | |
| header, err := r.Read() | |
| if err != nil { | |
| fmt.Fprintf(os.Stderr, "Error reading header: %v\n", err) | |
| os.Exit(1) | |
| } | |
| colIndex := func(name string) int { | |
| for i, h := range header { | |
| if h == name { | |
| return i | |
| } | |
| } | |
| return -1 | |
| } | |
| apiKeyCol := colIndex("api_key") | |
| costCol := colIndex("cost_usd") | |
| if apiKeyCol == -1 || costCol == -1 { | |
| fmt.Fprintln(os.Stderr, "Required columns 'api_key' or 'cost_usd' not found") | |
| os.Exit(1) | |
| } | |
| totals := make(map[string]float64) | |
| for { | |
| record, err := r.Read() | |
| if err == io.EOF { | |
| break | |
| } | |
| if err != nil { | |
| fmt.Fprintf(os.Stderr, "Error reading row: %v\n", err) | |
| continue | |
| } | |
| user := extractUsername(record[apiKeyCol]) | |
| cost, err := strconv.ParseFloat(record[costCol], 64) | |
| if err != nil { | |
| continue | |
| } | |
| totals[user] += cost | |
| } | |
| type entry struct { | |
| user string | |
| cost float64 | |
| } | |
| rows := make([]entry, 0, len(totals)) | |
| var grand float64 | |
| for user, cost := range totals { | |
| rows = append(rows, entry{user, cost}) | |
| grand += cost | |
| } | |
| sort.Slice(rows, func(i, j int) bool { | |
| return rows[i].cost > rows[j].cost | |
| }) | |
| fmt.Printf("%-25s %12s %8s\n", "User", "Cost (USD)", "Share") | |
| fmt.Println(strings.Repeat("-", 48)) | |
| for _, e := range rows { | |
| share := e.cost / grand * 100 | |
| fmt.Printf("%-25s %12.2f %7.1f%%\n", e.user, e.cost, share) | |
| } | |
| fmt.Println(strings.Repeat("-", 48)) | |
| fmt.Printf("%-25s %12.2f %8s\n", "TOTAL", grand, "") | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment