> For the complete documentation index, see [llms.txt](https://docs.inrupt.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.inrupt.com/ess/services/service-search/ocr-service.md).

# OCR Service

{% hint style="success" %}
Added in version 3.2.0
{% endhint %}

The OCR Service extracts text from PDFs and images for the ESS [Search Service](/ess/services/service-search.md). It is called by the [Ingest Service](/ess/services/service-search/ingest-service.md) during content indexing to convert documents into searchable text.

## How It Works

The OCR Service supports two modes of text extraction:

### PDF Text Extraction

PDFs are processed with a two-pass approach:

1. **Text layer extraction**: The service first extracts the embedded text layer from each page using pypdf. This is fast and produces high-quality text for PDFs that contain a text layer.
2. **OCR fallback**: For pages where the text layer contains fewer than the configured minimum characters (default: 50), the service renders the page to an image and runs OCR using RapidOCR.

This approach ensures that both digitally-created PDFs (with embedded text) and scanned documents (image-only PDFs) are handled correctly.

### Image OCR

Standalone images (PNG, JPEG, GIF, WEBP) are processed directly by RapidOCR.

{% hint style="info" %}
**Graceful Degradation**

The OCR Service can run without the RapidOCR engine available. In this mode, PDF text extraction uses only the text layer (no OCR fallback), and image OCR requests return **`503`**.
{% endhint %}

## API Endpoints

The OCR Service exposes two endpoints on the main API port (default 8443, mTLS in production):

{% hint style="info" %}
**Internal Service**

The OCR Service is an internal ESS service. It is called by the Ingest Service over mTLS and is not directly accessible to external clients.
{% endhint %}

### POST /api/ocr

Extracts text from a PDF document.

#### Input

| Field        | Value                                                                                      |
| ------------ | ------------------------------------------------------------------------------------------ |
| Endpoint     | **`https://enterprise-ocr/api/ocr`**                                                       |
| Method       | **`POST`**                                                                                 |
| Content-Type | **`multipart/form-data`**                                                                  |
| Payload      | File upload (**`file`** field). Must be a valid PDF (starts with **`%PDF-`** magic bytes). |

#### Output

Returns a **`PdfOcrResponse`**:

| Field          | Type                | Description                                 |
| -------------- | ------------------- | ------------------------------------------- |
| **`text`**     | String              | The complete extracted text from all pages. |
| **`pages`**    | Array of PageResult | Per-page extraction results.                |
| **`metadata`** | Object              | Processing metadata.                        |

Each **`PageResult`** contains:

| Field            | Type    | Description                                                              |
| ---------------- | ------- | ------------------------------------------------------------------------ |
| **`pageNumber`** | Integer | The 1-based page number.                                                 |
| **`text`**       | String  | The extracted text for this page.                                        |
| **`method`**     | String  | The extraction method used: **`text_layer`** or **`ocr`**.               |
| **`confidence`** | Number  | Confidence score. **`1.0`** for text layer extraction; variable for OCR. |

The **`metadata`** object contains:

| Field                     | Type    | Description                                          |
| ------------------------- | ------- | ---------------------------------------------------- |
| **`pageCount`**           | Integer | Total number of pages in the PDF.                    |
| **`ocrPagesCount`**       | Integer | Number of pages processed via OCR fallback.          |
| **`textLayerPagesCount`** | Integer | Number of pages processed via text layer extraction. |
| **`processingTimeMs`**    | Integer | Total processing time in milliseconds.               |

#### Example Response

```http
HTTP/1.1 200 OK
Content-Type: application/json

{
  "text": "Page 1 content here...\nPage 2 content here...",
  "pages": [
    {
      "pageNumber": 1,
      "text": "Page 1 content here...",
      "method": "text_layer",
      "confidence": 1.0
    },
    {
      "pageNumber": 2,
      "text": "Page 2 content here...",
      "method": "ocr",
      "confidence": 0.92
    }
  ],
  "metadata": {
    "pageCount": 2,
    "ocrPagesCount": 1,
    "textLayerPagesCount": 1,
    "processingTimeMs": 1850
  }
}
```

### POST /api/ocr-image

Extracts text from a standalone image.

#### Input

| Field        | Value                                                                                                                    |
| ------------ | ------------------------------------------------------------------------------------------------------------------------ |
| Endpoint     | **`https://enterprise-ocr/api/ocr-image`**                                                                               |
| Method       | **`POST`**                                                                                                               |
| Content-Type | **`multipart/form-data`**                                                                                                |
| Payload      | File upload (**`file`** field). Must be one of: **`image/png`**, **`image/jpeg`**, **`image/gif`**, or **`image/webp`**. |

#### Output

Returns an **`ImageOcrResponse`**:

| Field            | Type   | Description                 |
| ---------------- | ------ | --------------------------- |
| **`text`**       | String | The extracted text.         |
| **`confidence`** | Number | OCR confidence score (0–1). |
| **`metadata`**   | Object | Processing metadata.        |

The **`metadata`** object contains:

| Field                  | Type    | Description                                 |
| ---------------------- | ------- | ------------------------------------------- |
| **`processingTimeMs`** | Integer | Processing time in milliseconds.            |
| **`engine`**           | String  | The OCR engine used (e.g., **`rapidocr`**). |

#### Example Response

```http
HTTP/1.1 200 OK
Content-Type: application/json

{
  "text": "Invoice #12345\nDate: 2026-07-15\nTotal: $1,234.56",
  "confidence": 0.95,
  "metadata": {
    "processingTimeMs": 520,
    "engine": "rapidocr"
  }
}
```

## Health Endpoints

The OCR Service provides health and metrics endpoints on the management port (default 9000, HTTPS without client certificate verification):

| Endpoint                | Description                                                                                       |
| ----------------------- | ------------------------------------------------------------------------------------------------- |
| **`GET /health/live`**  | Liveness probe. Returns **`{"status": "up"}`**.                                                   |
| **`GET /health/ready`** | Readiness probe. Returns engine availability status. Returns **`503`** if pypdf is not available. |
| **`GET /metrics`**      | Prometheus metrics.                                                                               |

The readiness response includes engine status:

```json
{
  "status": "up",
  "engines": {
    "pypdf": true,
    "rapidocr": true
  }
}
```

## Configuration

All configuration is via environment variables.

### File and Image Limits

#### MAX\_FILE\_SIZE\_MB

Default: **`50`**

The maximum upload file size in megabytes.

#### MAX\_PDF\_PAGES

Default: **`200`**

The maximum number of pages allowed in a PDF. PDFs exceeding this limit are rejected.

#### MAX\_OCR\_PAGES

Default: **`20`**

The maximum number of pages per PDF to process via OCR fallback. Pages beyond this limit that need OCR are skipped (text layer extraction is still attempted for all pages).

#### MIN\_TEXT\_CHARS

Default: **`50`**

The minimum number of characters a page's text layer must contain before the service considers it sufficient. Pages with fewer characters trigger OCR fallback.

#### MAX\_IMAGE\_DIMENSION

Default: **`10000`**

The maximum width or height in pixels for an uploaded image.

#### MAX\_IMAGE\_PIXELS

Default: **`100000000`**

The maximum total pixel count for an uploaded image. This is a safety limit to prevent decompression bombs.

### PDF Rendering

#### PDF\_RENDER\_DPI

Default: **`200`**

The DPI used when rendering PDF pages to images for OCR fallback. Higher values produce better OCR quality but increase processing time and memory usage.

#### PDF\_RENDER\_TIMEOUT

Default: **`30`**

The per-page timeout in seconds for rendering PDF pages to images.

### Server

#### PORT

Default: **`8443`**

The main API listening port.

#### MANAGEMENT\_PORT

Default: **`9000`**

The health and metrics port.

#### LOG\_LEVEL

Default: **`info`**

The log level. Values: **`debug`**, **`info`**, **`warning`**, **`error`**, **`critical`**.

#### GUNICORN\_WORKERS

Default: **`1`**

The number of Gunicorn worker processes.

### TLS

#### TLS\_CERTFILE

Path to the server TLS certificate file. Must be set together with [**`TLS_KEYFILE`**](#tls_keyfile) and [**`TLS_CA_CERTFILE`**](#tls_ca_certfile), or all must be unset.

#### TLS\_KEYFILE

Path to the server TLS private key file.

#### TLS\_CA\_CERTFILE

Path to the CA certificate for client certificate verification. When TLS is enabled, the OCR Service enforces mutual TLS (mTLS) on the main API port. The management port uses server-side TLS only.

## Additional Information

* [Search Service](/ess/services/service-search.md) — Overview and architecture.
* [Ingest Service](/ess/services/service-search/ingest-service.md) — How the Ingest Service uses OCR during indexing.
* [Embedding Service](/ess/services/service-search/embedding-service.md) — Vector embedding generation.
