GroupDocs.Redaction for Python via .NET is designed to work smoothly with AI coding assistants such as Claude Code, Cursor, and GitHub Copilot in agent mode.
Built into the package
The groupdocs-redaction-net wheel ships a bundled AGENTS.md reference. Once the package is installed, AI tools discover it automatically at groupdocs/redaction/AGENTS.md. It covers the canonical imports, the load → apply → save workflow, per-operation recipes, licensing, the full API-surface tables, and troubleshooting — everything an agent needs to write correct redaction code without guessing.
MCP server
For on-demand documentation lookups, point your AI tool at the GroupDocs MCP server:
The complete reference bundled inside the wheel is reproduced below.
# GroupDocs.Redaction for Python via .NET -- AGENTS.md
> Instructions for AI agents working with this package.
Permanently remove sensitive content from documents -- redact text by exact phrase or regex, scrub or rewrite metadata, replace or delete annotations, black out image regions, remove pages, and rasterize the result so nothing redacted can be recovered. Works across Word, Excel, PowerPoint, PDF, images, and text formats through one unified API, with no MS Office or external software installed.
## Install
```bash
pip install groupdocs-redaction-net
```**Python**: 3.5 - 3.14 | **Platforms**: Windows, Linux, macOS
## Resources
| Resource | URL |
|---|---|
| Documentation | https://docs.groupdocs.com/redaction/python-net/ |
| LLM-optimized docs | https://docs.groupdocs.com/redaction/python-net/llms-full.txt |
| API reference | https://reference.groupdocs.com/redaction/python-net/ |
| Code examples | https://docs.groupdocs.com/redaction/python-net/developer-guide/ |
| Release notes | https://releases.groupdocs.com/redaction/python-net/release-notes/ |
| PyPI | https://pypi.org/project/groupdocs-redaction-net/ |
| Free support forum | https://forum.groupdocs.com/c/redaction/ |
| Temporary license | https://purchase.groupdocs.com/temporary-license |
## MCP Server
If your environment has MCP configured, you can connect your AI tool to the GroupDocs documentation server for on-demand API lookups:
```json
{"mcpServers":{"groupdocs-docs":{"url":"https://docs.groupdocs.com/mcp"}}}```Works with Claude Code (`~/.claude/settings.json`), Cursor (`.cursor/mcp.json`), VS Code Copilot (`.vscode/mcp.json`), and any MCP-compatible client. If MCP is unavailable, fall back to the LLM-optimized docs URL above and this file -- both are shipped inside the wheel.
## Imports
```python
fromgroupdocs.redactionimport(License,Metered,Redactor,RedactionPolicy,RedactionResult,RedactionStatus,RedactorChangeLog,RedactorLogEntry,FileType,IDocumentInfo,)fromgroupdocs.redaction.redactionsimport(# TextExactPhraseRedaction,RegexRedaction,CellColumnRedaction,CellFilter,PageAreaRedaction,ReplacementOptions,RegionReplacementOptions,# MetadataEraseMetadataRedaction,MetadataSearchRedaction,MetadataFilters,# AnnotationsAnnotationRedaction,DeleteAnnotationRedaction,# Image / pageImageAreaRedaction,RemovePageRedaction,PageSeekOrigin,# Custom rules / callbackICustomRedactionHandler,CustomRedactionContext,CustomRedactionResult,IRedactionCallback,RedactionDescription,RedactionType,RedactionActionType,)fromgroupdocs.redaction.optionsimport(LoadOptions,SaveOptions,RasterizationOptions,AdvancedRasterizationOptions,PdfComplianceLevel,PreviewOptions,PreviewFormats,RedactorSettings,)fromgroupdocs.redaction.exceptionsimport(GroupDocsRedactionException,DocumentFormatException,IncorrectPasswordException,PasswordRequiredException,TrialLimitationsException,)fromgroupdocs.pydrawingimportColor,Point,Size# for box / image-area redactions```## Load + Apply + Save (the core workflow)
`Redactor` is the entry point. The flow is always: **open → one or more `apply(...)` calls → `save()`**. Each redaction mutates the in-memory document, so you can apply several before a single save. Use `Redactor` as a context manager so the native document handle is released.
```python
fromgroupdocs.redactionimportRedactorfromgroupdocs.redaction.redactionsimportExactPhraseRedaction,ReplacementOptionswithRedactor("document.docx")asredactor:redactor.apply(ExactPhraseRedaction("confidential",ReplacementOptions("[REDACTED]")))redactor.save()```**Redactor constructor.**`Redactor(file_path)` or `Redactor(stream)`, optionally with `load_options` and/or `RedactorSettings`: `Redactor(file_path, LoadOptions(password="..."))`, `Redactor(stream, LoadOptions(...), RedactorSettings(...))`.
**`apply(...)`** accepts a single `Redaction`, a list of redactions, or a `RedactionPolicy`. It returns a `RedactorChangeLog` whose `.status` is a `RedactionStatus` (`APPLIED`, `PARTIALLY_APPLIED`, `SKIPPED`, `FAILED`).
**`save(...)`** writes the current state and returns the output path (when saving to a file). **By default it rasterizes the document to a PDF and appends a `_Redacted` suffix.** To control this, pass `SaveOptions` or `RasterizationOptions` (see Rasterization below).
## Operations
### Text redaction (exact phrase / regex)
```python
fromgroupdocs.redactionimportRedactorfromgroupdocs.redaction.redactionsimportExactPhraseRedaction,RegexRedaction,ReplacementOptionsfromgroupdocs.pydrawingimportColorwithRedactor("document.docx")asredactor:redactor.apply(ExactPhraseRedaction("John Doe",ReplacementOptions("[CUSTOMER]")))# case-insensitiveredactor.apply(ExactPhraseRedaction("ACME",True,ReplacementOptions("[CO]")))# case-sensitiveredactor.apply(RegexRedaction(r"\d{2,}",ReplacementOptions("[NUM]")))# regex matchredactor.apply(RegexRedaction(r"\bsecret\b",ReplacementOptions(Color.BLACK)))# draw a black boxredactor.save()````ReplacementOptions(text)` replaces the match with a string; `ReplacementOptions(Color)` draws a filled box over it. `ExactPhraseRedaction` also exposes `is_right_to_left`. `CellColumnRedaction(CellFilter, regex, ReplacementOptions)` targets a spreadsheet column.
### Metadata redaction
```python
fromgroupdocs.redactionimportRedactorfromgroupdocs.redaction.redactionsimportEraseMetadataRedaction,MetadataSearchRedaction,MetadataFilterswithRedactor("document.docx")asredactor:redactor.apply(EraseMetadataRedaction(MetadataFilters.ALL))# erase everythingredactor.apply(EraseMetadataRedaction(MetadataFilters.AUTHOR))# erase one fieldredactor.apply(MetadataSearchRedaction(".*@acme\\.com","[EMAIL]"))# rewrite by value patternredactor.save()````MetadataFilters` is a flags-style enum: `AUTHOR`, `COMPANY`, `COMMENTS`, `MANAGER`, `TITLE`, `SUBJECT`, `KEYWORDS`, `CONTENT_STATUS`, … and `ALL`. `MetadataSearchRedaction(value_pattern, replacement, key_pattern=...)` can match on key and/or value.
### Annotation redaction
```python
fromgroupdocs.redactionimportRedactorfromgroupdocs.redaction.redactionsimportAnnotationRedaction,DeleteAnnotationRedactionwithRedactor("document.pdf")asredactor:redactor.apply(AnnotationRedaction("(?i)approved","[REVIEW]"))# rewrite annotation textredactor.apply(DeleteAnnotationRedaction("(?i)internal"))# delete matching annotationsredactor.save()```### Image-area and page-area redaction
```python
fromgroupdocs.redactionimportRedactorfromgroupdocs.redaction.redactionsimportImageAreaRedaction,PageAreaRedaction,RegionReplacementOptions,ReplacementOptionsfromgroupdocs.pydrawingimportPoint,Size,ColorwithRedactor("scan.pdf")asredactor:# black out a fixed rectangle on every pageredactor.apply(ImageAreaRedaction(Point(50,60),RegionReplacementOptions(Color.BLACK,Size(200,80))))# redact text AND its image rendering in a page arearedactor.apply(PageAreaRedaction(r"\d{3}-\d{2}-\d{4}",ReplacementOptions("[SSN]"),RegionReplacementOptions(Color.BLACK,Size(120,20))))redactor.save()```### Page removal
```python
fromgroupdocs.redactionimportRedactorfromgroupdocs.redaction.redactionsimportRemovePageRedaction,PageSeekOriginwithRedactor("document.pdf")asredactor:redactor.apply(RemovePageRedaction(PageSeekOrigin.BEGIN,0,1))# drop the first pageredactor.save()```### Custom redaction rule / callback
A `RedactorSettings(callback=...)` receives every redaction description and decides whether to accept it. Pass a plain Python callable -- the binding wraps it as the .NET `IRedactionCallback` automatically. `RedactorSettings` is the **third** Redactor argument, after `load_options`, so pass `LoadOptions()` (or a configured one) as well.
```python
fromgroupdocs.redactionimportRedactorfromgroupdocs.redaction.redactionsimportExactPhraseRedaction,ReplacementOptionsfromgroupdocs.redaction.optionsimportLoadOptions,RedactorSettingsdefaccept(description):# description.original_text, .redaction_type, .action_typereturn"keep-me"notin(description.original_textor"")# return False to skip this matchwithRedactor("document.docx",LoadOptions(),RedactorSettings(callback=accept))asredactor:redactor.apply(ExactPhraseRedaction("secret",ReplacementOptions("[X]")))redactor.save()```The callback returns `bool` (accept/reject) -- `IRedactionCallback.accept_redaction` returns a boolean. For text-rewriting rules use `ICustomRedactionHandler` via `ReplacementOptions.custom_redaction`.
### Rasterization (flatten to PDF)
```python
fromgroupdocs.redactionimportRedactorfromgroupdocs.redaction.redactionsimportExactPhraseRedaction,ReplacementOptionsfromgroupdocs.redaction.optionsimportSaveOptions,RasterizationOptions,AdvancedRasterizationOptionswithRedactor("document.docx")asredactor:redactor.apply(ExactPhraseRedaction("secret",ReplacementOptions("[X]")))# (a) default: rasterize to PDF, "_Redacted" suffixredactor.save()# (b) keep original format, no rasterizationredactor.save(SaveOptions(rasterize_to_pdf=False))# (c) rasterize with anti-extraction effectsro=RasterizationOptions()ro.enabled=Truero.add_advanced_option(AdvancedRasterizationOptions.NOISE)ro.add_advanced_option(AdvancedRasterizationOptions.GRAYSCALE)redactor.save(ro)````RasterizationOptions`: `enabled`, `page_index`, `page_count`, `compliance` (`PdfComplianceLevel.AUTO` / `PDF_A1A`), `add_advanced_option(AdvancedRasterizationOptions.{TILT,NOISE,BORDER,GRAYSCALE})`.
### Save to a stream
`save(stream, rasterization_options)` writes to any writable stream. Return a path-backed or `io.BytesIO` stream; `BytesIO` is updated after the call.
```python
importiofromgroupdocs.redaction.optionsimportRasterizationOptionswithRedactor("document.docx")asredactor:redactor.apply(ExactPhraseRedaction("secret",ReplacementOptions("[X]")))ro=RasterizationOptions();ro.enabled=False# keep original formatbuf=io.BytesIO()redactor.save(buf,ro)data=buf.getvalue()```### Redaction policy (reusable rule set)
```python
fromgroupdocs.redactionimportRedactor,RedactionPolicyfromgroupdocs.redaction.redactionsimportExactPhraseRedaction,RegexRedaction,ReplacementOptionspolicy=RedactionPolicy([ExactPhraseRedaction("ACME",ReplacementOptions("[CO]")),RegexRedaction(r"\d{2,}",ReplacementOptions("[NUM]")),])withRedactor("document.docx")asredactor:redactor.apply(policy=policy)redactor.save()```Build the policy in memory and reuse it across documents. (A policy can also be authored as an XML file and read with `RedactionPolicy.load(path)`.)
### Document info & preview
```python
withRedactor("document.pdf")asredactor:info=redactor.get_document_info()print(info.file_type.file_format,info.page_count,info.size)````get_document_info()` returns an `IDocumentInfo`: `file_type` (a `FileType`), `page_count`, `size`, `pages`. `generate_preview(PreviewOptions(...))` renders pages to images (`PreviewFormats.PNG`/`JPEG`/`BMP`) via a `create_page_stream(page_number)` callback that returns a writable file/path stream (not `BytesIO`).
## Licensing
```python
fromgroupdocs.redactionimportLicense# From fileLicense().set_license("path/to/license.lic")# From streamwithopen("license.lic","rb")asf:License().set_license(f)```Or auto-apply: `export GROUPDOCS_LIC_PATH="path/to/license.lic"`Metered licensing is also available:
```python
fromgroupdocs.redactionimportMeteredMetered().set_metered_key("public-key","private-key")print(Metered().get_consumption_quantity(),Metered().get_consumption_credit())```**Evaluation vs licensed.** Without a license the library still runs, but **only one document may be opened per process** (subsequent opens raise a trial-limitation error), PDF output carries an evaluation watermark, and other formats show an equivalent evaluation mark. Set `GROUPDOCS_LIC_PATH` (or call `License().set_license(...)`) and re-run to clear these. A 30-day full license is free: https://purchase.groupdocs.com/temporary-license
## API Reference
### Redactor
| Method | Returns | Description |
|---|---|---|
| `Redactor(file_path / stream [, load_options [, settings]])` | | Open by path or binary stream; optional `LoadOptions` and `RedactorSettings`. Use as a context manager. |
| `apply(redaction)` / `apply(redactions=[...])` / `apply(policy=...)` | `RedactorChangeLog` | Apply one redaction, a list, or a `RedactionPolicy`. `.status` is a `RedactionStatus`. |
| `save([save_options])` | `str` | Write to disk; default rasterizes to PDF + `_Redacted` suffix. Returns the output path. |
| `save(stream [, rasterization_options])` | `str` | Write to a stream (file/path stream or `io.BytesIO`). |
| `generate_preview(preview_options)` | `None` | Render pages to images via a `create_page_stream` callback. |
| `get_document_info()` | `IDocumentInfo` | `file_type`, `page_count`, `size`, `pages`. |
| `dispose()` | `None` | Release native resources (handled by `with`). |
### Redactions
| Type | Notes |
|---|---|
| `ExactPhraseRedaction(phrase [, is_case_sensitive], options)` | Replace/box an exact phrase. `is_right_to_left` for RTL. |
| `RegexRedaction(pattern, options)` | Replace/box a regex match. |
| `CellColumnRedaction(CellFilter, regex, options)` | Redact a spreadsheet column. |
| `PageAreaRedaction(regex, options, RegionReplacementOptions)` | Text + image redaction over a page area. |
| `EraseMetadataRedaction(MetadataFilters)` | Erase metadata by filter. |
| `MetadataSearchRedaction(value_pattern, replacement [, key_pattern])` | Rewrite metadata by key/value pattern. |
| `AnnotationRedaction(pattern, replacement)` / `DeleteAnnotationRedaction(pattern)` | Rewrite or delete annotations. |
| `ImageAreaRedaction(Point, RegionReplacementOptions)` | Black out a rectangle on a page. |
| `RemovePageRedaction(PageSeekOrigin, index, count)` | Remove pages from the start/end. |
| `ReplacementOptions(text)` / `ReplacementOptions(Color)` | Text replacement vs. a filled box. `filters`, `custom_redaction`. |
| `RegionReplacementOptions(fill_color, size [, font, expected_text])` | Box geometry for image/page-area redactions. |
### Options & enums
| Type | Notes |
|---|---|
| `LoadOptions(password=..., pre_rasterize=...)` | Open protected / pre-rasterized input. |
| `SaveOptions(rasterize_to_pdf=..., suffix=...)` | Control format + `_Redacted` suffix. |
| `RasterizationOptions()` | `enabled`, `page_index`, `page_count`, `compliance`, `add_advanced_option(...)`. |
| `AdvancedRasterizationOptions` | `NONE`, `TILT`, `NOISE`, `BORDER`, `GRAYSCALE`. |
| `PdfComplianceLevel` | `AUTO`, `PDF_A1A`. |
| `PreviewOptions(create_page_stream [, release_page_stream])` | `width`, `height`, `page_numbers`, `preview_format`. |
| `PreviewFormats` | `PNG`, `JPEG`, `BMP`. |
| `RedactorSettings(logger=..., callback=..., ocr_connector=...)` | Inject a logger, an `IRedactionCallback`, or OCR. |
| `MetadataFilters` | `AUTHOR`, `COMPANY`, `COMMENTS`, `MANAGER`, `TITLE`, `SUBJECT`, `KEYWORDS`, …, `ALL`. |
| `RedactionStatus` | `APPLIED`, `PARTIALLY_APPLIED`, `SKIPPED`, `FAILED`. |
| `RedactionType` / `RedactionActionType` | `TEXT`/`METADATA`/`ANNOTATION`/`IMAGE_AREA`/`PAGE`; `REPLACEMENT`/`CLEANUP`/`DELETION`. |
| `PageSeekOrigin` | `BEGIN`, `END`. |
### License / Metered
`License().set_license(path_or_stream)` · `Metered().set_metered_key(public, private)` · `Metered().get_consumption_quantity()` · `Metered().get_consumption_credit()`## Key Patterns
- **Properties**: use `snake_case` -- auto-mapped to .NET `PascalCase`- **Context managers**: `with Redactor(...) as r:` ensures the document handle is released
- **Chaining**: each `apply()` mutates in place; run several before one `save()`- **Default save rasterizes**: `save()` produces a PDF + `_Redacted` suffix; pass `SaveOptions(rasterize_to_pdf=False)` to keep the source format
- **Colors / geometry**: `Color`, `Point`, `Size` come from `groupdocs.pydrawing`; `ReplacementOptions(Color)` draws a box
- **Regex args**: pass a plain Python regex string -- the binding wraps it as a .NET `Regex`- **Streams**: pass `open("file", "rb")` or `io.BytesIO(data)` where .NET expects a Stream; `BytesIO` is updated after `save(stream)`- **Enums**: case-insensitive, lazy-loaded (e.g., `MetadataFilters.ALL`, `PreviewFormats.PNG`)
- **Callbacks**: a Python callable is accepted for `IRedactionCallback`; it returns `bool` to accept/reject each match
- **Exceptions**: catch `PasswordRequiredException` / `IncorrectPasswordException` / `DocumentFormatException` / `TrialLimitationsException` (all subclass `GroupDocsRedactionException`)
## Platform Requirements
| Platform | Requirements |
|---|---|
| Windows | None |
| Linux | `apt install libgdiplus libfontconfig1 ttf-mscorefonts-installer` |
| macOS | `brew install mono-libgdiplus` |
## Troubleshooting
**`Trial mode allows only 1 document to open` / `TrialLimitationsException`** -- no license. Apply one with `License().set_license(...)` or set `GROUPDOCS_LIC_PATH`; a free 30-day license is at https://purchase.groupdocs.com/temporary-license
**`PasswordRequiredException` / `IncorrectPasswordException`** -- the source is encrypted. Open it with `Redactor(path, LoadOptions(password="..."))`.
**`DocumentFormatException`** -- the file format isn't supported or the file is corrupted. Check it against the supported-formats list.
**`System.Drawing.Common is not supported`** -- install libgdiplus: `sudo apt install libgdiplus` (Linux) / `brew install mono-libgdiplus` (macOS)
**`Gdip` type initializer exception** -- outdated libgdiplus: `brew reinstall mono-libgdiplus` (macOS)
**Garbled text / missing fonts** -- install fonts: `sudo apt install ttf-mscorefonts-installer fontconfig && sudo fc-cache -f`**`DllNotFoundException: libSkiaSharp`** -- stale system copy conflicts with bundled version. Rename it: `sudo mv /usr/local/lib/libSkiaSharp.dylib /usr/local/lib/libSkiaSharp.dylib.bak`**`DOTNET_SYSTEM_GLOBALIZATION_INVARIANT` errors** -- do NOT set this. Install ICU: `sudo apt install libicu-dev`**`TypeLoadException`** -- reinstall: `pip install --force-reinstall groupdocs-redaction-net`**Still stuck?** Post your question at https://forum.groupdocs.com/c/redaction/ -- the development team responds directly.
Was this page helpful?
Any additional feedback you'd like to share with us?
Please tell us how we can improve this page.
Thank you for your feedback!
We value your opinion. Your feedback will help us improve our documentation.