Examples

Ten before/after pairs, every one produced by the API itself with options.preview = "changes" over a real document or a test fixture. Nothing is retouched: each pair is one page, cropped to the edited text plus context, rendered from the input and from the output at the same zoom.

01. Replace a fee in an agreementChromium print-to-PDF document, page 1

One call, no template: the amount changes, the embedded font and the rest of the line stay exactly as the generator wrote them.

{
  "edits": [
    {
      "find": "$4,200",
      "replace": "$4,800"
    }
  ]
}
beforeBefore: Replace a fee in an agreement
afterAfter: Replace a fee in an agreement
02. Delete a phrase and tidy what it leaves behindChromium print-to-PDF document, page 1

op: delete removes the street address; options.tidy collapses the double space and the stranded comma instead of leaving a gap.

{
  "edits": [
    {
      "op": "delete",
      "find": "123 Harbour Road, "
    }
  ],
  "options": {
    "tidy": true
  }
}
beforeBefore: Delete a phrase and tidy what it leaves behind
afterAfter: Delete a phrase and tidy what it leaves behind
03. Replace and restyle in one editthe same document, page 1

style sets the replacement's colour; fit: auto reports whether the new text had to be compressed to keep the line's layout.

{
  "edits": [
    {
      "find": "$4,200",
      "replace": "$4,800",
      "style": {
        "color": "#c00000"
      }
    }
  ]
}
beforeBefore: Replace and restyle in one edit
afterAfter: Replace and restyle in one edit
04. Redact an address before handing the file to a modelthe same document, page 1

The text is removed from the content stream and the pixels under the box are scrubbed: not covered with a black rectangle. redaction.found_in reports anywhere else the string still occurs.

{
  "edits": [
    {
      "op": "redact",
      "find": "billing@example.com"
    }
  ],
  "options": {
    "redact_metadata": true
  }
}
beforeBefore: Redact an address before handing the file to a model
afterAfter: Redact an address before handing the file to a model
05. Insert a clause below the signature blockthe same document, page 1

at.x/at.y is the baseline origin of the new line: the same coordinate a span's origin is reported in, so a position read out of /v1/text can be used here unchanged. The style is inherited from the nearest text, and the engine checks the page's own paper colour at the insertion point rather than overprinting ink. (at.after anchors on existing text instead; a below insert is refused when the next line already sits there.)

{
  "edits": [
    {
      "op": "insert",
      "text": "Late payment accrues 1.5% interest per month.",
      "at": {
        "page": 1,
        "x": 90,
        "y": 240
      }
    }
  ]
}
beforeBefore: Insert a clause below the signature block
afterAfter: Insert a clause below the signature block
06. Fill an AcroForm field, appearance and alla synthetic test fixture, page 1

op: form writes the field's value and reads /V back after reopening the document, so the value is in the file and not only in the appearance stream.

{
  "edits": [
    {
      "op": "form",
      "fields": {
        "applicant": "Zhang Wei"
      }
    }
  ]
}
beforeBefore: Fill an AcroForm field, appearance and all
afterAfter: Fill an AcroForm field, appearance and all
07. A longer replacement relines the whole paragrapha synthetic test fixture, page 1

fit: reflow re-wraps the paragraph in its own measured alignment, leading and column: and measures the room below BEFORE erasing anything, so the paragraph underneath does not move.

{
  "edits": [
    {
      "find": "sale set out in this schedule",
      "replace": "sale set out in this long and detailed schedule of charges and conditions",
      "fit": "reflow",
      "match": {
        "across_lines": true
      }
    }
  ]
}
beforeBefore: A longer replacement relines the whole paragraph
afterAfter: A longer replacement relines the whole paragraph
08. A date split across two fonts is still one matcha synthetic test fixture, page 2

Renewal: 2025- is one span and 10-01 the next, in a different font. The matcher works on the line, so the phrase is found; the rewrite splits into runs so the untouched spans do not move.

{
  "edits": [
    {
      "find": "Renewal: 2025-10-01",
      "replace": "Renewal: 2026-04-01",
      "scope": {
        "pages": "2"
      }
    }
  ]
}
beforeBefore: A date split across two fonts is still one match
afterAfter: A date split across two fonts is still one match
09. A header shared by three pages, edited on onea synthetic test fixture, page 1

The header is one Form XObject drawn on every page. Editing page 1 un-shares it first and notes unshared_xobject, so pages 2 and 3 keep the original header instead of silently changing too.

{
  "edits": [
    {
      "find": "HEADER 2025 Report",
      "replace": "HEADER 2026 Report",
      "scope": {
        "pages": "1"
      }
    }
  ]
}
beforeBefore: A header shared by three pages, edited on one
afterAfter: A header shared by three pages, edited on one
10. A regex over a matplotlib-rendered invoice numbermatplotlib, Type 42 document, page 1

match.mode: regex (linear time, no lookaround) against text a plotting library emitted one glyph at a time.

{
  "edits": [
    {
      "find": "INV-2026-00\\d\\d",
      "replace": "INV-2026-0177",
      "match": {
        "mode": "regex"
      }
    }
  ]
}
beforeBefore: A regex over a matplotlib-rendered invoice number
afterAfter: A regex over a matplotlib-rendered invoice number

Quick start API reference