Skip to content
Claude

How to Build a Claude-Powered Legal Document Analyzer (No Lawyers Required)

Learn to build a Claude-powered contract analyzer using Batch API and smart prompts to extract clauses, flag risks, and cut legal review costs fast.

11 min read
How to Build a Claude-Powered Legal Document Analyzer (No Lawyers Required)

Legal review is one of those things that sounds like it should be solved by now. You have a stack of NDAs, vendor contracts, or service agreements that need reviewing before you sign anything. Your lawyer charges $400/hour. Half of that time is spent reading boilerplate that hasn’t changed since 1994. There has to be a better way — and in 2026, there is.

Claude’s API, specifically when paired with the Batch API and some disciplined prompt engineering, turns contract review from a billable-hour nightmare into an automated first-pass pipeline. You’re not replacing your lawyer for the judgment calls. You are, however, eliminating the part where they charge you $300 to confirm that yes, the indemnification clause is one-sided. This tutorial walks you through building that pipeline from scratch.

By the end, you’ll have a working system that ingests contracts, extracts liability clauses, flags non-standard terms, and outputs a structured risk summary — all for a fraction of what a single hour of legal counsel costs.

What You’ll Actually Build

The goal is a contract analysis assistant that does four things reliably: extracts specific clause types (liability, indemnification, termination, payment terms), compares them against a baseline of what’s “normal,” flags anything that deviates significantly, and packages everything into a readable risk summary. You’ll use Claude’s API for single-document deep dives and the Batch API when you’re processing ten or fifty contracts at once. The Batch API is particularly useful here — Anthropic offers approximately 50% off input token costs compared to standard API calls, which adds up fast when you’re feeding 20-page contracts through the model repeatedly.

What You’ll Need

Before writing a single line of code, make sure you have an Anthropic API key with Batch API access enabled, Python 3.9 or later, the anthropic Python SDK installed (pip install anthropic), and your contracts in PDF or plain text format. Claude can handle PDFs directly via the Files API, or you can extract text first using a library like pdfplumber — either works. You do not need a law degree, which is kind of the whole point.

Note 💡

Claude cannot give legal advice and does not assume liability for what it flags or misses. This system is a first-pass screening tool. Anything it surfaces should go to an actual lawyer before you make binding decisions. Think of it as a very thorough paralegal, not a partner at Sullivan & Cromwell.

Step 1 — Set Up Your System Prompt

The system prompt is where you set the rules of engagement. A weak system prompt gets you vague summaries. A good one gets you structured, actionable output. Here’s the baseline you’ll use for contract analysis:

You are a contract analysis assistant. Your job is to extract specific clauses from legal documents and flag non-standard terms. You do not provide legal advice. You produce structured, factual summaries of what the document contains.

For every contract you analyze, identify and extract the following sections verbatim where they exist:
1. Limitation of Liability clause
2. Indemnification clause
3. Termination clause (for cause and for convenience separately)
4. Payment terms and penalties
5. Intellectual property ownership clause
6. Governing law and jurisdiction

For each extracted clause, provide:
- The exact clause text (verbatim)
- A plain-English one-sentence summary
- A risk flag: LOW / MEDIUM / HIGH, based on how one-sided or unusual the language is
- A brief reason for the risk flag (one sentence)

Output as structured JSON. If a clause is not present in the document, mark it as "NOT FOUND" in the extraction field.

This prompt does several things right: it tells Claude exactly what to look for, demands verbatim extraction so you’re working with actual contract language rather than paraphrases, forces a consistent JSON output structure, and keeps it honest about limitations. Adjust the clause list to match your contract type — software licenses need different clauses than real estate agreements.

Pro tip ✅

Always ask for verbatim extraction first, then summarization. If Claude summarizes first and you never see the original language, you’ve just introduced a game of telephone between your contract and your risk assessment. Lawyers will immediately ask to see the actual clause.

Step 2 — Process a Single Contract

Start with a single contract to validate your setup before going to batch. Here’s the Python code:

import anthropic

client = anthropic.Anthropic(api_key="your_api_key_here")

def analyze_contract(contract_text: str, contract_name: str) -> dict:
    system_prompt = """You are a contract analysis assistant. Your job is to extract specific clauses from legal documents and flag non-standard terms. You do not provide legal advice. You produce structured, factual summaries of what the document contains.

For every contract you analyze, identify and extract the following sections verbatim where they exist:
1. Limitation of Liability clause
2. Indemnification clause
3. Termination clause (for cause and for convenience separately)
4. Payment terms and penalties
5. Intellectual property ownership clause
6. Governing law and jurisdiction

For each extracted clause, provide:
- The exact clause text (verbatim)
- A plain-English one-sentence summary
- A risk flag: LOW / MEDIUM / HIGH
- A brief reason for the risk flag

Output as valid JSON only. No preamble, no markdown fences."""

    response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=4096,
        system=system_prompt,
        messages=[
            {
                "role": "user",
                "content": f"Contract name: {contract_name}nnContract text:n{contract_text}"
            }
        ]
    )
    
    return {
        "contract": contract_name,
        "analysis": response.content[0].text,
        "input_tokens": response.usage.input_tokens,
        "output_tokens": response.usage.output_tokens
    }

Run this on a test contract. If the output is clean JSON with the right fields, you’re ready to scale. If Claude is wrapping the JSON in markdown code fences despite being told not to, add one more line to your system prompt: Your response must begin with { and end with }. No other characters outside the JSON object.

Step 3 — Scale with Batch API

The Batch API is where the economics actually make sense. Instead of sending contracts one at a time and paying full price, you submit a batch of requests, Claude processes them asynchronously (usually within 24 hours, often much faster), and you collect the results. That 50% discount on input tokens matters when you’re processing 50 contracts with 15,000 tokens each.

import anthropic
import json

client = anthropic.Anthropic(api_key="your_api_key_here")

def build_batch_request(contract_text: str, contract_id: str, system_prompt: str) -> dict:
    return {
        "custom_id": contract_id,
        "params": {
            "model": "claude-haiku-4-5",
            "max_tokens": 4096,
            "system": system_prompt,
            "messages": [
                {
                    "role": "user",
                    "content": f"Contract ID: {contract_id}nnContract text:n{contract_text}"
                }
            ]
        }
    }

def submit_contract_batch(contracts: list[dict], system_prompt: str) -> str:
    requests = [
        build_batch_request(
            contract["text"],
            contract["id"],
            system_prompt
        )
        for contract in contracts
    ]
    
    batch = client.messages.batches.create(requests=requests)
    print(f"Batch submitted. ID: {batch.id} | Status: {batch.processing_status}")
    return batch.id

Pro tip ✅

For batch contract analysis, use Claude Haiku instead of Sonnet when your contracts are straightforward and well-formatted. Haiku is significantly cheaper and still handles clause extraction accurately on clean legal text. Save Sonnet for contracts that are ambiguously written or heavily negotiated — the ones where you actually need the extra reasoning horsepower.

Once the batch is submitted, you poll for results:

import time

def collect_batch_results(batch_id: str) -> list[dict]:
    while True:
        batch = client.messages.batches.retrieve(batch_id)
        status = batch.processing_status
        
        if status == "ended":
            print("Batch complete. Collecting results...")
            break
        elif status == "in_progress":
            print(f"Still processing... checking again in 60 seconds.")
            time.sleep(60)
        else:
            print(f"Unexpected status: {status}")
            break
    
    results = []
    for result in client.messages.batches.results(batch_id):
        if result.result.type == "succeeded":
            results.append({
                "contract_id": result.custom_id,
                "analysis": result.result.message.content[0].text,
                "status": "success"
            })
        else:
            results.append({
                "contract_id": result.custom_id,
                "status": "error",
                "error": result.result.error.type
            })
    
    return results

Step 4 — Build the Risk Flag Prompt

Clause extraction is step one. The more valuable layer is flagging what’s actually unusual or dangerous. This requires a second prompt pass — either inline or as a follow-up call — where you ask Claude to compare the extracted clauses against a baseline of standard commercial contract terms.

Given the following extracted contract clauses, identify any provisions that deviate significantly from standard commercial contract practice. Focus specifically on:

1. Liability caps that are unusually low (below the contract value, or absent entirely)
2. Indemnification clauses that are unilateral or cover unusually broad categories
3. IP assignment clauses that claim ownership of work product beyond the specific engagement
4. Auto-renewal terms with short cancellation windows (under 30 days)
5. Payment penalty clauses that exceed standard late fees (above 1.5% per month)
6. Jurisdiction clauses requiring disputes to be resolved in a distant or unusual location

For each deviation you identify, state:
- Which clause contains the deviation
- What the deviation is
- Why it matters in plain English
- A recommended action: ACCEPT / NEGOTIATE / ESCALATE TO COUNSEL

Extracted clauses to review:
{INSERT_EXTRACTED_JSON_HERE}

This two-pass approach — extract first, evaluate second — produces much cleaner results than trying to do both in one prompt. The first pass is factual retrieval. The second is comparative analysis. Mixing them tends to make Claude hedge more and summarize less precisely.

Pro tip ✅

Build a “standard terms baseline” document specific to your industry and include it in the risk flag prompt. Instead of asking Claude to flag against generic commercial norms, paste in your own company’s preferred standard language. Claude will then flag deviations from your actual baseline, not some hypothetical average contract. This is how in-house legal teams get the most value out of this kind of tool.

Step 5 — Generate the Risk Summary Report

The final step is turning the structured analysis into something a non-technical person can act on. Use this prompt to generate an executive summary:

Based on the following contract analysis, write a concise risk summary for a business decision-maker who is not a lawyer. The summary should:

1. Open with a one-sentence overall risk verdict: LOW RISK / MEDIUM RISK / HIGH RISK — DO NOT SIGN WITHOUT COUNSEL
2. List the top 3 concerns in plain English, ordered by severity
3. List any clauses that are notably favorable to us (if any)
4. Close with a recommended next step: sign as-is, request specific changes, or escalate for legal review

Keep the entire summary under 300 words. No legal jargon. Write as if explaining to a smart founder who has never read a contract before.

Contract analysis to summarize:
{INSERT_ANALYSIS_JSON_HERE}

This prompt produces output that’s actually readable by the person who has to make the call. Which is the whole point — you’ve done the heavy lifting of extraction and analysis, now you deliver a decision-ready summary instead of a wall of legalese.

Warning ⚠️

Never send AI-generated contract summaries to counterparties or use them as the basis for legal representations without attorney review. The system you’re building is a screening tool for your internal decision-making. The moment you use it to draft positions, respond to legal disputes, or make compliance certifications, you’ve crossed into territory that requires actual legal counsel.

Step 6 — Handle PDFs Directly

If your contracts come in as PDFs rather than plain text, Claude’s Files API handles them natively. Here’s how to upload and analyze a PDF directly:

import anthropic

client = anthropic.Anthropic(api_key="your_api_key_here")

def analyze_pdf_contract(pdf_path: str, contract_name: str) -> dict:
    with open(pdf_path, "rb") as pdf_file:
        uploaded_file = client.beta.files.upload(
            file=(contract_name, pdf_file, "application/pdf"),
        )
    
    response = client.beta.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=4096,
        betas=["files-api-2025-04-14"],
        system="You are a contract analysis assistant. Extract and analyze key clauses as instructed. Output valid JSON only.",
        messages=[
            {
                "role": "user",
                "content": [
                    {
                        "type": "text",
                        "text": "Analyze this contract. Extract all liability, indemnification, termination, payment, IP, and jurisdiction clauses. For each, provide verbatim text, plain-English summary, risk flag (LOW/MEDIUM/HIGH), and reason. Output as JSON."
                    },
                    {
                        "type": "document",
                        "source": {
                            "type": "file",
                            "file_id": uploaded_file.id
                        }
                    }
                ]
            }
        ]
    )
    
    return {
        "contract": contract_name,
        "file_id": uploaded_file.id,
        "analysis": response.content[0].text
    }

Pro tip ✅

After uploading a PDF, store the file_id returned by the Files API. If you need to run multiple analysis passes on the same document — extraction, risk flagging, summary — you can reuse the file ID instead of uploading the PDF again each time. This keeps your costs down and your processing faster.

The Cost Math

Here’s where the numbers get interesting. A typical 10-page commercial contract is roughly 4,000-6,000 tokens. Running 50 contracts through the Batch API at Claude Haiku pricing, with the approximate 50% input token discount, brings your per-contract analysis cost down to a few cents each. Compare that to even one hour of attorney review time at $400/hour and the arithmetic is obvious. You’re not replacing the attorney for the judgment call — you’re eliminating the three hours they’d spend reading before making that call.

Pro tip ✅

Set up a simple logging table that records contract_id, model_used, input_tokens, output_tokens, and timestamp for every batch you run. After a month, you’ll have real data on your per-contract processing cost. That number becomes very useful when justifying the tooling budget to finance — or when deciding whether to upgrade from Haiku to Sonnet for higher-complexity contracts.

What This System Won’t Do

Claude’s 200k context window means even the longest commercial contracts fit in a single request — a 200-page agreement is roughly 100,000 tokens of text, well within limits. But there are real limitations you should build around. Claude will occasionally miss a clause buried in an unusual document structure or labeled with non-standard section names. It can misread heavily formatted PDFs with complex tables or multi-column layouts — in those cases, extract the text first with a dedicated PDF parser before sending to Claude. It cannot verify whether a clause is enforceable in your jurisdiction, and it cannot negotiate. It also cannot catch errors in contracts that look standard but contain subtle changes from a negotiated version — for that, you’d need to run a diff against your approved template, which is a separate step worth adding to your pipeline if you’re in a high-volume deal environment.

Why This Actually Matters

The value of this system isn’t that it eliminates legal risk — it doesn’t. The value is that it changes the conversation with your lawyers from “here are 40 contracts, please review all of them” to “here are the 5 contracts our AI flagged as HIGH risk, please focus there.” That’s a fundamentally different engagement, and it’s one where your legal spend is concentrated where human judgment actually adds something that a well-prompted language model cannot.

Building this pipeline takes a few hours. Running it costs a few dollars. The first time it surfaces a one-sided liability cap or a buried auto-renewal clause before you sign, it will have paid for itself many times over — in money, in time, and in the particular quiet satisfaction of not finding out about a bad contract clause from your lawyer after the fact.

author avatar
promptyze

promptyze

ADMINISTRATOR