Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Headless Claude: STDIN Prompts to JSON Responses

Hello! Welcome back.

In our last lesson, we built a powerful automated Test-Driven Development workflow using a shell script. That was a practical, hands-on demonstration of what's possible when you run Claude Code programmatically. You've already seen the power of "headless" mode in action.

Today, we'll take a step back to systematically master the fundamental concepts that made that script possible. We will do a deep dive into non-interactive (headless) mode, exploring how to execute commands, pass data using standard input (stdin), and most importantly, how to get and parse structured JSON responses. Mastering this is the cornerstone of building any serious automation or CI/CD integration with Claude Code.

From Interactive REPL to Headless Automation

You're already an expert in using Claude Code interactively. Headless mode takes that same power and makes it available to your scripts, treating the Claude agent like any other command-line utility. This "Unix philosophy"—small, composable tools that work together—is what makes headless mode so versatile.

Let's begin with a video from Anthropic that introduces this concept.

Building headless automation with Claude Code | Code w/ Claude

This clip from 'Building headless automation with Claude Code' frames the Claude Code SDK as a 'Unix-like tool' that you can pipe data into and out of, which is the core idea behind headless mode.

Watch from 01:43 to 04:38. Pay close attention to: The concept of using Claude as a building block in pipelines. The basic command-line invocation. (Note: The video uses claude-b, an older syntax. We will use the modern claude -p.) The introduction of the --output-format JSON flag, which is the key to making Claude's output machine-readable for your scripts.

Running Claude Code Non-Interactively

As you just saw, the key to headless mode is running Claude from the command line in a way that it executes a single task and then exits, printing the result to standard output. This is perfect for scripting.

Let's consult the official documentation to see the modern syntax and basic usage patterns.

Run Claude Code programmatically - Claude Code Docs

The official documentation, 'Run Claude Code programmatically', provides the definitive guide to using headless mode. We'll focus on the core commands.

Read the introduction and the 'Basic usage' section. These sections introduce the -p (or --print) flag, which is the modern equivalent of the -p flag used for non-interactive execution.

To summarize, you can run any prompt non-interactively using the -p flag:




# Ask a question about the current project context
claude -p "What does the auth module do?"

More powerfully, you can pipe data into it via stdin. This allows you to chain Claude with other command-line tools. Given your experience with building REST APIs and working with server logs, this pattern should feel very familiar.




# Pipe the contents of a log file to Claude for analysis
cat server.log | claude -p "Summarize the critical errors in these logs."




# Chain it with other tools, like 'git'
git diff | claude -p "Based on this diff, write a concise summary of the changes for a PR description."

Getting Structured, Parsable Output

While plain text output is useful, it's not reliable for automation. A script needs to know where the AI's answer begins and ends, and it needs access to metadata like cost and session information. This is where structured JSON output becomes essential.

Let's look at the documentation and a practical guide on how to enable and use this feature.

Run Claude Code programmatically - Claude Code Docs

This section of the documentation, 'Get structured output', details the --output-format flag. This is the most important flag for building robust automations.

Read the 'Get structured output' section. Focus on the difference between the json and stream-json formats and the introduction to using jq to parse the output.

The --output-format json flag changes the output from a simple string to a JSON object. For a more detailed look at the structure of this object, the Introl Blog provides an excellent breakdown.

Claude Code CLI: The Definitive Technical Reference | Introl Blog

The 'Claude Code CLI: The Definitive Technical Reference' provides a practical example of the JSON output structure.

Find the subsection 'Non-Interactive Mode'. Examine the example JSON output provided. Notice the useful fields like total_cost_usd, duration_ms, and session_id alongside the main result.

The typical JSON response looks something like this:

{
 "type": "result",
 "subtype": "success",
 "total_cost_usd": 0.0034,
 "is_error": false,
 "duration_ms": 2847,
 "result": "The text response from Claude goes here...",
 "session_id": "abc-123-def"
}

This structure is ideal for scripting. You can now reliably extract the AI's response, check for errors, and log metadata like cost and execution time.

Parsing JSON Responses with jq

To make use of this JSON output in a shell script, you need a tool to parse it. The standard and most powerful tool for this is jq. With your background in backend development and scripting, you'll find jq to be an indispensable part of your automation toolkit.

Let's see how the documentation recommends using it.

Claude Code CLI: The Definitive Technical Reference | Introl Blog

This guide provides clear examples of using jq to extract specific fields from the JSON response.

Review the 'JSON Output Parsing' section for concise examples of how to extract different fields like the result, cost, and session ID into shell variables.

Here’s a practical example you can run in your terminal. Let's ask Claude to generate a JSON string, and then we'll use jq to parse both the outer response and the inner JSON result.

  1. Run the command and save the output to a variable:

    
    
    
    # Prompt Claude to generate a JSON object and capture the full response
    RESPONSE=$(claude -p "Generate a JSON object with two keys: 'file_path' set to 'src/utils/helpers.py' and 'line_count' set to 150." --output-format json)
    
  2. Parse the response with jq:

    
    
    
    # Extract the 'result' field, which contains the JSON string Claude generated
    # The -r flag removes the quotes from the output string
    RESULT_JSON=$(echo "$RESPONSE" | jq -r '.result')
    
    
    
    
    # Now parse the nested JSON string to get the specific values
    FILE_PATH=$(echo "$RESULT_JSON" | jq -r '.file_path')
    LINE_COUNT=$(echo "$RESULT_JSON" | jq '.line_count')
    
    
    
    
    # You can also grab metadata from the original response
    COST=$(echo "$RESPONSE" | jq '.total_cost_usd')
    
    echo "Analysis complete. Cost: \$${COST}"
    echo "File: ${FILE_PATH}"
    echo "Lines: ${LINE_COUNT}"
    

This two-step parsing process is very common: first, you unwrap the Claude Code response envelope with jq; second, you parse the actual content of the result field. This gives you full programmatic control over the AI's output.

Conclusion

In this lesson, we've solidified your understanding of the fundamentals of headless Claude Code. You've gone from using it in a single, complex script to understanding each component part in detail.

Key Takeaways:

  • Non-Interactive Execution: Use claude -p "<prompt>" to run Claude from any script.
  • Piping with stdin: Claude Code acts like a standard Unix tool, allowing you to pipe data from other commands (e.g., cat, git diff) for analysis.
  • Structured Output is Essential: The --output-format json flag provides a machine-readable response envelope.
  • Parse with jq: Use jq in your scripts to reliably extract the result text, cost, session ID, and other critical metadata from the JSON response.

Next Up:

Now that you've mastered the building blocks of headless mode—invoking Claude, passing data, and parsing the output—we'll assemble them into a more sophisticated workflow. In the next lesson, you will build a shell script that automates a multi-step task, controlling tool access along the way, bringing you one step closer to full CI/CD integration.

Can't find a good explanation? Sign up and we'll make it for you

Sign up