{
  "id": 13686902,
  "title": "Parsing Claude Code Session Transcripts in Python: Turns, Tool Calls, and Results",
  "url": "https://urgent.news/2026/10/11/parsing-claude-code-session-transcripts-in-python-turns-tool-calls",
  "topic": "ai",
  "section": "AI",
  "published": "2026-10-11T09:30:29.000Z",
  "source": {
    "name": "Dev.to",
    "slug": "dev-to",
    "url": "https://dev.to/royalpinto007/parsing-claude-code-session-transcripts-in-python-turns-tool-calls-and-results-3p3l"
  },
  "original_language": "en",
  "account": "Every time I run Claude Code, it silently saves the entire conversation session to a text file without requiring any additional configuration. This single characteristic has enabled me to develop lightweight analysis tools directly on top of my own agent runs, without needing to directly interact with the agent itself. In this guide, I will demonstrate how to parse those files and extract meaningful information: the turns, tool calls, and the results produced by those calls.\n\nClaude Code stores one file per session in the following location: ~/.claude/projects/project-slug/session-id.jsonl. The slug represents your project's path, with slashes replaced by dashes. Each file is in JSONL format, meaning each line is a separate JSON object that gets appended as the session progresses. This append-only nature is crucial later on.\n\nTo locate these files, you can use the following Python function:\n\n```python\nfrom pathlib import Path\n\ndef find_sessions(root: Path | None = None) -> list[Path]:\nroot = root or (Path.home() / \".claude\" / \"projects\")\nif not root.exists():\nreturn []\nreturn sorted(root.glob(\"**/*.jsonl\"))\n```\n\nWhen reading these transcripts, it's important to handle the possibility of a torn final line, which can occur if Claude Code is still writing while you're reading. To avoid crashing your analysis, simply skip any incomplete lines.\n\nEach line in the transcript is not always a conversation turn. The 'type' field can contain values such as 'user', 'assistant', 'queue-operation', 'attachment', 'file-history-snapshot', 'mode', and other miscellaneous types. The two types you primarily care about for turns are 'user' and 'assistant'. Each of these types carries a 'message' object, which in turn contains a 'content' field that is a list of typed blocks. These blocks are where the interesting information resides.\n\nA 'text' block has the following structure: { \"type\": \"text\", \"text\": \"...\"} A 'tool_use' block represents a tool call, and its 'tool_result' block represents the result produced by that tool call. Thus, you can think of the data as follows: lines give you turns, content blocks within a turn provide text, tool calls, and results.\n\nTo extract the tool calls and results, you can use two separate functions. The first function, `tool_calls(path)`, collects all tool calls, while the second function, `tool_results(path)`, gathers all tool results. These results are then joined together. The pairing of a tool call with its corresponding result is achieved by matching the 'id' field in a 'tool_use' block with an 'id' field in a subsequent 'tool_result' block. This is the key to understanding the structure of the data.",
  "summary": "Every time I run Claude Code, it quietly writes the entire session to disk. No flag, no setup, no instrumentation. The file is already there when I want it. That single fact is the reason I have been able to build small analysis tools on top of my own agent runs without ever touching the agent itself. In this tutorial I want to show you how to read those files and pull real structure out of them:…",
  "key_points": [],
  "editors_take": null,
  "illustration": null,
  "coverage": {
    "outlets": 1,
    "also_reported_by": []
  },
  "ai_generated": true,
  "disclaimer": "Summaries, key points and the editor’s take are written by software from other outlets’ reporting and may contain errors — always check the linked original."
}