The Problem Space
You need to manipulate Word documents programmatically. Your options:
- Microsoft's APIs - Graph, Open XML SDK, Word JS API
- Commercial SDKs - Aspose, Syncfusion, GrapeCity
- Open Source - python-docx, docx4j, OpenXML SDK
- Conversion Services - CloudConvert, Gotenberg
- Purpose-Built APIs - DocMods, etc.
Each has different capabilities, limitations, and pricing. This is the honest breakdown.
Microsoft's Ecosystem
Microsoft Graph API
Graph is Microsoft's unified API for Microsoft 365 services.
What it CAN do:
- Upload/download files to OneDrive/SharePoint
- Convert DOCX to PDF (via OneDrive)
- Read file metadata
- Manage permissions and sharing
- Trigger Microsoft 365 workflows
What it CANNOT do:
- Edit document content via API
- Modify text, tables, or images in place
- Access track changes
- Manipulate OOXML structure
// Microsoft Graph - File operations only
const graph = require('@microsoft/microsoft-graph-client');
// Upload a document
await graphClient.api('/me/drive/root:/Documents/contract.docx:/content')
.put(fileBuffer);
// Convert to PDF (via OneDrive conversion)
const pdfStream = await graphClient
.api('/me/drive/root:/Documents/contract.docx:/content?format=pdf')
.getStream();
// You CANNOT do this:
// await graphClient.api('/me/drive/root:/Documents/contract.docx')
// .patch({ content: { paragraph1: "new text" } });
// This doesn't exist
Requirements:
- Azure AD app registration
- Microsoft 365 subscription
- Files must be in OneDrive/SharePoint
- OAuth 2.0 authentication
Pricing:
- Graph API itself is "free" with Microsoft 365 subscription
- You're paying for Microsoft 365 ($12.50-$38/user/month)
Open XML SDK (.NET)
Microsoft's open-source SDK for OOXML manipulation.
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using (var doc = WordprocessingDocument.Open("document.docx", true))
{
var body = doc.MainDocumentPart.Document.Body;
// Find and replace text
foreach (var text in body.Descendants<Text>())
{
if (text.Text.Contains("{{PLACEHOLDER}}"))
{
text.Text = text.Text.Replace("{{PLACEHOLDER}}", "Actual Value");
}
}
// Add a paragraph
body.AppendChild(new Paragraph(
new Run(new Text("New paragraph added."))
));
doc.MainDocumentPart.Document.Save();
}
Pros:
- Free and open source
- Full OOXML access
- Microsoft-supported
- No external dependencies
Cons:
- .NET only
- Low-level API (verbose code)
- Track changes require manual XML manipulation
- No rendering (can't preview or convert)
Word JavaScript API
For Office Add-ins that run inside Word.
// Only works inside Word as an add-in
Word.run(async (context) => {
const body = context.document.body;
// Insert text
body.insertText("Hello, World!", Word.InsertLocation.end);
// Find and replace
const searchResults = body.search("old text", { matchCase: true });
searchResults.load("items");
await context.sync();
for (const result of searchResults.items) {
result.insertText("new text", Word.InsertLocation.replace);
}
await context.sync();
});
Limitations:
- Requires Word to be running
- Can't run server-side
- Limited to add-in scenarios
- No batch processing
Commercial SDKs
Aspose.Words
The "gold standard" commercial SDK. Available for .NET, Java, Python, C++, Node.js.
import aspose.words as aw
# Load document
doc = aw.Document("input.docx")
# Find and replace
doc.range.replace("{{CLIENT_NAME}}", "Acme Corporation", aw.replacing.FindReplaceOptions())
# Access track changes (revisions)
for revision in doc.revisions:
print(f"{revision.revision_type}: {revision.parent_node.get_text()}")
# Accept specific revisions
doc.revisions[0].accept()
# Convert to PDF
doc.save("output.pdf")
Capabilities:
- Complete OOXML support
- Track changes read/write
- Mail merge
- PDF conversion (high fidelity)
- 100+ format conversions
- Document comparison
- Digital signatures
Pricing (2025):
| License Type | Price | Notes |
|---|---|---|
| Developer Small Business | $1,199/year | 1 developer |
| Developer OEM | $5,997/year | Distribution rights |
| Site Small Business | $3,597/year | Unlimited developers |
| Metered | ~$0.10/call | Pay-per-use |
Cons:
- Expensive
- Large dependency
- Some features require native libraries
- Licensing complexity
Syncfusion DocIO
Cheaper Aspose alternative, .NET ecosystem.
using Syncfusion.DocIO;
using Syncfusion.DocIO.DLS;
using (WordDocument document = new WordDocument("input.docx"))
{
// Find and replace
document.Replace("{{PLACEHOLDER}}", "Value", true, true);
// Access revisions
foreach (Revision revision in document.Revisions)
{
Console.WriteLine($"{revision.RevisionType}: {revision.Range.Text}");
}
// Save as PDF
using (DocIORenderer renderer = new DocIORenderer())
{
PdfDocument pdfDoc = renderer.ConvertToPDF(document);
pdfDoc.Save("output.pdf");
}
}
Pricing:
| License Type | Price |
|---|---|
| Essential Studio | $995/year (per developer) |
| Team License | $2,500/year (5 developers) |
| Community License | Free (< $1M revenue, < 5 developers) |
Pros:
- Good track changes support
- Community license available
- PDF conversion included
- Lower cost than Aspose
Cons:
- .NET focus (limited cross-platform)
- Less comprehensive than Aspose
- Some advanced features require paid tier
GrapeCity Documents for Word
Another commercial option, focus on performance.
Pricing: ~$1,449/year per developer
Similar capabilities to Aspose/Syncfusion with focus on:
- High-performance processing
- Low memory footprint
- .NET Core support
Open Source Options
python-docx (Python)
from docx import Document
doc = Document("input.docx")
for para in doc.paragraphs:
if "{{NAME}}" in para.text:
for run in para.runs:
run.text = run.text.replace("{{NAME}}", "John Doe")
doc.add_paragraph("New paragraph at the end.")
doc.save("output.docx")
Limitations:
- No track changes support
- No PDF conversion
- Tables are awkward
- Headers/footers are limited
- Can corrupt complex documents
See our python-docx track changes guide for details.
docx4j (Java)
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage
.load(new File("input.docx"));
MainDocumentPart mainPart = wordMLPackage.getMainDocumentPart();
// Search and replace
mainPart.variableReplace(
Map.of("placeholder", "value")
);
// Save
wordMLPackage.save(new File("output.docx"));
Pros:
- More comprehensive than python-docx
- Partial track changes support
- PDF conversion (via Plutext commercial add-on)
Cons:
- Complex API
- Memory intensive
- PDF conversion requires commercial license
LibreOffice Headless
Use LibreOffice as a conversion/manipulation engine.
import subprocess
# Convert DOCX to PDF
subprocess.run([
'soffice',
'--headless',
'--convert-to', 'pdf',
'--outdir', './output',
'input.docx'
])
Pros:
- Free
- Many format conversions
- Cross-platform
Cons:
- Slow startup
- Formatting differences from Word
- Difficult to make granular edits
- Resource intensive
- Not thread-safe (need multiple processes)
Conversion-Focused Services
CloudConvert
curl -X POST "https://api.cloudconvert.com/v2/jobs" \
-H "Authorization: Bearer API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tasks": {
"import-file": {
"operation": "import/upload"
},
"convert": {
"operation": "convert",
"input": "import-file",
"output_format": "pdf"
},
"export": {
"operation": "export/url",
"input": "convert"
}
}
}'
Pricing:
- 25 free conversions/day
- Packages from $8/500 conversions
- Subscription from $8/month (1,000/month)
Best for: Format conversion, not document editing.
Gotenberg
Self-hosted document conversion via Docker.
# Start Gotenberg
docker run --rm -p 3000:3000 gotenberg/gotenberg:7
# Convert DOCX to PDF
curl -X POST http://localhost:3000/forms/libreoffice/convert \
-F files=@document.docx \
-o output.pdf
Pros:
- Self-hosted (no per-call costs)
- Uses LibreOffice under the hood
- Clean API
Cons:
- Conversion only (no editing)
- LibreOffice formatting limitations
- Requires Docker infrastructure
Track Changes: The Gap in the Market
Here's the reality: most DOCX APIs handle track changes poorly or not at all.
| Solution | Read Track Changes | Write Track Changes | Preserve Track Changes |
|---|---|---|---|
| Microsoft Graph | ❌ | ❌ | ✅ (passthrough) |
| Open XML SDK | ⚠️ Manual | ⚠️ Manual | ✅ |
| Aspose.Words | ✅ | ✅ | ✅ |
| Syncfusion | ✅ | ✅ | ✅ |
| python-docx | ❌ | ❌ | ❌ |
| docx4j | ⚠️ Partial | ⚠️ Partial | ⚠️ Partial |
| LibreOffice | ⚠️ Via UNO | ⚠️ Via UNO | ⚠️ |
| DocMods | ✅ | ✅ | ✅ |
If track changes are critical to your workflow (legal, contracts, regulated industries), your options narrow significantly.
DocMods API
Purpose-built for document editing with track changes.
from docxagent import DocxClient
client = DocxClient()
# Upload document
doc_id = client.upload("contract.docx")
# Read with revision awareness
content = client.read(doc_id, include_revisions=True)
# AI-powered editing with track changes
client.edit(
doc_id,
"Replace all instances of 'Net 30' with 'Net 45' and highlight payment terms"
)
# Get revision list
revisions = client.get_revisions(doc_id)
for rev in revisions:
print(f"{rev['type']}: '{rev['text']}' by {rev['author']}")
# Accept/reject specific revisions
client.accept_revision(doc_id, revision_id="1")
client.reject_revision(doc_id, revision_id="2")
# Download with changes tracked
client.download(doc_id, "contract_edited.docx")
Capabilities:
- Full track changes support
- AI-powered editing (not just find/replace)
- Comment management
- Revision accept/reject
- Cross-platform (REST API)
Decision Matrix
"I need to generate documents from templates"
Best choice: docxtpl (Python) or Open XML SDK (.NET)
- Simple, free, well-documented
- Handles most template scenarios
"I need to convert DOCX to PDF"
Budget: Gotenberg (self-hosted) or CloudConvert Quality: Aspose.Words or Microsoft Graph (with OneDrive)
- Graph gives you Word-native rendering
- Aspose gives you local control
"I need to edit documents with track changes"
Enterprise budget: Aspose.Words Developer-focused: DocMods API Windows only: Word COM Automation
"I need to build a document comparison feature"
Commercial: Aspose.Words has built-in comparison DIY: Extract text with python-docx, use diff algorithm, rebuild
"I need to process thousands of documents"
Self-hosted: Aspose.Words or Open XML SDK Managed: DocMods or CloudConvert with high-volume plan
Architecture Patterns
Document Processing Pipeline
from docxagent import DocxClient
import json
def process_contract(template_path, data, output_path):
"""
Full contract processing pipeline:
1. Load template
2. Fill variables
3. AI review with tracked changes
4. Return for human review
"""
client = DocxClient()
# Upload
doc_id = client.upload(template_path)
# Fill template variables
for placeholder, value in data.items():
client.edit(doc_id, f"Replace {{{{{placeholder}}}}} with: {value}")
# AI contract review
client.edit(
doc_id,
"""Review this contract. Flag and suggest fixes for:
- Unusual indemnification terms
- Missing limitation of liability
- Payment terms that don't match industry standard
- Ambiguous definitions
Make changes with track changes. Add comments explaining concerns."""
)
# Get summary
revisions = client.get_revisions(doc_id)
comments = client.get_comments(doc_id)
# Download
client.download(doc_id, output_path)
return {
'output_file': output_path,
'revision_count': len(revisions),
'comment_count': len(comments),
'needs_review': len(revisions) > 0 or len(comments) > 0
}
# Usage
result = process_contract(
'msa_template.docx',
{
'CLIENT_NAME': 'Acme Corp',
'EFFECTIVE_DATE': 'January 1, 2025',
'PAYMENT_TERMS': 'Net 30'
},
'msa_acme_draft.docx'
)
print(json.dumps(result, indent=2))
Batch Processing with Rate Limiting
import asyncio
from docxagent import DocxClient
async def process_batch(files, semaphore):
"""Process files with concurrency limit."""
client = DocxClient()
async def process_one(file_path):
async with semaphore:
doc_id = client.upload(file_path)
# ... processing ...
return doc_id
tasks = [process_one(f) for f in files]
return await asyncio.gather(*tasks)
# Usage: max 5 concurrent
files = ['doc1.docx', 'doc2.docx', ...]
semaphore = asyncio.Semaphore(5)
asyncio.run(process_batch(files, semaphore))
The Bottom Line
The DOCX API landscape is fragmented:
- Microsoft Graph is for file operations, not document editing
- Commercial SDKs (Aspose, Syncfusion) are comprehensive but expensive
- Open source (python-docx) is limited, especially for track changes
- Conversion services handle format conversion, not editing
For track changes support—essential for legal, contracts, and regulated workflows—your realistic options are:
- Aspose.Words ($1,200+/year) - If you need a comprehensive SDK
- DocMods API - If you want AI-powered editing with track changes
- Word COM Automation - If you're Windows-only and can run Word
Choose based on your specific requirements: budget, platform, track changes needs, and whether you need AI assistance or pure programmatic control.



