Drop-in Claude Code Stop hook that ships per-user, per-project usage traces to Langfuse.
Project description
claude-code-langfuse-hook
See what your team is doing in Claude Code.
This is a small tool that hooks into Claude Code and sends every turn to Langfuse — so you can see who used Claude, on which project, how many tokens, and how much it cost.
You get:
- One trace per turn, grouped by session.
- Tagged with the project name and model.
- Attributed to the engineer's git email.
- Token counts (including prompt-cache hits) so Langfuse can show real cost.
Before you start a couple of disclaimers
This works with Claude Code CLI only. It does not work with Claude on the web (claude.ai) or the Claude desktop app. The desktop app runs in its own sandbox and doesn't expose hooks, so there's no way for this package to plug in.
The costs are estimates. Numbers are computed from token counts using Anthropic's published API base rates. They won't match your official Anthropic invoice down to the cent. Treat them as a way to get a feel for usage by team, project, or person — not as a billing source of truth.
Step 1 — Get a Langfuse instance
You need a Langfuse somewhere to send the data to. Three options:
- Try it on your laptop — run Langfuse locally with Docker Compose: langfuse.com/self-hosting/local.
- Self-host on a VM — deploy on any VM or Kubernetes cluster: langfuse.com/self-hosting.
- Use the cloud — sign up at cloud.langfuse.com.
Once it's running, create a project inside Langfuse and copy the public key, secret key, and base URL. You'll paste them in below.
Step 2 — Install the hook
pipx install claude-code-langfuse-hook # recommended
# or
pip install --user claude-code-langfuse-hook
Then plug it into Claude Code:
claude-langfuse install
This adds one line to ~/.claude/settings.json so every Claude Code
session triggers the hook. You only do this once per machine.
Step 3 — Turn it on for a project
Add five lines to your project's .env file:
CC_TRACE_TO_LANGFUSE=true
CC_PROJECT_NAME=my-project
CC_LANGFUSE_BASE_URL=https://langfuse.example.com
CC_LANGFUSE_PUBLIC_KEY=pk-lf-...
CC_LANGFUSE_SECRET_KEY=sk-lf-...
Don't want to type them? Run claude-langfuse init and copy-paste.
That's all. The next time you use Claude Code in this project, traces will show up in Langfuse.
Tip: real OS environment variables beat the
.envfile. So you can commit the non-secret lines to.env.exampleand let direnv, doppler, 1Password, or Vault inject the keys.
Note on the
CC_prefix: every variable starts withCC_so this hook doesn't accidentally pick up keys meant for some other Langfuse-using app running in the same shell. PlainLANGFUSE_PUBLIC_KEYis ignored on purpose.
Commands you can run
| Command | What it does |
|---|---|
claude-langfuse install |
Hooks into Claude Code (one time per machine) |
claude-langfuse uninstall |
Unhooks it |
claude-langfuse init |
Prints the .env block to copy-paste |
claude-langfuse status |
Tells you if everything is wired up |
claude-langfuse test |
Sends a fake trace to check the connection |
claude-langfuse hook |
Internal — Claude Code runs this for you |
What shows up in Langfuse
For every Claude Code turn, you'll see one trace shaped like this:
trace "Claude Code - Turn N" session, user, project, model
└─ span "Claude Code - Turn N" the user's prompt
├─ generation "Claude Response" Claude's reply + token usage
└─ tool "Tool: <name>" one per tool Claude called
A few things to know:
- Who did it.
user_idis taken fromgit config user.email. If that's missing, the hook falls back to your OS username and logs a warning. - Grouping. All turns from the same Claude Code session share a
session_id, so they group nicely in Langfuse's Sessions view. - Big payloads get trimmed. Anything over
CC_LANGFUSE_MAX_CHARS(default 20,000) is truncated. The trim records the original length and a sha256 hash so you can prove the original existed.
Token usage and cost
Claude charges four different rates depending on what kind of input tokens you used — especially when prompt caching is on. The hook sends all four to Langfuse:
| Token kind | Roughly costs |
|---|---|
input_tokens |
base input |
output_tokens |
base output |
cache_creation_input_tokens |
1.25× base input |
cache_read_input_tokens |
0.1× base input |
Langfuse's built-in price table already knows these names, so you'll see accurate dollar amounts without any extra setup.
Extended thinking
When Claude is in "extended thinking" mode, the reasoning isn't shown in the main input/output (which would be noisy). Instead it's tucked into the generation's Metadata → thinking tab in Langfuse so you can read it if you want to.
All the variables
| Var | Required? | What it does |
|---|---|---|
CC_TRACE_TO_LANGFUSE |
yes | Set to true to turn tracing on for this project |
CC_PROJECT_NAME |
yes | The project name tagged on every trace |
CC_LANGFUSE_BASE_URL |
yes | Your Langfuse URL |
CC_LANGFUSE_PUBLIC_KEY |
yes | Langfuse public key |
CC_LANGFUSE_SECRET_KEY |
yes | Langfuse secret key |
CC_LANGFUSE_DEBUG |
no | Set to true for chatty logs |
CC_LANGFUSE_MAX_CHARS |
no | Trim long content above this size (default 20000) |
CC_TRACE_SUBAGENTS |
no | Set to true to also trace subagents (off by default) |
Things that might go wrong
| What you see | Why | What to do |
|---|---|---|
| No traces in Langfuse | The hook isn't installed | Run claude-langfuse install, then claude-langfuse status to confirm |
status says config is incomplete |
A required variable is missing | Run claude-langfuse init and fill in the five CC_* vars |
user_id is your OS username, not your email |
Git doesn't know your email | git config user.email you@example.com |
| Tool calls show up as plain spans | Your Langfuse is older than v3.10 | Upgrade Langfuse |
Hook fails with socksio import error |
You use a SOCKS proxy | pipx inject claude-code-langfuse-hook httpx[socks] |
| Cost is $0 | Langfuse doesn't have a price for the model | In Langfuse: Settings → Models → add the model with prices for input, output, cache_creation_input, cache_read_input |
| Hook times out (over 15s) | Your transcript is huge | Raise the timeout in ~/.claude/settings.json, or lower CC_LANGFUSE_MAX_CHARS |
Working on this package
git clone <repo>
cd pypi-package
pip install -e . pytest
pytest
License
MIT.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file claude_code_langfuse_hook-0.1.0.tar.gz.
File metadata
- Download URL: claude_code_langfuse_hook-0.1.0.tar.gz
- Upload date:
- Size: 35.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a52abe59756e1d531cc1b62f260e70a30a5f1fc34c45b9feeda932201c30d380
|
|
| MD5 |
ff4dccd4c8c8bb75386f8efe745e9223
|
|
| BLAKE2b-256 |
8e22003d2d77cb897e6bb874c750f9b4c1d4291fb56cd3491631981b490d2c39
|
File details
Details for the file claude_code_langfuse_hook-0.1.0-py3-none-any.whl.
File metadata
- Download URL: claude_code_langfuse_hook-0.1.0-py3-none-any.whl
- Upload date:
- Size: 25.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dabfc0c8dd2af83dc5315c8f477d3106ce14d8c24710ce22cb1883bea62e59ef
|
|
| MD5 |
a53f2bf884e3162f242516a0bd596863
|
|
| BLAKE2b-256 |
4d4ce2f33d7a202e59c030fefa05eed7f44262bf33a608518973d2d07e0b0527
|