How to obtain token usage per request?

For analytics of the effectiveness of my system, i want to be able to collect token usage after every request processing finished, parent agent and subagents separately. It seems i need to collect these parameters:

  1. model
  2. duration_ms
  3. inputTokens
  4. cache_write
  5. cacheReadTokens
  6. outputTokens
  7. reasoningTokens

How could i retrieve this information from the hook on the request/session end?

https://cursortokens.vercel.app/ except for 2 and 7
see also: C:\Users\USER\.cursor\projects\PROJECT\agent-transcripts\TRANSCRIPT.jsonl

Thanks, but there is no usage info in the agent-transcripts and i’m not on position to call external 3rd party services.

Hi @Stanislav_Kostomakha I worked thru a couple examples scenarios with my Cursor agent, let me know if this makes sense!

Most of what you’re after is already available from hooks, though two of the fields on your list aren’t yet.

Token counts: the stop hook

stop fires when the agent loop finishes for a turn, and the payload carries token usage accumulated across every model call in that turn:

{
  "hook_event_name": "stop",
  "conversation_id": "a1b2c3d4-1111-4222-8333-444455556666",
  "generation_id": "f0e1d2c3-7777-4888-8999-aaaabbbbcccc",
  "model": "cursor-grok-4.6-high-fast",
  "model_id": "grok-4.6",
  "model_params": [{ "id": "effort", "value": "high" }, { "id": "fast", "value": "true" }],
  "status": "completed",
  "loop_count": 0,
  "input_tokens": 1180993,
  "output_tokens": 8146,
  "cache_read_tokens": 1007022,
  "cache_write_tokens": 173957
}

The same four token fields are also on afterAgentResponse.They aren’t listed on the hooks docs page yet, which is why they’re easy to miss, and I’ve let the team know so we can get that page updated.

Three things to handle in your collector:

  1. input_tokens is inclusive of cache_read_tokens and cache_write_tokens. In the payload above only 14 of the 1.18M input tokens were genuinely uncached. If you want a non overlapping breakdown, subtract the two cache figures from input_tokens and clamp at zero.
  2. The numbers are cumulative for the turn, not per message. stop and afterAgentResponse report identical values for the same generation_id, so pick one hook as your source of truth rather than summing both.
  3. The token fields are optional, so treat them as possibly absent rather than defaulting to zero.

model, model_id and model_params are on every hook payload, so you get the model dimension for free. That covers your items 1, 3, 4, 5 and 6.

Duration

There’s no per-turn duration on stop. The simplest approach is to record a timestamp in beforeSubmitPrompt and subtract it at stop. Both payloads carry generation_id, which changes with every user message, so it works well as the join key for a turn. If you also want coarser or finer numbers, sessionEnd has duration_ms for the whole session and postToolUse has duration per tool call.

Reasoning tokens

Not currently exposed through hooks.

Subagents

subagentStop gives you subagent_id, subagent_type, status, duration_ms, message_count, tool_call_count, modified_files and agent_transcript_path, but no token counts.

One correction on the transcript files mentioned above: those JSONL lines only contain {role, message}, so there’s no token data to fall back on there.

For team-wide analytics

Since you’re on an Enterprise team, your Cursor admin can pull the Admin API usage events endpoint. Every event includes the model, inputTokens, outputTokens, cacheWriteTokens, cacheReadTokens, the cost in cents, and a conversationId. It’s per model call rather than per turn and it’s the same data your billing is based on, so it’s a good cross-check against what you collect locally. Joining it to your hook events on the conversation ID should get you close to the full picture.

Thanks Kevin, using hook `afterAgentResponse` looks to be a simplest solution, but are these token counts including counts consumed by sub-agents?

This is not team-wide stats, i want to make agent reporting cases when it failed individual request if it consumed sensitive amount of tokens to possibly adapt rules/skills to prevent same mistake in future.

Hi @Stanislav_Kostomakha Those afterAgentResponse / stop token counts are the parent agent’s turn only. They do not include tokens used by subagents.

Subagent model calls are billed and reported as their own requests. subagentStop can tell you that a subagent ran (subagent_id, subagent_type, model, duration_ms, message_count, tool_call_count), but it does not currently include token fields. I would not use it as a spend signal.

For the case you described (flag a single request that burned a lot of tokens, then tighten rules or skills), the Admin API usage events endpoint is the better source if you need parent plus subagent usage together. Each event has the model, inputTokens, outputTokens, cacheWriteTokens, cacheReadTokens, and a conversationId. Sum the events that share that conversation ID to get the full cost of the user turn.

If a parent-only threshold is enough, afterAgentResponse is still the simplest hook. Just treat those numbers as parent-only, so a cheap parent with expensive subagents does not slip through.

Hi Kevin,

I’ve tried to create API key of type “Admin” in my profile (this is only type of key i can create there, not selectable), but getting response from API: `{“code”:“error”,“message”:“Invalid Team API Key”}` and this does not look like as good option anyway, because matching requests by time will make not reliable in cases when someone need to analyse things like “which of simultaneously run subagents consumed all this crazy amount of tokens!?”

May be it is possible to just make usage reporting uniform between agent and subagent calls to use it in hooks?