← Blog

Claude API Code Review Bot Setup Guide

2026-10-21 · 5 min read · SubToAPI Team

Setting up a Claude API code review bot means connecting your Git host's pull request webhook to an endpoint that sends the diff to Claude, gets back structured feedback, and posts it as a PR comment. The core pieces are: a webhook listener, a prompt that produces consistent review output, a diff-fetching step, and an API client that calls Claude with the right context window and parameters. This article walks through each piece with working code.

Most teams get stuck not on the AI part but on the plumbing — API key management, rate limits, and reliably parsing diffs into something Claude can reason about. We'll cover both the logic and the operational setup, including how to avoid common failure modes like truncated diffs and inconsistent comment formatting.

Architecture overview

A minimal code review bot has four stages:

  1. Trigger — a GitHub/GitLab webhook fires on pull_request.opened or pull_request.synchronize.
  2. Fetch — your server pulls the diff via the Git provider's API.
  3. Review — the diff (plus relevant file context) is sent to Claude with a review-focused system prompt.
  4. Post — Claude's structured response is parsed and posted back as inline comments or a summary comment.

This runs well as a small Node.js service deployed on any serverless platform (Vercel, Cloudflare Workers, AWS Lambda) or a lightweight long-running server if you need to queue large diffs.

Step 1: Set up the webhook

For GitHub, create a webhook in repo settings pointing to your endpoint, subscribed to pull_request events. Verify the signature before processing anything:

import crypto from "crypto";

function verifySignature(payload, signature, secret) {
  const hmac = crypto.createHmac("sha256", secret);
  const digest = "sha256=" + hmac.update(payload).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signature));
}

Only proceed to the review step on opened and synchronize actions to avoid re-reviewing on every label change or comment.

Step 2: Fetch the diff

Use the GitHub API to pull the unified diff for the PR:

const diffResponse = await fetch(
  `https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}`,
  {
    headers: {
      Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
      Accept: "application/vnd.github.v3.diff",
    },
  }
);
const diff = await diffResponse.text();

If the diff exceeds a few thousand lines, split it by file and review the largest/most-changed files first, or summarize unchanged context and only send full detail for modified hunks. Sending an entire monorepo diff in one request wastes tokens and degrades review quality — Claude does better reviewing focused chunks with clear boundaries.

Step 3: Build the review prompt

Consistency matters more than cleverness here. Use a system prompt that pins the output format so your parser doesn't break on edge cases:

You are a senior code reviewer. Review the given diff for:
- bugs and logic errors
- security issues
- performance problems
- style inconsistencies with the surrounding code

Respond ONLY with a JSON array of objects, each with:
"file", "line", "severity" (low|medium|high), "comment".
If no issues, return an empty array.

Pass the diff as the user message, truncated per file if needed. Keeping the output schema fixed means your posting step can loop over a predictable structure instead of regex-parsing free text.

Step 4: Call the API and post comments

This is where you plug in your Claude access. If you're using SubToAPI to turn your existing Claude subscription into an HTTPS API, the call looks like this:

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $SUBTOAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4",
    "max_tokens": 2048,
    "system": "You are a senior code reviewer...",
    "messages": [{"role": "user", "content": "DIFF CONTENT HERE"}]
  }'

Parse the returned JSON array and post each comment using the GitHub review API, which supports inline comments tied to specific file/line pairs:

await fetch(
  `https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}/reviews`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      event: "COMMENT",
      comments: parsedIssues.map((issue) => ({
        path: issue.file,
        line: issue.line,
        body: `**${issue.severity.toUpperCase()}**: ${issue.comment}`,
      })),
    }),
  }
);

SubToAPI issues application-scoped sub_live_... keys per service, so your review bot, any internal tooling, and other projects each get separate keys with their own usage visible in one dashboard — useful when a bot is misbehaving and you need to isolate which key is burning through requests. See /docs/quickstart for key setup and /docs/messages for the full request/response reference.

Step 5: Handle rate limits and retries

PR activity is bursty — a large team pushing many commits in a short window can trigger overlapping webhook calls. Queue reviews per-PR so you don't send five concurrent requests for the same branch, and add exponential backoff on 429 responses. If you expect high review volume, check /pricing for seat-based plans that fit a team workflow rather than a single key shared across CI runners.

Step 6: Keep context tight

Code review quality drops when the model doesn't have enough surrounding context, but it also drops when you dump an entire file for a one-line change. A good middle ground: include the changed hunk plus 10–15 lines of surrounding context, and only pull the full file when the diff touches function signatures or imports that affect other files.

Streaming for large PRs

For PRs with many files, streaming the response lets you post comments incrementally instead of waiting for the full JSON array to complete. This matters more for dashboards or CI logs where you want live feedback. See /docs/streaming if you want to build that incrementally rather than waiting on one large blocking call.

Tool use for deeper checks

If your review bot needs to run a linter or test suite before commenting (rather than relying purely on the model's judgment), Claude's tool use lets it call out to a function — e.g., "run eslint on this file" — and incorporate the result into its review. This is worth adding once the basic bot is stable; see /docs/tools for the mechanics.

questions

Do I need my own Anthropic API key to build this? No — if you already have Claude Pro or Max access, SubToAPI converts it into an HTTPS API key you can use directly in the webhook handler shown above, without separately provisioning Anthropic API billing.

How do I avoid the bot commenting on every trivial change? Filter by file type and diff size before calling the API, and instruct the model explicitly to return an empty array when it finds nothing significant — this keeps noise low and token spend predictable.

Can this work with GitLab or Bitbucket instead of GitHub? Yes, the architecture is identical — only the webhook payload format and comment-posting API calls change; the Claude review prompt and API call stay the same.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →