Claude API GitHub Actions CI/CD Setup Guide
Setting up the Claude API inside a GitHub Actions workflow means storing your API key as a repository secret, calling the API from a workflow step (via curl or a small script), and wiring the output into your pipeline — as a PR comment, a commit check, or a generated artifact. The whole setup takes under 30 minutes and doesn't require any extra infrastructure beyond what GitHub Actions already gives you.
This guide walks through a complete working example: a workflow that runs Claude on every pull request to review the diff and post feedback as a comment. You can adapt the same pattern for test generation, changelog writing, or documentation checks.
Why use Claude in CI/CD
Running an LLM as part of your pipeline is useful for tasks that are repetitive but need judgment:
- Automated code review — catching style issues, obvious bugs, or missing error handling before a human reviewer looks at it
- Test scaffolding — generating unit test stubs for new functions added in a PR
- Release notes — summarizing commits since the last tag into human-readable changelogs
- Docs drift detection — flagging when code changes aren't reflected in README or API docs
None of these need a long-running service. A GitHub Actions job spins up, calls the API once or twice, and exits — which is exactly the kind of workload that's cheap and simple to automate.
Step 1: Store your API key as a secret
Never put API keys directly in workflow files. Add the key under your repo's Settings → Secrets and variables → Actions:
ANTHROPIC_API_KEY=sk-ant-...
If you're using SubToAPI instead of a raw Anthropic key, the setup is identical — you just generate an application key (sub_live_...) from your dashboard and store that as the secret instead. This is a common choice for teams who already pay for Claude access through a Pro/Team/Max subscription and want to reuse it across CI jobs without managing separate Anthropic billing. See /docs/quickstart for how keys are issued.
Step 2: Write the workflow file
Create .github/workflows/claude-review.yml:
name: Claude PR Review
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get PR diff
id: diff
run: |
git diff origin/${{ github.base_ref }}...HEAD > diff.txt
echo "lines=$(wc -l < diff.txt)" >> $GITHUB_OUTPUT
- name: Call Claude API
env:
API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
node .github/scripts/review.js
- name: Post comment
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const body = fs.readFileSync('review.md', 'utf8');
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body,
});
Step 3: The review script
// .github/scripts/review.js
const fs = require('fs');
async function main() {
const diff = fs.readFileSync('diff.txt', 'utf8').slice(0, 15000);
const response = await fetch('https://api.subtoapi.app/v1/messages', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'claude-sonnet-4-5',
max_tokens: 1024,
messages: [{
role: 'user',
content: `Review this diff for bugs, missing error handling, and style issues. Be concise, use bullet points, and only flag real problems:\n\n${diff}`,
}],
}),
});
const data = await response.json();
const text = data.content[0].text;
fs.writeFileSync('review.md', `## Claude Review\n\n${text}`);
}
main();
This uses the standard /v1/messages endpoint format — see /docs/messages for the full request and response schema, including how to handle longer diffs with pagination or truncation.
Handling diffs that are too large
PRs with large diffs will exceed context limits or just produce unfocused reviews. Two practical fixes:
- Truncate and prioritize: only send diffs for changed source files, skip lockfiles, generated code, and test snapshots
- Chunk per file: loop over changed files and call the API once per file, then concatenate results — this also makes output easier to map back to specific files in the comment
const changedFiles = diff.split(/^diff --git/m).filter(Boolean);
for (const chunk of changedFiles) {
// call API per chunk, accumulate results
}
Adding a required status check
If you want Claude's review to block merges on serious issues (not just comment), parse the response for a severity marker and set the job's exit code accordingly:
if (text.includes('BLOCKING')) {
process.exit(1);
}
Then mark the review job as a required check in your branch protection rules. Be conservative here — treat this as a safety net for obvious issues, not a replacement for human review.
Rate limits and parallel PRs
If your team opens many PRs at once, concurrent workflow runs can hit rate limits. Two mitigations:
- Add a
concurrencygroup in the workflow so only one review runs per PR at a time, not per push - Use a dashboard that reports usage metadata per key so you can see whether CI traffic is a meaningful share of your total calls — this is one of the things SubToAPI's dashboard tracks automatically, alongside team seats if more than one project shares the same underlying Claude access
concurrency:
group: claude-review-${{ github.event.pull_request.number }}
cancel-in-progress: true
Using streaming for long-running jobs
For workflows generating longer outputs — full test suites, long changelogs — streaming avoids timeout issues on slow connections and lets you write partial output incrementally. See /docs/streaming for the event format if you want to adapt the script above to stream to a log file as it generates.
Setting it up with SubToAPI
If your organization already has a Claude subscription and doesn't want to provision a separate Anthropic API account for CI, SubToAPI turns that subscription into an application-ready key you can drop straight into the ANTHROPIC_API_KEY secret above — no code changes needed beyond pointing the base URL at api.subtoapi.app. Start with a free trial at /signup, or compare Solo, Team, and Scale plans at /pricing if multiple repos or team members need their own keys for usage tracking.
questions
Do I need a paid GitHub Actions plan to run Claude API calls in CI? No — public repos get free Actions minutes, and private repos get a generous free tier too. The API call itself is billed separately through your Claude or SubToAPI plan, not through GitHub.
Can I use this setup with GitLab CI or other pipelines instead of GitHub Actions? Yes. The pattern is identical — store the key as a pipeline secret/variable and call the /v1/messages endpoint from a job step. Only the YAML syntax for secrets and job definitions changes.
What's the safest way to prevent leaking the API key in logs? Never echo the key or print full request headers in a step. GitHub Actions automatically masks secret values in logs, but double-check by keeping the key out of run: command strings and passing it only through env:.