← Blog

How to Build a Summarization App with Claude API

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

Building a summarization app with Claude API means sending text to a Claude model with a prompt that specifies the output format and length, then handling the response in your app — whether that's a one-off script, a browser extension, or a full product with user accounts and billing. The core loop is simple: text in, prompt wrapping, API call, summary out. The hard parts are prompt design for consistent output, handling documents longer than the model's context window in a single request, and deciding how to expose the feature to real users without burning your own API key budget.

This guide walks through all of that: the request shape, chunking strategy for long documents, streaming for responsive UIs, and the packaging decisions that turn a script into a shippable product.

What you actually need to build

A summarization app has four moving parts:

  1. Input handling — pasted text, uploaded files, or URLs that need to be fetched and cleaned.
  2. Prompting — instructions that control length, tone, and structure of the summary.
  3. Chunking — splitting documents too large for one request and merging partial summaries.
  4. Delivery — returning the result to the user, ideally streamed if the summary is long.

Everything else — auth, rate limiting, logging — is standard SaaS plumbing, not specific to summarization.

Writing the prompt

Claude follows explicit formatting instructions well, so be specific rather than vague. A weak prompt ("summarize this") produces inconsistent length and structure. A better pattern:

Summarize the following document in 3-5 bullet points.
Each bullet should be one sentence. Do not include an
introduction or conclusion sentence — only the bullets.

Document:
<document text>

For different use cases, change the constraint, not the instruction style:

Putting the document after the instructions (as above) works better than before them — Claude attends to the task definition first, then applies it to the content that follows.

Calling the API

A basic request looks like this:

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $SUBTOAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-opus-4",
    "max_tokens": 300,
    "messages": [
      {"role": "user", "content": "Summarize the following document in 3 bullet points:\n\n<document text>"}
    ]
  }'

max_tokens caps the summary length — not the input. If you need a hard word limit, state it in the prompt ("exactly 100 words") since token counts and word counts don't map 1:1.

For a product-facing app, you'll want this wrapped in your backend, not called from the browser, so your API key never ships to the client. See /docs/quickstart for the full request/response shape and /docs/messages for parameter details.

Handling documents longer than the context window

Most real-world summarization requests — PDFs, transcripts, long articles — eventually hit a size you can't send in one call, either due to context limits or because you want to control cost. The standard approach is map-reduce summarization:

  1. Split the document into chunks (by paragraph or heading, not mid-sentence).
  2. Summarize each chunk independently.
  3. Concatenate the chunk summaries.
  4. Summarize the concatenation into a final summary.
async function summarizeLongDocument(chunks) {
  const partials = await Promise.all(
    chunks.map(chunk => summarizeChunk(chunk))
  );
  const combined = partials.join("\n\n");
  return summarizeChunk(combined, { finalPass: true });
}

async function summarizeChunk(text, opts = {}) {
  const prompt = opts.finalPass
    ? `Combine these partial summaries into one coherent summary:\n\n${text}`
    : `Summarize this section in 2-3 bullet points:\n\n${text}`;

  const res = await fetch("https://api.subtoapi.app/v1/messages", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      model: "claude-opus-4",
      max_tokens: 250,
      messages: [{ role: "user", content: prompt }]
    })
  });
  const data = await res.json();
  return data.content[0].text;
}

Chunk by logical boundaries (sections, paragraphs) rather than fixed character counts when possible — a chunk cut mid-argument produces a worse partial summary than one cut at a natural break, even if it's a bit shorter.

Streaming for a responsive UI

If you're summarizing long inputs and want the user to see output as it's generated rather than waiting on a multi-second map-reduce pass, use streaming on the final summarization call:

const res = await fetch("https://api.subtoapi.app/v1/messages", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    model: "claude-opus-4",
    max_tokens: 300,
    stream: true,
    messages: [{ role: "user", content: prompt }]
  })
});

for await (const chunk of res.body) {
  process.stdout.write(decodeChunk(chunk));
}

This matters most for the final merge step in map-reduce, since individual chunk summaries are usually short enough that streaming adds little perceived benefit. Full details are in /docs/streaming.

Going from script to product

A script that calls Claude once is easy. Turning it into something users can rely on adds real requirements:

This is the layer SubToAPI handles on top of raw Claude access: it issues scoped sub_live_... keys per application or team member, exposes token usage per request, and supports streaming and tool use out of the box — so you're not building key management and usage tracking yourself just to ship a summarization feature. Plans start at €9/month (Solo), with Team at €19/seat and Scale at €49/seat, and a free trial at signup to test the integration before committing.

FAQ

What's the cheapest way to summarize long documents with Claude?

Use map-reduce chunking and keep per-chunk summaries short (2-3 bullets) — this minimizes total output tokens across the multiple calls, which is usually the bigger cost driver than input tokens for long documents.

Can Claude summarize PDFs directly?

You need to extract text from the PDF first (most PDF libraries handle this) and send the extracted text in the prompt; Claude API itself works on text input, not raw binary files.

How do I keep summary length consistent across requests?

State an explicit constraint in the prompt (word count, bullet count, or sentence count) rather than relying on max_tokens alone — max_tokens only sets an upper bound, it doesn't guarantee the model hits a target length.

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 →