Skip to main content

A Python API wrapper for paperless-ngx

Project description

easypaperless

PyPI version License: MIT

⚠️ Alpha Release This project is under active development. Breaking changes might occur, but will be documented in the changelog.


easypaperless is a high-level easy-to-use Python API wrapper for Paperless-ngx.

Unlike other wrappers that simply mirror REST endpoints, easypaperless is designed for humans. It provides a true abstraction layer, allowing you to interact with your document management system using intuitive Python methods and objects. You don't need to understand the underlying REST API to be productive.


✨ Key Features

  • True Abstraction: Focus on your logic, not on HTTP methods or JSON payloads.
  • Developer Experience (DX): Full Type Hinting support. Your IDE (VS Code, PyCharm, etc.) will provide perfect autocompletion and documentation as you type.
  • Extensive Coverage: Currently covers a lot of essential workflows from document management to complex bulk operations.
  • Async-First with Sync Support: Built on top of httpx, easypaperless is fully asynchronous by default for high-performance applications. But it also offers a synchronous wrapper for a classic blocking workflow.
  • Built-in Bulk Tools: Easily manage hundreds of tags, correspondents, or documents with single-method calls.
  • Structured Logging: The library emits structured log records under the easypaperless logger hierarchy. Attach any standard Python logging handler to capture API calls, responses, and errors.
  • Intuitive Error Hierarchy: easypaperless provides descriptive, custom exceptions that tell you exactly what went wrong.

📋 Requirements

  • Python: 3.11 or higher
  • Paperless-ngx: 2.18 or higher (only tested with 2.18 so far)

Core Dependencies:

  • httpx>=0.27
  • Pydantic>=2.0

🚀 Installation

Install the package via pip:

pip install easypaperless

🛠 Quickstart

Get up and running in seconds. No API deep-dive required.

Create a API Authentification token in your paperless-ngx user profile and replace "YOUR_TOKEN" in following code examples by your actual token. Replace url with your actual url. Consider to create a dedicated paperless-ngx user for the api usage and limit the permissions to your needs.

async Client

import asyncio
from easypaperless import PaperlessClient

async def main():

    # create a paperless client
    # we encourage you to use .env files to store your credentials
    url = "http://localhost:8000"
    api_token = "YOUR_TOKEN"
    async with PaperlessClient(url=url, api_token=api_token) as client:
        # List documents — full-text search across title and OCR content, return the last 3 docs
        # list() returns a PagedResult; use .results for the items and .count for the total
        docs = await client.documents.list(
            search="test", max_results=3, ordering="added", descending=True
        )
        print(f"Total matching: {docs.count}")
        for doc in docs.results:
            print(f"Id: {doc.id} \nTitle: {doc.title} \nadded: {doc.added}\n")

        # Fetch a single document
        doc = await client.documents.get(id=1)
        print(doc.title, doc.created)

        # check if a "API_edited" tag already exists - otherwise create it.
        tags = await client.tags.list(name_exact="API_edited")
        if not tags.results:
            await client.tags.create(name="API_edited", color="#40bfb7")

        # Update metadata — string names are resolved to IDs automatically
        await client.documents.update(id=1, tags=["API_edited"])

        # Upload and wait for processing to complete
        # doc = await client.documents.upload("path/scan.pdf", title="your title here", wait=True)
        # print("Processed:", doc.id)


asyncio.run(main())

sync Client

from easypaperless import SyncPaperlessClient

# same example with the sync client:
# we encourage you to use .env files to store your credentials
url = "http://localhost:8000"
api_token = "YOUR_TOKEN"
with SyncPaperlessClient(url=url, api_token=api_token) as client:
    # List documents — full-text search across title and OCR content, return the last 3 docs
    # list() returns a PagedResult; use .results for the items and .count for the total
    docs = client.documents.list(search="test", max_results=3, ordering="added", descending=True)
    print(f"Total matching: {docs.count}")
    for doc in docs.results:
        print(f"Id: {doc.id} \nTitle: {doc.title} \nadded: {doc.added}\n")

    # Fetch a single document
    doc1 = client.documents.get(id=1)
    print(doc1.title, doc1.created)

    # check if a "API_edited" tag already exists - otherwise create it.
    tags = client.tags.list(name_exact="API_edited")
    if not tags.results:
        client.tags.create(name="API_edited", color="#40bfb7")

    # Update metadata — string names are resolved to IDs automatically
    doc_after_update = client.documents.update(id=1, tags=["API_edited"])
    print(doc_after_update.title, doc_after_update.created, doc_after_update.tags)

📖 Functional Overview

All functionality is accessed through resource attributes on the client (e.g. client.documents, client.tags). Both PaperlessClient (async) and SyncPaperlessClient (sync) expose the same resources and methods.

All list() methods return a PagedResult[T] containing .results (the items), .count (total matches from the server), .next / .previous (raw page URLs when requesting a single page), and .all (all matching IDs when provided by the API).

The UNSET sentinel (importable from easypaperless) allows you to distinguish "not provided" from explicit None in optional parameters. Omitting a parameter or passing UNSET leaves the field unchanged; passing None explicitly clears a nullable field.

Client

  • PaperlessClient / SyncPaperlessClient Token-based authentication. Configurable timeout, poll interval, and poll timeout for slow instances. Optional built-in retry-with-exponential-backoff for transient errors (retry_attempts, retry_backoff, retry_on); also accepts a pre-configured tenacity.AsyncRetrying instance for advanced retry strategies.

client.documents — Documents

  • list() Search and filter documents by title, content, tags, dates, correspondent, and more. The workhorse of the wrapper.
  • get() Fetch a single document's data; optionally includes extended file metadata.
  • get_metadata() Retrieve file-level technical metadata (checksums, sizes, MIME types) for a specific document.
  • update() Partially update document fields like title, tags, or dates using PATCH semantics. Supports UNSET to distinguish "not provided" from explicit None.
  • delete() Permanently remove a document from your Paperless-ngx instance.
  • download() Download the binary content of a document — either the archived PDF or the original file.
  • thumbnail() Download the thumbnail image of a document as raw bytes.
  • upload() Upload a new file to Paperless-ngx and optionally wait for the processing task to complete.
  • history() Retrieve the full audit log for a document as a PagedResult[AuditLogEntry], showing who changed what and when.

Document Bulk Operations

  • bulk_add_tag() Add a single tag to a collection of documents in one request.
  • bulk_remove_tag() Strip a specific tag from multiple documents simultaneously.
  • bulk_modify_tags() Atomically add and remove multiple tags across a set of documents.
  • bulk_delete() Permanently delete a list of documents in a single batch.
  • bulk_set_correspondent() Assign or clear the correspondent for multiple documents at once.
  • bulk_set_document_type() Change the document type for a group of documents in one go.
  • bulk_set_storage_path() Update the storage path for multiple documents simultaneously.
  • bulk_modify_custom_fields() Batch update or remove custom field values across multiple documents.
  • bulk_set_permissions() Manage ownership and access permissions for a list of documents.
  • bulk_download() Download multiple documents as a single ZIP archive. Supports choosing the file variant (archive, originals, or both), compression algorithm, and optional filename formatting.

client.documents.notes — Document Notes

  • list() Retrieve all text notes attached to a specific document.
  • create() Add a new text note to an existing document.
  • delete() Remove a specific note from a document.

client.tags — Tags

  • list() / get() / create() / update() / delete() Full CRUD for document tags, supporting colors and matching algorithms.
  • bulk_delete() Batch delete tags.
  • bulk_set_permissions() Batch update access control and ownership for tags.

client.correspondents — Correspondents

  • list() / get() / create() / update() / delete() Full CRUD for document authors, senders, or recipients.
  • bulk_delete() Batch delete correspondents.
  • bulk_set_permissions() Batch update access control and ownership for correspondents.

client.document_types — Document Types

  • list() / get() / create() / update() / delete() Full CRUD for categories like "Invoice," "Letter," or "Contract."
  • bulk_delete() Batch delete document types.
  • bulk_set_permissions() Batch update access control and ownership for document types.

client.storage_paths — Storage Paths

  • list() / get() / create() / update() / delete() Full CRUD for the physical directory structure where files are stored.
  • bulk_delete() Batch delete storage paths.
  • bulk_set_permissions() Batch update access control and ownership for storage paths.

client.custom_fields — Custom Fields

  • list() / get() / create() / update() / delete() Full CRUD for user-defined metadata fields for advanced document tracking.

client.users — Users

  • list() / get() / create() / update() / delete() Full CRUD for Paperless-ngx user accounts. Supports filtering by username and paginated results.
  • Use PaperlessPermission (a typed Literal alias) for type-safe permission checks against user.user_permissions or user.inherited_permissions.

client.trash — Trash

  • list() List all documents currently in the Paperless-ngx trash bin. Returns PagedResult[Document].
  • restore() Recover one or more trashed documents back to active status.
  • empty() Permanently and irreversibly delete trashed documents. This operation cannot be undone.

📚 Documentation

See the 👉 Full API Reference (pdoc)

🤝 Contributing

Contributions are welcome! If you find a bug or have a feature request, please open an issue.

📄 License

This project is licensed under the MIT License.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

easypaperless-0.6.0.tar.gz (486.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

easypaperless-0.6.0-py3-none-any.whl (71.5 kB view details)

Uploaded Python 3

File details

Details for the file easypaperless-0.6.0.tar.gz.

File metadata

  • Download URL: easypaperless-0.6.0.tar.gz
  • Upload date:
  • Size: 486.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for easypaperless-0.6.0.tar.gz
Algorithm Hash digest
SHA256 6e4247eb0dc1a290b2ef737f012f6d05b47357a8d7a2b93cb5e6d25ea6c2ef50
MD5 67a792fe30be92b35a4daa7a7de3260d
BLAKE2b-256 4deacc86eb519e7af825d05d91e74ee6b046942e8d116d9f8c382b5ebb016da5

See more details on using hashes here.

File details

Details for the file easypaperless-0.6.0-py3-none-any.whl.

File metadata

  • Download URL: easypaperless-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 71.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for easypaperless-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9c005057deb4f6ee36dce5c916e34b04661cce124dbe26a12d3434e6ee2d275d
MD5 44f5eb5ab7530cede735b6f5a542cecb
BLAKE2b-256 d949962c571804be514ab4e1a83c5d4fff0723f70429a4e0ee7e3205962218f6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page