claude-code·8 min read·

Headless Claude Code: claude -p scripting for UK indie hackers (2026)

Most UK indie hackers run Claude Code as a chat. The -p flag turns it into a one-shot CLI you can drop into bash, cron, GitHub Actions, or a Vercel cron job. Here is the headless playbook: piping, JSON output, allowed tools, max turns, and a 50-line weekend scraper.

Headless Claude Code: claude -p scripting for UK indie hackers (2026)

Open Claude Code, type a prompt, watch it work. That is how most UK indie hackers meet the tool, and that is the right way to start. It is also a ceiling. Interactive Claude Code needs a human in front of it. Every prompt is one you typed. Every approval is one you clicked. The moment you walk away, the agent stops.

The way past that ceiling is one flag: -p. The print flag turns Claude Code from a chat into a one-shot, headless command. Same engine, same tools, same model — but now it accepts a prompt, runs it to completion, and exits. Which means it is no longer a chat tool. It is a Unix binary, and Unix binaries go anywhere: cron, GitHub Actions, Vercel cron, a Windows scheduled task, a Docker container, an iOS Shortcut. For a UK indie hacker who wants Claude Code to work while they sleep, -p is the single most important flag in the CLI.

What headless mode actually is

In interactive mode, claude opens a REPL. It loads CLAUDE.md, takes your prompt, runs the tool loop, asks for permissions, streams the answer, then waits for the next message.

In headless mode — claude -p "<prompt>" — it does steps one through five, prints the final answer, and exits. No REPL. No permission prompts (unless you opt in). Exit code zero on success, non-zero on failure, output on stdout, errors on stderr. It behaves like any other CLI you would chain with && or pipe through jq.

claude -p "What is two plus two? Reply with just the number."
# 4

That is the whole interface. What makes it powerful is everything you can put around it.

The walkthrough: from one-liner to scripted pipeline

1. The plain one-shot

claude -p "Read README.md and tell me in one sentence what this project does."

Runs in the current working directory, picks up the CLAUDE.md if there is one, reads the file, returns one sentence. This alone is enough to drop a "summarise this PR" step into a bash script.

2. Piping stdin

claude -p accepts stdin. Pipe anything into it.

cat error.log | claude -p "Summarise the top three errors in this log."

Or from a curl:

curl -s https://example.com/feed.json | claude -p "List the five most recent UK-relevant items as bullets."

This is the single most useful pattern in the headless playbook. Anything you can produce on stdout, Claude Code can turn into a summary, a JSON object, a markdown report, or a slack-shaped string.

3. Structured output with --output-format json

For anything you need to parse, pass --output-format json:

claude -p "Summarise README.md in three bullets." --output-format json | jq '.result'

The JSON envelope includes the final response text, the tool calls the agent made, and a usage block with input and output tokens. That last bit is gold for a UK indie hacker keeping an eye on the API bill — you can sum it up across a week of cron runs and know exactly what you spent.

4. Restricting tools with --allowedTools

By default, headless claude -p has access to the full toolset (Read, Write, Edit, Bash, Grep, Glob, WebFetch, plus any MCP tools wired in). For a scheduled job, that is almost always too much. Lock it down:

claude -p \
  --allowedTools "Read,Grep,Glob" \
  "Find all TODO comments in src/ and print them grouped by file."

The agent now physically cannot run Bash, cannot write files, cannot fetch URLs. If the prompt asks it to, it will fail loudly rather than do unsafe work. For unattended jobs, the rule of thumb is: list every tool you need, then remove one and see if it still works.

5. Capping the loop with --max-turns

The agent loop runs until the agent decides it is done or it hits the turn limit. For headless jobs, always set the turn limit:

claude -p \
  --max-turns 4 \
  --allowedTools "Read,Write" \
  "Summarise notes/today.md into notes/today-summary.md."

Four turns is plenty for a one-step job. Twelve for a multi-step one. Setting --max-turns 50 on a script is how a quiet bug becomes a GBP 30 surprise on the API bill.

6. Working directory and exit codes

Pass --cwd or simply cd first. The working directory matters because it determines which CLAUDE.md the agent loads and what files it can see.

cd /home/tim/projects/morning-brief
claude -p "Run the weekly digest."
echo "exit: $?"

Exit code zero on success. Non-zero on failure. Check it in any script that schedules unattended runs — silent failure is the enemy.

7. Permissions: --permission-mode

For unattended runs, you need an explicit permission posture. Three useful values:

# Default: prompt for risky actions (no good for headless)
claude -p "..."

# Skip permission prompts for tools in --allowedTools
claude -p --permission-mode acceptEdits --allowedTools "Read,Write" "..."

# Plan only - the agent thinks but does nothing
claude -p --permission-mode plan "..."

For a cron job, acceptEdits paired with a tight --allowedTools list is the standard combination.

UK builder angle: hosts, pricing, time zones

Where headless Claude Code lives, for a UK indie hacker, in 2026:

  • Windows Task Scheduler. A PC that stays on is the cheapest host going. Schedule a .bat or .ps1 that invokes claude -p. Logs to a folder. Zero monthly cost.
  • Linux / macOS cron. Same shape, friendlier syntax. crontab -e, drop in 0 7 * * 1-5 cd /home/tim/agent && claude -p "..." > log.txt 2>&1, done.
  • GitHub Actions. Free for public repos, generous free tier for private ones. Pair with the anthropic-ai/setup-claude-code action (or install the CLI in a step) and you have a hosted, logged, version-controlled headless agent. Best fit for jobs that touch the repo itself.
  • Vercel cron. Free tier covers daily runs. Caveats: a 60-second execution limit on the Hobby plan and a Node-only runtime. For short summarisation jobs it is fine; for long agent loops, host elsewhere.
  • Railway one-shot. Around GBP 4 to GBP 10/mo for an always-on worker. EU regions help with GDPR if you are touching personal data.

Pricing in practice. A small headless run — a single file summary, one tool call — lands at 1p to 5p. A daily 20k-token brief: roughly 5p to 30p per run. A month of weekday runs: GBP 1 to GBP 6. Set a daily spend cap on your Anthropic key before you schedule anything. The cap lives in the Anthropic console under API Keys and Limits. Thirty seconds, peace of mind for the next year.

Time zones matter more than you think. Cron and Vercel default to UTC; a UK builder usually wants Europe/London so summer time tracks correctly. Set TZ=Europe/London in your environment, or schedule by UTC and do the maths once.

What can go wrong, and how to plan for it

Runaway loops. A poorly bounded agent in a tight loop can chew tokens at speed. Always set --max-turns. Always set a daily spend cap on the Anthropic key. Log every run.

Silent permission denials. If you forgot to add a tool to --allowedTools the agent will sometimes finish "successfully" without doing the work, because it could not call the tool it needed. Always inspect the JSON output for tool calls — if you expected three and got zero, something is off. A two-line jq check at the end of the script catches this.

Stale CLAUDE.md drift. Headless runs hide CLAUDE.md from view because nothing prints it. If the file gets out of date, your scheduled agent will silently make worse decisions for weeks. Treat CLAUDE.md as part of the deployable — version-control it, review it once a fortnight, and prune anything the agent no longer needs. (The CLAUDE.md guide goes deep on what belongs in it.)

API key leaks in logs. If you log the full environment, you log the key. Never echo $ANTHROPIC_API_KEY. Never include env in a debug dump that gets posted publicly. Rotate the key immediately if you suspect it ended up in a public CI log.

Time-zone drift on cron. A 7am UK job that mysteriously starts running at 6am in October is on UTC and was never told about BST. Pin TZ=Europe/London in the cron environment.

Output truncation. A long agent reply piped through jq can fail silently if you forget that some shells truncate at a few hundred kilobytes. For anything that might return a long brief, write the raw JSON to a file first (> out.json), parse from the file, and keep the file as a debug artefact. Disk is cheap; lost output is not.

The "it worked locally" trap. A claude -p invocation that flies on your UK desktop can fail in CI because the working directory is different, CLAUDE.md is missing, or the ANTHROPIC_API_KEY secret is not exposed to the runner. Always smoke-test the exact same command in the target environment before relying on it. For GitHub Actions, that means committing a workflow that runs the command on a manual workflow_dispatch trigger; for Vercel cron, hitting the endpoint manually before the first scheduled fire.

Prompt drift over time. A scheduled job whose prompt has been steady for six months can start producing worse output after a model version bump or a context-shape change. Build in a weekly review where you eyeball the latest run; if the quality has slipped, the fix is usually a short prompt rewrite rather than a model swap.

The weekend build: a 50-line UK landlord-listings summariser

Here is a concrete, shippable, side-hustle-flavoured job: a bash loop that pulls UK landlord-listing URLs from a Supabase queue, summarises each with claude -p, writes the summary back to Supabase, and emails you a daily digest.

#!/usr/bin/env bash
set -euo pipefail

SUPABASE_URL="https://your-project.supabase.co"
SUPABASE_KEY="$SUPABASE_SERVICE_KEY"
TODAY=$(date -u +%F)

# 1. Pull pending listings
listings=$(curl -s \
  -H "apikey: $SUPABASE_KEY" \
  -H "Authorization: Bearer $SUPABASE_KEY" \
  "$SUPABASE_URL/rest/v1/listings?status=eq.pending&select=id,url")

# 2. Loop, summarise, write back
echo "$listings" | jq -c '.[]' | while read -r row; do
  id=$(echo "$row" | jq -r '.id')
  url=$(echo "$row" | jq -r '.url')

  summary=$(curl -s "$url" | claude -p \
    --max-turns 3 \
    --allowedTools "" \
    --output-format json \
    "Summarise this UK landlord listing in 3 bullets. Note the city, the rent in GBP, and any red flags." \
    | jq -r '.result')

  curl -s -X PATCH \
    -H "apikey: $SUPABASE_KEY" \
    -H "Authorization: Bearer $SUPABASE_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"summary\": $(echo "$summary" | jq -Rs .), \"status\": \"done\"}" \
    "$SUPABASE_URL/rest/v1/listings?id=eq.$id"
done

echo "Daily run $TODAY complete"

Schedule it with cron at 7am, log to a file, set a daily Anthropic spend cap of GBP 2 — and you have a headless researcher. Total monthly cost: probably under GBP 5. Total setup time: a weekend afternoon.

If you want a UK opportunity that fits this exact shape — a niche where a headless summariser turns into a small, defendable product — start with this week's free report. The research is done. The headless scaffolding is now yours.

Frequently asked

What does the -p flag do in Claude Code?

The -p (print) flag tells Claude Code to run a single prompt non-interactively, print the result to stdout, and exit. No REPL, no chat loop, no waiting for input. It turns the CLI into a normal Unix command you can pipe, redirect, schedule, and chain like any other binary.

Can I use claude -p inside a cron job or GitHub Actions?

Yes. claude -p is the supported way to run Claude Code in any non-interactive environment: Linux cron, Windows Task Scheduler, GitHub Actions, Vercel cron, Railway, Fly machines, Docker containers. Anywhere bash and Node 20 run, claude -p runs. Most UK builders pair it with a cron expression and a logfile.

How do I get structured output instead of free text?

Pass --output-format json and Claude Code emits a structured JSON envelope with the final text, tool calls, and usage stats. Pipe it into jq or parse it in your script. For a daily report run, JSON output is what separates a fragile grep-the-output script from a reliable pipeline.

Is it safe to give a headless claude -p run shell or write access?

It is as safe as you make it. Use --allowedTools to whitelist only the tools the job needs, set --max-turns to cap the agent loop, and run inside a working directory that contains nothing you would mind losing. For unattended runs, also set a hard spend cap on your Anthropic key in the console.

Does claude -p use my Claude Code Pro plan or my API key?

By default, claude -p uses whichever auth your local Claude Code install is using. If you are logged in with Pro, it runs against Pro. If you have ANTHROPIC_API_KEY set, it runs against the API. For unattended servers and CI, you almost always want the API key path: explicit, billable, and not tied to a desktop login.

Related reading

More UK-focused guides from the IdeaStack blog.

Error tracking for your live v1 (UK SaaS, Claude Code 2026)

Error tracking for your live v1 (UK SaaS, Claude Code 2026)

Your v1 is live, three founding members are using it, and the next outage is a question of when, not if. This is the right-sized observability layer for a brand-new UK SaaS: the three things actually worth watching, how to wire error tracking and a money-path alarm into a Next.js app with Claude Code in one session, source maps so a stack trace points at real code, a simple uptime ping, and the discipline to stop there instead of building enterprise monitoring for three customers.

Read more →

Your first feature build session after launch (UK SaaS, Claude Code 2026)

Your first feature build session after launch (UK SaaS, Claude Code 2026)

Your v1 is live and three founding members are using it. The feedback is rolling in faster than you can build. This is the UK indie hacker guide to your first feature build session after launch: how to read founding feedback for the one signal that matters, pick the single feature that removes the most friction, scope it to one Claude Code session, build and test it against the money path, and ship it the same day with a 'you asked for this' note that turns a paying founder into an evangelist.

Read more →

Ship your v1 to founding members (UK SaaS, Claude Code 2026)

Ship your v1 to founding members (UK SaaS, Claude Code 2026)

Scaffold done, auth and billing wired. Now you ship the v1 to the founding members who already paid. This is the UK indie hacker go-live checklist: the pre-launch checks that stop launch day going sideways, how to onboard three pre-paid founders by hand, the feedback loop that tells you what to build next, and the first iteration cadence that turns a founding cohort into retained, paying customers instead of three refunds.

Read more →

Supabase auth + Stripe billing for a UK SaaS (Claude Code, 2026)

Supabase auth + Stripe billing for a UK SaaS (Claude Code, 2026)

Your skeleton is live. Now your founding members need two things: a way to log in and a way to keep paying you. This is the UK indie hacker walkthrough for wiring Supabase auth and Stripe subscriptions with Claude Code - the auth flow, the subscription model, the one webhook that actually matters, and the UK-specific decision every US tutorial skips: do you take payments through Stripe directly and own your VAT, or go through a Merchant of Record like Paddle and hand the VAT headache away?

Read more →

Scaffold your validated v1 with Claude Code (UK, 2026)

Scaffold your validated v1 with Claude Code (UK, 2026)

You did the hard part. You ran the smoke test, made the Mom Test calls, and three founding members have already paid. Now you have to build the thing. This is the UK indie hacker walkthrough for turning a validated idea into a working app skeleton in a single Claude Code session: the PRD that anchors every decision, the CLAUDE.md that keeps the agent honest, plan mode before any code, the first five files, and why you deploy to Vercel on day one before you have a single feature.

Read more →

The newsletter

One UK business idea, every Thursday

By Tim Bland. Free.