Summarizing Long Documents: Map-Reduce or a 1M Context Window

Map-reduce is for documents that do not fit the context window or need parallelism, not for saving money. Both approaches with code and cost math.

6 min · · development · автоматизация

Summarizing long documents is a common task: an annual report, a bundle of contracts, an email export, a thick research PDF. There are two approaches: split the text into parts and summarize each separately (map-reduce), or send the whole document to a model with a large context window. Below is when to pick which, working code, and a cost estimate at Zukko prices.

The key point about choosing

A common myth is that "map-reduce is cheaper". On the same model it is not: with map-reduce the model still reads the entire document (each chunk once), and on top of that come per-chunk instructions, intermediate summaries on the output side, and reading those summaries again at the reduce step. You end up with more tokens, not fewer.

Map-reduce is chosen for other reasons:

  • The document does not fit the model's context. glm-5.3 and glm-5.3-flash have a 128K-token context; a 300K-token document will not fit in one request.
  • Parallelism. Chunks can be processed concurrently, so total time depends on chunk size rather than document size.
  • Per-section summaries. If you need a chapter-by-chapter report or references to specific parts, intermediate summaries are useful on their own.

Sending the whole document wins when it fits the chosen model's context: the model sees connections between sections (an appendix referring to a contract clause, a conclusion relying on a table at the start), and the code is simpler. claude-sonnet-5, claude-opus-5-5, gpt-5.6-sol and gpt-5.6-luna have a 1M-token context.

How many tokens are in a document

Roughly: a hundred pages of business prose is tens of thousands of tokens, and Russian text usually takes more tokens than the same amount of English. For an exact count on Claude models there is a token counting endpoint; see the count_tokens docs. For other models, use usage from the response to your first request.

Code: both approaches and automatic selection

You need pip install openai and a fetch_... key in OPENAI_API_KEY.

bash
import os
from concurrent.futures import ThreadPoolExecutor

from openai import OpenAI

client = OpenAI(
    base_url="https://api.zukko.pro/v1",
    api_key=os.environ["OPENAI_API_KEY"],
)

CONTEXT = {
    "glm-5.3-flash": 128_000, "glm-5.3": 128_000,
    "gpt-5.6-luna": 1_000_000, "gpt-5.6-sol": 1_000_000,
    "claude-sonnet-5": 1_000_000, "claude-opus-5-5": 1_000_000,

What matters here:

  • summarize sends the document whole if it fits the model's context with headroom, and switches to map-reduce only when it does not.
  • Chunks are split by paragraph; a paragraph longer than the limit stays one large chunk, so for such texts add sentence-level splitting.
  • The map step runs in 5 parallel threads. Zukko does not rate-limit requests on its side, but a key can have daily and monthly dollar limits: once reached, you get a 429 with code key_daily_limit_reached or key_monthly_limit_reached.
  • If there are so many parts that their summaries do not fit the context, reduce in several levels: first in groups of 10–20, then the final pass.

More about the client is in the Python SDK guide.

Cost comparison

Every model on Zukko charges the same for input and output, so the formula is simple: (input + output) × price / 1,000,000. Assumptions: an instruction is about 50 tokens, a chunk summary about 400, the final summary about 1,500.

A 50K-token document, same model

Map-reduce with 4,000-token chunks gives 13 chunks. Input: 50,000 (document) + 13 × 50 (instructions) + about 5,250 (reading intermediate summaries at the reduce step) ≈ 56,000. Output: 13 × 400 + 1,500 = 6,700. Total ≈ 62,700 tokens.

Whole document: input ≈ 50,050, output 1,500. Total ≈ 51,550 tokens.

ModelMap-reduceWhole document
glm-5.3-flash ($0.04)62,700 × 0.04 / 1,000,000 ≈ $0.002551,550 × 0.04 / 1,000,000 ≈ $0.0021
claude-sonnet-5 ($0.3)62,700 × 0.3 / 1,000,000 ≈ $0.01951,550 × 0.3 / 1,000,000 ≈ $0.015

On the same model, map-reduce costs about 11K extra tokens: that is the price of splitting.

A 400K-token document

It does not fit glm-5.3-flash (128K), so the options are map-reduce on a cheap model or the whole document in a 1M-context model.

  • Map-reduce on glm-5.3-flash: 100 chunks. Input ≈ 400,000 + 100 × 50 + 40,050 ≈ 445,000, output ≈ 100 × 400 + 1,500 = 41,500. Total 486,500 × 0.04 / 1,000,000 ≈ $0.019.
  • Whole document in gpt-5.6-luna: (400,000 + 1,500) × 0.1 / 1,000,000 ≈ $0.040.
  • Whole document in claude-sonnet-5: 401,500 × 0.3 / 1,000,000 ≈ $0.12.
  • Map-reduce on claude-sonnet-5: 486,500 × 0.3 / 1,000,000 ≈ $0.15.

The price difference here comes from the model's price, not from the method. Which result works for you is something to test on two or three of your own documents: compare which facts, numbers and decisions made it into each summary. The current model list is at GET https://api.zukko.pro/v1/models or on the "Models" page in the dashboard.

A caveat about repeated questions and caching

If you ask many questions about one large document, every request resends the whole document as input. Vendors charge roughly 10% of the input price for reading such a repeated prefix from cache, while Zukko bills cached tokens at the regular input price. Ten questions about a 400K-token document on gpt-5.6-luna cost 10 × 400,000 × 0.1 / 1,000,000 = $0.40. It is cheaper to produce one detailed summary and ask questions against it.

How to choose: a quick cheat sheet

SituationApproach
The document fits the chosen model's contextWhole document
The document is larger than the model's contextMap-reduce or a 1M-context model
You need a summary per sectionMap-reduce
Links between distant parts of the text matterWhole document
Many documents, speed mattersMap-reduce with parallel requests

To run the code, create a key in the dashboard; setup steps are in the quick start.

FAQ

Does map-reduce lose information?

It can: a connection that crosses a chunk boundary is invisible to the model at the map step. Overlapping chunks (the last paragraph of the previous chunk at the start of the next) and asking for unresolved references in intermediate summaries both help. For contracts and documents with cross-references, sending the whole document is more reliable.

How do I handle PDF and DOCX files?

Extract the text first, then pass it to summarize:

bash
import pdfplumber
from docx import Document

with pdfplumber.open("report.pdf") as pdf:
    pdf_text = "\n\n".join(page.extract_text() or "" for page in pdf.pages)

docx_text = "\n\n".join(p.text for p in Document("contract.docx").paragraphs)

Scans without a text layer need OCR; extract_text will return empty strings.

How do I get the summary in a specific language?

Add a line like "Answer in Russian" to the prompt. Check the quality in your language and domain on several documents before running it on the whole archive.

How do I control summary length?

Set the length in the prompt: "no more than 300 words" or "5 bullet points". max_tokens is a hard cut-off of the answer, not a length control: if the limit is too low, the summary stops mid-sentence.

zukko

Pay 25×
less

for Claude Code & ChatGPT

One fetch_* key and base URL api.zukko.pro — for Cursor, Claude Code, Codex, and any SDK.

Get API keyDocumentation

api.zukko.pro · fetch_*

Long Document Summarization: Map-Reduce vs 1M Context · Zukko