← Blog

How to Chunk Documents for Claude API

2026-09-23 · 5 min read · SubToAPI Team

Chunking documents for Claude means splitting long text into smaller, semantically coherent pieces before sending them to the API — either because the source exceeds the context window, or because you're feeding chunks into a retrieval system and only want the most relevant ones in each request. The right chunk size and split strategy directly affect answer quality, cost, and latency, so it's worth getting right rather than picking an arbitrary number and moving on.

This guide covers the practical decisions: how big chunks should be, where to split, how to preserve context across boundaries, and how to structure the request once your chunks are ready.

Why chunking matters

Claude's models have large context windows, but "fits in the context window" and "produces a good answer" are different problems. Three issues show up if you get chunking wrong:

Good chunking is a tradeoff between chunk size, semantic coherence, and how much overlap you keep between chunks.

Choosing a chunk size

There's no single right number, but here are reasonable starting points depending on your use case:

Start with 500 tokens and overlap of 10–15%, then adjust based on how often retrieved chunks miss relevant context or return too much noise.

Splitting strategies

1. Fixed-size splitting

Simplest approach: split every N tokens, with some overlap between chunks so context isn't lost at the boundary.

function chunkText(text, chunkSize = 500, overlap = 50) {
  const words = text.split(/\s+/);
  const chunks = [];
  let start = 0;

  while (start < words.length) {
    const end = Math.min(start + chunkSize, words.length);
    chunks.push(words.slice(start, end).join(" "));
    start += chunkSize - overlap;
  }

  return chunks;
}

This is fast and predictable but ignores document structure — it will happily cut a sentence in half. Fine for rough drafts, not ideal for production RAG.

2. Semantic / structural splitting

Split on natural boundaries: paragraphs, headings, sentence groups. Most document formats give you these for free.

function chunkByParagraphs(text, maxTokens = 500) {
  const paragraphs = text.split(/\n\s*\n/);
  const chunks = [];
  let current = "";

  for (const para of paragraphs) {
    if ((current + para).split(/\s+/).length > maxTokens) {
      if (current) chunks.push(current.trim());
      current = para;
    } else {
      current += "\n\n" + para;
    }
  }
  if (current) chunks.push(current.trim());
  return chunks;
}

This keeps sentences and paragraphs intact, which noticeably improves answer quality compared to fixed-size splitting, at the cost of slightly variable chunk sizes.

3. Recursive splitting

Try to split on the largest structural boundary first (sections), then fall back to smaller ones (paragraphs, then sentences) only if a chunk is still too big. This is what most production RAG libraries do under the hood, and it's worth implementing yourself if you're not using one — it's not much more code than the paragraph splitter above.

4. Markdown/HTML-aware splitting

If your source documents are markdown or HTML, split on headings first so each chunk stays under one topic:

function chunkByHeadings(markdown) {
  return markdown.split(/(?=^#{1,3}\s)/m).filter(Boolean);
}

Combine this with a size limit per section so long sections still get subdivided.

Preserving context across chunks

Two techniques make a real difference here:

function withContext(chunk, docTitle, sectionTitle) {
  return `Document: ${docTitle}\nSection: ${sectionTitle}\n\n${chunk}`;
}

Sending chunks to Claude

Once your chunks are ready, the pattern depends on the use case. For summarizing a whole document, send chunks sequentially and ask Claude to maintain a running summary. For Q&A over a large corpus, retrieve the top-k relevant chunks and pass them as context in a single request.

If you're calling the Anthropic API directly, or routing through SubToAPI so you get a standard HTTPS endpoint, application API keys, and usage metadata alongside your existing Claude access, the request shape is the same — you're just assembling the chunks into the messages payload:

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $SUBTOAPI_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-sonnet-4-5",
    "max_tokens": 1024,
    "messages": [
      {"role": "user", "content": "Context:\n\n" + chunk1 + "\n\n" + chunk2 + "\n\nQuestion: ..."}
    ]
  }'

Details on request structure are in the Messages docs; if you're chunking a document too large for one call, streaming responses helps avoid timeouts on long generations. Getting the API wired up is a separate step from chunking — see the quickstart if you haven't set that up yet.

Common mistakes to avoid

questions

What chunk size should I use for Claude API requests? For retrieval-based use cases, 300–800 tokens per chunk with 10–15% overlap is a solid default. For direct document analysis, chunk by natural sections rather than a fixed token count.

Should I chunk by tokens or by characters? Tokens. Character counts don't correspond reliably to token counts across languages and formats, so a character-based splitter can produce wildly inconsistent chunk sizes.

Do I need to chunk documents that fit in Claude's context window? Not necessarily. If the whole document fits and you're doing a single analysis or summary, sending it whole is simpler and preserves all context. Chunking mainly matters for retrieval pipelines or documents that exceed the context window.

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 →