A grammar checker that feels instant on a tweet can fall apart on a manuscript. The reason is structural: most proofreading APIs are stateless, so every check resubmits the entire text. On a short message that's fine. On a 50,000-character document being actively edited, it means paying to re-proofread the same correct paragraphs hundreds of times — and waiting on them.

This article walks through why the naive approaches break, how incremental proofreading solves the problem properly, and the cost math that makes it the difference between a viable feature and an abandoned one.

The as-you-type cost spiral

Picture a realistic editing session: a user revising a 48,000-character chapter in your app. Your checker debounces keystrokes and fires a request after each pause — say 200 proofreading runs over the session.

With a stateless API, every run sends all 48,000 characters:

Characters processed
Full re-proofread, 200 runs 9,600,000
Incremental, ~3% changed per run ~335,000

On AmberPen's Starter plan (€5 per month with 500K characters included, then €9 per million), that's roughly €87 versus €5 — per document, per session. Multiply by thousands of active documents and the stateless approach prices the feature out of existence. The latency story is just as bad: median response time scales with input length, so every pause triggers the slowest possible request.

Why the obvious fixes don't work

Teams usually reach for one of four workarounds before looking for a better primitive:

Debounce harder. Waiting two seconds instead of 400 ms cuts request count but makes the checker feel broken. Users judge a proofreader by whether it keeps up with them.

Proofread only the current paragraph. Cheap, but wrong in two directions. Errors that span paragraph boundaries disappear — a tense established three paragraphs ago, a pronoun whose antecedent moved. And paragraph-scoped edits must be mapped back into document coordinates by hand.

Diff on the client, send only changed sentences. Now you own a diffing layer, sentence segmentation, offset bookkeeping, and the hard question of how much surrounding context the model needs to correct a fragment correctly. Send too little context and correction quality drops exactly where the user is working.

Cache results client-side. You still pay for the full first pass on every session, and you're merging stale and fresh edits yourself.

All four share a root cause: the API treats every request as the first time it has seen the document.

How incremental proofreading works

Incremental proofreading adds one field to the request: a stable textId for the document. Everything else — the request shape, the response shape, the edit offsets — stays the same.

The work happens inside the SDK, in your own process, and it is the bookkeeping you were about to build:

  1. The first request with a textId proofreads the full text. The client keeps that text and its edits in memory.
  2. On each later request, the client diffs the new text against the remembered version to find the blocks that changed — a block being a line.
  3. Around each change it takes roughly 800 characters of context on each side by default — configurable with incrementalChunkSize — rounded to the closest block boundary, merging chunks that touch.
  4. The chunks are sent to the API in parallel, as independent requests.
  5. Edits from those chunks replace the remembered edits in the same ranges; the edits outside them are kept and shifted to their new offsets.
  6. You get one array covering your complete document, and the client remembers the version you just sent.

If nothing changed since the last request, no request is sent at all and the previous edits come straight back. A resubmitted 48,000-character document with a few revised sentences reprocesses about 3% of the text — and returns in a few hundred milliseconds instead of seconds.

There is no behavioral difference to design around: the response is a complete result for the whole document, not a patch, so you can replace your suggestion set with it. What changed is where the work happened — only the chunks around edits went through a model, and the rest of the edits came from the client's memory.

Two integration rules matter. Use one textId per document (or per independently edited section, like a chapter), and always send the complete text — never just the changed part. The SDK computes the diff; edit offsets stay valid against your full document, which is what makes decorations and accept/dismiss flows keep working unchanged.

What it looks like in practice

With the SDK, incremental checking is a one-line change:

const result = await amberPen.proofread({
	text: fullDocumentText,
	textId: `doc-${documentId}`,
});

The response is identical in shape to a regular proofread, and the edits arrive in document coordinates whether the model examined 48,000 characters or 1,400 — nothing downstream needs to know the difference. That's the point: the optimization is done once, correctly, inside the client, with the model's context requirements baked in.

The trade-offs to know about

Incremental proofreading is a deliberate set of compromises, and they're worth stating plainly:

  • The text is held in memory — yours. A textId makes the client keep the previous version of the document, for 24 hours by default (incrementalTtlMs changes that). Nothing is stored on our side: the API never sees a textId, and rejects one if it is sent. The privacy policy covers retention in detail.
  • One long-lived client. The memory belongs to a client instance, so a process that builds a new client per request starts cold every time and proofreads the full text.
  • Streaming commits only complete snapshots. Pass the same textId to streaming mode to stream changed chunks on later checks. The SDK updates its cache only after every required chunk completes; an aborted, failed, or partially consumed stream leaves the previous completed snapshot intact.
  • One-off texts gain nothing. A textId only pays off when the same document is proofread more than once.

Match the tool to the workload

The decision is simpler than it looks:

  • Short texts, checked occasionally — regular requests. The overhead of a textId buys nothing.
  • Long documents, checked once at publish time — streaming, so users see progress instead of a spinner.
  • Long documents, checked continuously while editing — incremental, optionally combined with streaming when suggestions should appear progressively. This is the workload that breaks budgets, and the one that most proofreading APIs leave you to solve on the client.

Writing apps, note-taking tools, and CMS editors almost always end up in the third bucket as their users' documents grow. If that's where you're headed, the incremental proofreading guide has the full reference, and a free test key lets you measure the difference on your own documents — run the same long document through both modes and compare the character counts on your dashboard.

The billing side of this decision is worked through in what you'll actually pay — the long-document example there is the one that turns a 310-billion-character monthly bill into something a real product can carry. And if you're still choosing a provider, the grammar checker API comparison notes which ones handle re-checking server-side at all.