Claude API in GitHub Actions

Run Claude API calls inside GitHub Actions workflows. Automate code review, PR summarization, and test-failure triage with the Anthropic SDK in CI/CD pipelines.

💥 50p impulse-buy: Power Prompts PDF (first 10 buyers) 30 battle-tested Claude Code prompts · 8-page PDF · paste into CLAUDE.md and never re-type a prompt again · 50p impulse-buy, no commitment

Running Claude inside GitHub Actions lets you automate code review, PR description drafting, and test-failure triage without any external infrastructure. This guide shows the canonical patterns for 2026.

Prerequisites

# Add to your repo: Settings → Secrets → Actions
ANTHROPIC_API_KEY=sk-ant-...

Minimal workflow: summarize a PR diff

name: Claude PR Review
on:
  pull_request:
    types: [opened, synchronize]

permissions:
  contents: read
  pull-requests: write

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install Anthropic SDK
        run: pip install anthropic

      - name: Review PR with Claude
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          BASE_BRANCH: ${{ github.event.pull_request.base.ref }}
        run: |
          DIFF=$(git diff origin/$BASE_BRANCH...HEAD -- '*.py' '*.ts' '*.js' | head -c 12000)
          python - <<'EOF'
import anthropic, os, subprocess

diff = os.environ.get("DIFF", "")
client = anthropic.Anthropic()

msg = client.messages.create(
    model="claude-haiku-4-5-20251001",
    max_tokens=512,
    system="You are a code reviewer. Given a git diff, write 3-5 concise bullet points covering: bugs, security issues, performance, and readability. Be specific and actionable. If the diff is clean, say so in one line.",
    messages=[{"role": "user", "content": f"Review this diff:\n\n{diff}"}]
)
review = msg.content[0].text
pr_num = os.environ.get("GITHUB_REF", "").split("/")[2]
subprocess.run(
    ["gh", "pr", "comment", pr_num, "--body", review],
    env={**os.environ, "GH_TOKEN": os.environ["GH_TOKEN"]}
)
print(review)
EOF

Triage failing tests

      - name: Run tests
        id: tests
        run: pytest --tb=short 2>&1 | tee test-output.txt
        continue-on-error: true

      - name: Triage failures with Claude
        if: steps.tests.outcome == 'failure'
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          python - <<'EOF'
import anthropic, os

log = open("test-output.txt").read()[-6000:]  # last 6000 chars = most recent failures
client = anthropic.Anthropic()
msg = client.messages.create(
    model="claude-haiku-4-5-20251001",
    max_tokens=400,
    system="You are a CI assistant. Given pytest output, identify the root cause in 2-3 sentences and suggest the most likely fix. Focus on the first failure.",
    messages=[{"role": "user", "content": f"Pytest output:\n\n{log}"}]
)
print("::warning title=Claude CI Triage::" + msg.content[0].text.replace("\n", " "))
EOF

Auto-generate PR descriptions

name: Auto PR Description
on:
  pull_request:
    types: [opened]

permissions:
  pull-requests: write

jobs:
  describe:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - run: pip install anthropic
      - name: Generate description
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PR_NUM: ${{ github.event.pull_request.number }}
          BASE: ${{ github.event.pull_request.base.ref }}
        run: |
          python - <<'EOF'
import anthropic, os, subprocess

diff = subprocess.check_output(
    ["git", "diff", f"origin/{os.environ['BASE']}...HEAD"],
    text=True
)[:10000]
commits = subprocess.check_output(
    ["git", "log", "--oneline", f"origin/{os.environ['BASE']}...HEAD"],
    text=True
)
client = anthropic.Anthropic()
msg = client.messages.create(
    model="claude-haiku-4-5-20251001",
    max_tokens=300,
    system="Write a GitHub PR description in markdown with sections: ## Summary (2 sentences), ## Changes (bullet list), ## Testing (what to verify). Be concise.",
    messages=[{"role": "user", "content": f"Commits:\n{commits}\n\nDiff:\n{diff}"}]
)
body = msg.content[0].text
subprocess.run(["gh", "pr", "edit", os.environ["PR_NUM"], "--body", body])
print(body)
EOF

Cost and model comparison

ModelCost per PR (500-line diff)LatencyBest for
Haiku~$0.001~3sHigh-frequency CI, every commit
Sonnet~$0.004~8sPre-merge reviews, architecture checks
Opus~$0.02~20sSecurity audits, large multi-file refactors
GitHub Copilot reviewSubscription~10sInline suggestions only

For prompt caching patterns (useful if you have a large coding standards document in the system prompt), see the prompt caching guide. For cost modelling, use the Claude API Cost Calculator.

Frequently asked questions

How do I store the Anthropic API key securely in GitHub Actions?
Add ANTHROPIC_API_KEY as a repository secret in Settings → Secrets and variables → Actions. Reference it in your workflow with `${{ secrets.ANTHROPIC_API_KEY }}`. Never hardcode the key in workflow YAML files.
Which Claude model is best for CI/CD code review?
claude-haiku-4-5-20251001 is the best choice for CI/CD: it reviews diffs in under 5 seconds, costs $0.80/1M input tokens, and its feedback quality is sufficient for automated PR comments. Reserve Sonnet for complex multi-file architectural reviews.
How do I avoid hitting Claude rate limits in a busy CI/CD pipeline?
Use exponential backoff with a max of 3 retries. For repositories with many concurrent PRs, set a concurrency group in the workflow (`concurrency: pr-review-${{ github.event.pull_request.number }}`) to queue rather than parallelise reviews.
Can Claude post review comments directly to GitHub PRs?
Yes. Use the GitHub REST API (`gh pr comment` or `octokit.rest.pulls.createReviewComment`) to post Claude's output as inline review comments on the diff. You need `pull-requests: write` permission in the workflow.
What is the cheapest way to run Claude on every PR?
Pipe only the diff (`git diff origin/$BASE_BRANCH...HEAD`) rather than the full codebase. Truncate diffs over 4000 lines to the first/last 2000. Use Haiku. A typical 500-line PR diff costs under $0.001.

Free tools

Cost Calculator → API Cookbook → Diff Summarizer → Skills Browser →

More examples

Claude API Python QuickstartClaude API Node.js / TypeScript QuickstartClaude API Streaming in PythonClaude API Streaming in Node.js / TypeScriptClaude API Tool Use in PythonClaude API Tool Use in Node.js / TypeScript