Building a Summarization Tool with Claude
Building a summarization tool with Claude means designing a prompt that produces consistent, length-controlled summaries, handling documents that exceed context limits, and exposing the result through an API your app can call reliably. This article walks through each piece: prompt structure, chunking strategy for long inputs, streaming vs. non-streaming output, and how to wire it all up with an HTTPS API.
The core workflow is simple: send text (or a document) to Claude with instructions on summary length, format, and tone, then return structured output your frontend or pipeline can use. The hard parts are consistency across requests, handling inputs longer than the model's context window, and making the tool fast enough for interactive use cases like dashboards or Slack bots.
Designing the Summarization Prompt
A good summarization prompt specifies three things explicitly: length, format, and focus. Vague prompts like "summarize this" produce inconsistent results — sometimes three sentences, sometimes three paragraphs.
A reliable template:
Summarize the following text in exactly 3-5 sentences.
Focus on: key decisions, numbers, and action items.
Do not include opinions or background that isn't in the source.
Text:
{{document}}
For structured output (useful if you're feeding the summary into another system), ask for JSON:
Summarize the text below and return ONLY valid JSON in this shape:
{"summary": string, "key_points": string[], "word_count": number}
Text:
{{document}}
Claude is reliable at following this format when the instruction is explicit and placed before the content, not buried after it. If you need guaranteed structure for downstream parsing, consider using tool calls instead of parsing free-text JSON — see /docs/tools for how tool-based extraction works through SubToAPI.
Handling Long Documents
Most real-world summarization tools hit the context window problem quickly: PDFs, transcripts, and support tickets often exceed what fits in a single request comfortably, even with large context models. Two common strategies:
1. Map-reduce summarization. Split the document into chunks (by paragraph, page, or token count), summarize each chunk independently, then summarize the summaries into a final output.
async function summarizeChunk(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-sonnet-4-5",
max_tokens: 300,
messages: [{
role: "user",
content: `Summarize in 2-3 sentences:\n\n${text}`
}]
})
});
const data = await res.json();
return data.content[0].text;
}
async function summarizeLongDocument(chunks) {
const partials = await Promise.all(chunks.map(summarizeChunk));
return summarizeChunk(partials.join("\n\n"));
}
This works well for transcripts and reports but can lose cross-chunk context — a decision made in chunk 1 and reversed in chunk 5 may not surface correctly in the final summary.
2. Sliding context with running summary. Instead of summarizing chunks independently, carry forward a running summary and ask Claude to update it with each new chunk:
Here is the current summary so far:
{{running_summary}}
Here is the next section of the document:
{{chunk}}
Update the summary to incorporate this new section. Keep it under 150 words.
This preserves narrative continuity better but is sequential, so it's slower for very long documents. Pick map-reduce for independent sections (e.g., multiple support tickets) and running summary for sequential narrative content (e.g., meeting transcripts, long-form articles).
Streaming for Interactive Use Cases
If your summarization tool is user-facing — a browser extension, a dashboard widget — streaming the response feels faster even when total latency is similar. Full details are in /docs/streaming, but the basic pattern with SubToAPI looks like:
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-sonnet-4-5",
max_tokens: 500,
stream: true,
messages: [{ role: "user", content: `Summarize:\n\n${documentText}` }]
})
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
let output = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
output += decoder.decode(value);
renderPartialSummary(output);
}
For batch jobs — nightly summarization of support tickets, email digests — skip streaming and just call the endpoint synchronously since nobody is watching the output appear in real time.
Controlling Cost and Consistency at Scale
Once the tool is working for one document, the next problem is running it across hundreds or thousands of documents a day. A few practical tactics:
- Cap
max_tokenstightly. Summaries don't need 4000 tokens of headroom; setting it close to your expected output length prevents runaway generations. - Pin the model version. If your prompt is tuned against a specific model, switching models silently can change summary length and tone.
- Log inputs and outputs. When a summary looks wrong, you need the exact request that produced it. SubToAPI's dashboard includes usage metadata per request so you can trace which calls are expensive or malformed without building your own logging layer.
- Separate dev and prod keys. Testing new prompt templates against production traffic risks inconsistent summaries reaching users. Scoped API keys per environment keep that separation clean.
Getting an API Key
Any summarization tool built on Claude needs an HTTPS endpoint with a key, usage tracking, and ideally team-level visibility if more than one person is building against it. SubToAPI turns your existing Claude access into that endpoint in a few minutes — see /docs/quickstart for setup, and /docs/messages for the full request/response reference. Plans start at €9/month for solo use, with team seats at €19 and scale pricing at €49 (/pricing). You can try it with a free trial via /signup.
questions
Can Claude summarize an entire book in one request? No — even with large context windows, very long books exceed practical limits once you add instructions and formatting. Use a map-reduce or running-summary approach to chunk the text and combine partial summaries.
How do I keep summary length consistent across requests? Specify length explicitly in sentences or word count rather than relative terms like "brief" or "short," and keep max_tokens close to that target so the model isn't tempted to expand.
Should I use streaming for a batch summarization job? No — streaming helps perceived speed for interactive UIs but adds no benefit for background jobs. Use standard synchronous requests and process results as they complete.