> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cognee.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Loaders

> Learn how Cognee handles different file formats.

Loaders are responsible for reading files from your disk or cloud storage and converting them into plain text that Cognee can process. When you run `remember()`, Cognee automatically selects the most appropriate loader for each file based on its extension and content type.

## Loader Selection

Cognee uses a priority system to decide which loader to use. It tries to match a loader in the following order:

1. **CodeLoader**: For source-code files (`.py`, `.ts`, `.go`, `.rs`, etc.). It sits ahead of `TextLoader` because code content-sniffs as plain text, so `TextLoader` would otherwise claim it first.
2. **TextLoader**: For plain text files (`.txt`, `.md`, `.json`, `.xml`, etc.).
3. **PyPdfLoader**: For PDF files (requires `pypdf`).
4. **ImageLoader**: For images (uses vision models to transcribe content, with an optional local OCR pass).
5. **AudioLoader**: For audio files (uses transcription models).
6. **VideoLoader**: For video files (transcribes the audio track with inline `[HH:MM:SS]` timestamps).
7. **DltCsvLoader**: For CSV files, when `cognee[dlt]` is installed (routes rows through dlt structured ingestion instead of text).
8. **CsvLoader**: For CSV files (converts rows to text).
9. **UnstructuredLoader**: For complex formats like `.docx`, `.pptx`, `.epub` (requires `unstructured`).
10. **AdvancedPdfLoader**: For layout-aware PDF extraction (requires `unstructured`).
11. **DoclingLoader**: If no other loader can ingest the file type, [Docling](https://github.com/docling-project/docling) is used for conversion (if the type is supported).

If you want to force a specific loader or provide custom configuration, you can use the `preferred_loaders` parameter in `remember()`.

## Available Loaders

### Core Loaders

These are available by default in every Cognee installation:

* **CodeLoader**: Claims source-code files by file-name extension and stores them verbatim under their original extension. Files it claims take the deterministic code graph pipeline during `cognify()` instead of LLM-based extraction. See the "Code Loader" accordion below.
* **TextLoader**: Reads text files with UTF-8 encoding.
* **CsvLoader**: Reads CSV files and converts each row into a structured text format (`Row N: key: value`).
* **ImageLoader**: Uses an LLM vision model to transcribe the image content. By default the transcription uses an extraction-oriented prompt that asks for entities, relationships, verbatim text, and structured content (tables, charts, diagrams) rather than a short caption. An optional local OCR pass can be enabled to append recognized text to the transcription; it requires `pip install cognee[rapidocr]`. See the "Image transcription and OCR" accordion below.
* **AudioLoader**: Uses an audio transcription API to transcribe audio files. This depends on your configured LLM provider supporting transcription endpoints; see [LLM Providers](/setup-configuration/llm-providers) for provider-specific caveats.
* **VideoLoader**: Transcribes a video's audio track into text with inline `[HH:MM:SS]` segment timestamps, then feeds it through the normal text pipeline. `.mp4` and `.webm` do not require `ffmpeg`; other containers do (see the VideoLoader usage accordion below).

### External Loaders

These require additional dependencies to be installed:

* **PyPdfLoader**: Extracts text from PDFs page by page using the `pypdf` library, preserving page boundaries with `Page N:` markers in the extracted text. Requires `pip install cognee[docs]` (or `pip install pypdf`).
* **AdvancedPdfLoader**: Layout-aware PDF extraction using the `unstructured` library. Extracts text, tables (as HTML), and image placeholders per page. Falls back to `PyPdfLoader` automatically if extraction fails. Requires `pip install cognee[docs]`, plus `poppler` and `tesseract` installed on the system.
* **UnstructuredLoader**: Handles many office and document formats (`.docx`, `.xlsx`, `.pptx`, `.odt`, `.rtf`, `.eml`, `.epub`, `.html`, and more) via `unstructured`'s auto-partition. Requires `pip install cognee[docs]`.
* **BeautifulSoupLoader**: Extracts text from HTML files using `BeautifulSoup`. Applies CSS selector rules to pull structured content from specific tags. Requires `pip install cognee[scraping]`.
* **DoclingLoader**: Catch-all fallback that converts a wide range of document formats (PDF, DOCX, XLSX, PPTX, HTML, Markdown, and more) to plain text via [Docling](https://github.com/docling-project/docling). Supported extensions are discovered dynamically from Docling's `FormatToExtensions` map. Requires `pip install cognee[docling]`.
* **DltCsvLoader**: Ingests `.csv` files through the [dlt structured path](/integrations/dlt-integration) instead of flattening them to text — the rows are loaded into a dlt staging database and the loader emits one manifest per CSV file, which cognify turns into row nodes deterministically from the schema, with no LLM extraction. It registers **above** `CsvLoader` in the priority order, so once the extra is installed every CSV takes this route by default. Requires `pip install 'cognee[dlt]'`.

## Supported File Extensions

Cognee selects loaders based on file type. The table below shows the supported extensions and their default loaders.

Extensions are matched case-insensitively for the text-only formats that carry no content signature — `.txt`, `.csv`, `.md`, `.json`, `.xml`, `.yaml`, and `.yml` — so `REPORT.CSV` and `report.csv` are detected as the same type and get the same loader. `.log` files are case-insensitive too: an unrecognized extension like `.LOG` falls back to plain text, which lands on the same TextLoader. Formats identified from their content, such as PDFs, images, audio, and video, were never case-sensitive.

<Accordion title="Supported extensions reference">
  | Extension(s)                                                                                                                                                                                                                                      | Default loader                                         | Notes                                                                                                                                                                                                                                                       |
  | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | `.txt`, `.md`, `.json`, `.xml`, `.yaml`, `.yml`, `.log`                                                                                                                                                                                           | TextLoader                                             | Plain text and structured text-like files                                                                                                                                                                                                                   |
  | `.c`, `.cc`, `.cpp`, `.cs`, `.cxx`, `.dart`, `.fs`, `.go`, `.h`, `.hcl`, `.hh`, `.hpp`, `.java`, `.js`, `.jsx`, `.kt`, `.kts`, `.php`, `.proto`, `.py`, `.rake`, `.rb`, `.rs`, `.scala`, `.svelte`, `.swift`, `.tf`, `.ts`, `.tsx`, `.vb`, `.vue` | CodeLoader                                             | Source-code files; matched by extension only and processed by the code graph pipeline rather than LLM extraction                                                                                                                                            |
  | `.csv`                                                                                                                                                                                                                                            | DltCsvLoader (with `cognee[dlt]`), otherwise CsvLoader | Tabular data. With the `dlt` extra installed, `DltCsvLoader` takes precedence and routes rows through dlt structured ingestion; without it, `CsvLoader` converts rows to text. Force text flattening per call with `preferred_loaders=[{"csv_loader": {}}]` |
  | `.pdf`                                                                                                                                                                                                                                            | PyPdfLoader                                            | Default PDF extraction; `AdvancedPdfLoader` is optional for layout-aware parsing                                                                                                                                                                            |
  | `.docx`, `.doc`, `.odt`                                                                                                                                                                                                                           | UnstructuredLoader                                     | Word-processor formats; requires `unstructured`                                                                                                                                                                                                             |
  | `.xlsx`, `.xls`, `.ods`                                                                                                                                                                                                                           | UnstructuredLoader                                     | Spreadsheet formats; requires `unstructured`                                                                                                                                                                                                                |
  | `.pptx`, `.ppt`, `.odp`                                                                                                                                                                                                                           | UnstructuredLoader                                     | Presentation formats; requires `unstructured`                                                                                                                                                                                                               |
  | `.rtf`, `.html`, `.htm`, `.eml`, `.msg`, `.epub`                                                                                                                                                                                                  | UnstructuredLoader                                     | Additional document and markup formats; requires `unstructured`                                                                                                                                                                                             |
  | `.png`, `.jpg`, `.jpe`, `.jpeg`, `.gif`, `.webp`, `.bmp`, `.tif`, `.tiff`, `.heic`, `.avif`, `.ico`, `.psd`, `.apng`, `.cr2`, `.dwg`, `.xcf`, `.jxr`, `.jpx`                                                                                      | ImageLoader                                            | Raster, design, raw, and CAD-adjacent image formats transcribed by a vision LLM; optional local OCR text is appended when `IMAGE_OCR_ENABLED="true"`                                                                                                        |
  | `.mp3`, `.wav`, `.aac`, `.flac`, `.ogg`, `.m4a`, `.mid`, `.amr`, `.aiff`                                                                                                                                                                          | AudioLoader                                            | Audio transcription via a Whisper-compatible model                                                                                                                                                                                                          |
  | `.mp4`, `.webm`                                                                                                                                                                                                                                   | VideoLoader                                            | Audio track transcribed with inline `[HH:MM:SS]` timestamps; no `ffmpeg` required (`ffmpeg` is used automatically when available)                                                                                                                           |
  | `.mov`, `.mkv`, `.avi`, `.m4v`                                                                                                                                                                                                                    | VideoLoader                                            | Audio track transcribed with inline `[HH:MM:SS]` timestamps; requires system `ffmpeg` on `PATH` to extract audio                                                                                                                                            |

  > **Note:** Files with extensions not in this table cannot be remembered by default. Use a [custom loader](#registering-custom-loaders) to handle additional formats.
  >
  > When no registered loader can handle a file, `remember()` raises a `ValueError` that names the file's extension and lists the currently supported extensions. For office and document formats that ship only via an optional loader (for example `.pptx`, `.docx`, `.xlsx`, `.html`, `.epub`), install `cognee[docling]` for Docling or `cognee[docs]` for Unstructured-backed loaders, then retry.
</Accordion>

## Usage

<AccordionGroup>
  <Accordion title="Using Preferred Loaders">
    You can override the default loader selection by specifying `preferred_loaders`. This is useful when you want to pass specific configuration options to a loader.

    ```python theme={null}
    import cognee

    await cognee.remember(
        data=["example_website.html"],
        preferred_loaders=[
            {
                "beautiful_soup_loader": {
                    "extraction_rules": {
                        "title": "h1",
                        "body": "article.main-content"
                    }
                }
            }
        ]
    )
    ```

    **Opting out of the dlt CSV route.** When `cognee[dlt]` is installed, `DltCsvLoader` outranks `CsvLoader` for every `.csv` file. Name `csv_loader` explicitly to restore text flattening for a single call:

    ```python theme={null}
    await cognee.remember(
        data=["employees.csv"],
        preferred_loaders=[{"csv_loader": {}}]
    )
    ```

    The same channel carries per-call dlt options — `primary_key`, `write_disposition`, `max_rows_per_table`, and `column_value_columns` — to `DltCsvLoader`:

    ```python theme={null}
    await cognee.remember(
        data=["employees.csv"],
        preferred_loaders=[
            {"dlt_csv_loader": {"primary_key": "id", "write_disposition": "merge"}}
        ]
    )
    ```
  </Accordion>

  <Accordion title="Code Loader">
    `CodeLoader` is a core loader that claims source-code files so they take Cognee's deterministic code graph pipeline instead of the LLM extraction path.

    ```python theme={null}
    import cognee

    await cognee.remember(data=["src/service.py"])
    ```

    **Matched by extension only.** Content sniffing cannot identify a programming language — it reports code as plain text — so `CodeLoader` looks at the file-name extension and ignores the detected MIME type entirely. It claims these 31 extensions:

    `.c`, `.cc`, `.cpp`, `.cs`, `.cxx`, `.dart`, `.fs`, `.go`, `.h`, `.hcl`, `.hh`, `.hpp`, `.java`, `.js`, `.jsx`, `.kt`, `.kts`, `.php`, `.proto`, `.py`, `.rake`, `.rb`, `.rs`, `.scala`, `.svelte`, `.swift`, `.tf`, `.ts`, `.tsx`, `.vb`, `.vue`

    Extensions that double as generic config or markup are deliberately **not** claimed — `.yaml` and `.yml` (Ansible), `.json` (OpenAPI), and template formats such as `.xaml`, `.razor`, `.cshtml`, and `.hbs`. Claiming those would hijack ordinary documents, so files with those extensions keep going to `TextLoader` as before. `.graphql` is also left out, for a different reason: the extractor only reads GraphQL schemas inside a larger project, not as lone files.

    **Stored under the real extension.** Like `TextLoader`, `CodeLoader` stores content verbatim under a content-hash file name, but it keeps the original suffix — `code_<content_hash>.<ext>` (for example `code_a1b2c3....py`). The storage file name is the only place the extension survives, and language detection downstream depends on it.

    **No LLM or embedding calls.** Claimed files are classified as code files and routed by `cognify()` down a dedicated code route whose task list runs the code graph extraction directly. Chunking, entity extraction with an LLM, and summarization do not run for them, so they also cost nothing in a `cognify(dry_run=True)` estimate — see [Dry-run cost estimation](/python-api/cognify#dry-run-cost-estimation).

    **Requires the enola binary.** The code graph extraction is performed by [enola](https://github.com/enola-labs/enola), an external Go CLI, not by Python code inside Cognee. The first `cognify()` run over a code file downloads and installs a pinned enola release automatically; set `ENOLA_PATH` to use a binary you installed yourself, or `ENOLA_AUTO_INSTALL=false` to disable the auto-install, in which case a missing binary raises `EnolaNotInstalledError`.

    **Opting out.** `preferred_loaders` is tried before the default priority order, so you can send a code file back down the normal text and LLM path:

    ```python theme={null}
    import cognee

    await cognee.remember(
        data=["src/service.py"],
        preferred_loaders=[{"text_loader": {}}]
    )
    ```
  </Accordion>

  <Accordion title="Advanced PDF Loader">
    `AdvancedPdfLoader` uses the `unstructured` library to perform layout-aware extraction, preserving page structure, tables, image metadata, and page numbers. It groups content by page and prepends `Page N:` markers to each page's extracted text, making source pages traceable in downstream chunks. Because `PyPdfLoader` has higher default priority, you need to request it explicitly with `preferred_loaders`. It accepts a `strategy` parameter that controls the trade-off between speed and accuracy:

    If you want to inspect those page markers after ingestion, retrieve raw chunks with `SearchType.CHUNKS`. The page number is kept in the chunk `text`, not in a separate metadata field.

    <Note>
      Make sure `poppler` and `tesseract` are installed on your system before using `AdvancedPdfLoader`, in addition to installing the Python dependencies with `pip install cognee[docs]`.
    </Note>

    | Strategy           | Description                                                   |
    | ------------------ | ------------------------------------------------------------- |
    | `"auto"` (default) | Automatically selects the best strategy based on the document |
    | `"fast"`           | Fast text extraction without layout analysis                  |
    | `"hi_res"`         | High-resolution extraction with full layout analysis (slower) |
    | `"ocr_only"`       | Uses OCR for text extraction, useful for scanned PDFs         |

    <AccordionGroup>
      <Accordion title="Scanned vs. text PDFs (enabling OCR)">
        Cognee does **not** automatically distinguish scanned (image-only) PDFs from text PDFs. The default `PyPdfLoader` reads the embedded text layer page by page and skips pages with no extractable text. For a scanned PDF — where each page is an image with no text layer — this silently produces empty or near-empty output, and no OCR is performed.

        A text layer is the selectable, machine-readable text embedded in the PDF. If a page has no text layer, `PyPdfLoader` omits that page and no `Page N:` marker is added for it. If `pypdf` errors on a malformed page, Cognee logs a warning, skips that page, and continues loading the rest of the document.

        To process scanned PDFs, explicitly select `AdvancedPdfLoader` with an OCR strategy. Because `PyPdfLoader` has higher default priority, you must request the OCR-capable loader through `preferred_loaders`:

        ```python theme={null}
        import cognee

        await cognee.remember(
            data=["scanned_document.pdf"],
            preferred_loaders=[
                {"advanced_pdf_loader": {"strategy": "ocr_only"}}
            ]
        )
        ```

        Use `strategy="ocr_only"` for fully image-based or scanned PDFs, and `strategy="hi_res"` for documents that mix a text layer with scanned images. OCR requires `pip install cognee[docs]` plus `poppler` and `tesseract` installed on the system. For non-English or CJK scanned documents, also install the matching Tesseract language packs (see the next accordion).
      </Accordion>

      <Accordion title="OCR for non-English and CJK PDFs">
        Standard PDF text extraction via `PyPdfLoader` or `AdvancedPdfLoader` with `strategy="fast"` often fails silently on CJK and other non-Latin documents. These PDFs commonly embed glyphs as images or use non-standard font encodings, which can lead to empty or garbled output.

        Use `AdvancedPdfLoader` with an OCR-based strategy and install Tesseract language packs for your target language.

        **Step 1 — install Tesseract language packs** (Ubuntu/Debian):

        ```bash theme={null}
        # Japanese
        sudo apt-get install tesseract-ocr-jpn tesseract-ocr-jpn-vert

        # Chinese (Simplified / Traditional)
        sudo apt-get install tesseract-ocr-chi-sim tesseract-ocr-chi-tra

        # Korean
        sudo apt-get install tesseract-ocr-kor
        ```

        **Step 2 — use `ocr_only` strategy with the `languages` parameter**:

        ```python theme={null}
        import cognee

        # Japanese PDF
        await cognee.remember(
            data=["japanese_document.pdf"],
            preferred_loaders=[
                {
                    "advanced_pdf_loader": {
                        "strategy": "ocr_only",
                        "languages": ["jpn"]
                    }
                }
            ]
        )
        ```

        The `languages` list accepts [ISO 639-2 Tesseract language codes](https://tesseract-ocr.github.io/tessdoc/Data-Files-in-different-versions.html). Common values:

        | Language            | Code        |
        | ------------------- | ----------- |
        | Japanese            | `"jpn"`     |
        | Chinese Simplified  | `"chi_sim"` |
        | Chinese Traditional | `"chi_tra"` |
        | Korean              | `"kor"`     |

        Use `strategy="hi_res"` for better layout accuracy when the document has mixed text and images, and `strategy="ocr_only"` for fully image-based or scanned PDFs.

        <Note>
          If you also need translation after OCR extraction, use the [Multilingual Ingestion](/guides/multilingual-ingestion) pipeline before building the knowledge graph.
        </Note>
      </Accordion>
    </AccordionGroup>
  </Accordion>

  <Accordion title="Unstructured Loader">
    `UnstructuredLoader` handles a wide range of office and document formats using `unstructured`'s auto-partition feature. It supports the same `strategy` options as `AdvancedPdfLoader`.

    Supported file types:

    | Category       | Extensions                                       |
    | -------------- | ------------------------------------------------ |
    | Word documents | `.docx`, `.doc`, `.odt`                          |
    | Spreadsheets   | `.xlsx`, `.xls`, `.ods`                          |
    | Presentations  | `.pptx`, `.ppt`, `.odp`                          |
    | Other          | `.rtf`, `.html`, `.htm`, `.eml`, `.msg`, `.epub` |

    ```python theme={null}
    import cognee

    await cognee.remember(
        data=["presentation.pptx"],
        preferred_loaders=[
            {
                "unstructured_loader": {
                    "strategy": "fast"
                }
            }
        ]
    )
    ```
  </Accordion>

  <Accordion title="Docling Loader">
    `DoclingLoader` is the lowest-priority loader and acts as a catch-all fallback for any file type that no other loader can ingest. It converts the document with [Docling](https://github.com/docling-project/docling) and exports plain text via Docling's `export_to_text()`. Supported extensions are pulled at runtime from Docling's `FormatToExtensions` (PDF, DOCX, XLSX, PPTX, HTML, Markdown, and more), so the available formats track your installed Docling version.

    Install with `pip install 'cognee[docling]'`. Because it sits last in the loader priority, Cognee only reaches for it when no higher-priority loader matches — to force it on a file another loader would normally handle (for example, to use Docling's layout-aware parsing on a PDF instead of `PyPdfLoader`), pass it through `preferred_loaders`:

    ```python theme={null}
    import cognee

    await cognee.remember(
        data=["report.pdf"],
        preferred_loaders=[{"docling_loader": {}}]
    )
    ```

    **When to use Docling vs. `AdvancedPdfLoader`**: prefer `AdvancedPdfLoader` for PDFs when you need per-page markers, HTML-formatted tables, or OCR strategy control (see the Advanced PDF Loader accordion above). Reach for `DoclingLoader` when you want a single unified converter across many formats, or for non-PDF files Cognee does not otherwise handle.
  </Accordion>

  <Accordion title="BeautifulSoup Loader">
    `BeautifulSoupLoader` parses HTML files using CSS selectors. By default it applies a comprehensive set of extraction rules covering common HTML content areas (headings, paragraphs, articles, tables, code blocks, etc.). You can pass your own `extraction_rules` dict to target specific elements.

    Each rule is a dict with the following optional keys:

    | Key         | Type   | Description                                                    |
    | ----------- | ------ | -------------------------------------------------------------- |
    | `selector`  | `str`  | CSS selector to match elements                                 |
    | `xpath`     | `str`  | XPath expression (requires `lxml`)                             |
    | `attr`      | `str`  | HTML attribute to extract instead of text content              |
    | `all`       | `bool` | If `True`, extract all matches; otherwise only the first       |
    | `join_with` | `str`  | String used to join multiple extracted values (default: `" "`) |

    ```python theme={null}
    import cognee

    await cognee.remember(
        data=["page.html"],
        preferred_loaders=[
            {
                "beautiful_soup_loader": {
                    "extraction_rules": {
                        "title": {"selector": "h1", "all": False},
                        "body": {"selector": "article.main-content", "all": True, "join_with": "\n\n"},
                        "og_image": {"selector": "meta[property='og:image']", "attr": "content"}
                    }
                }
            }
        ]
    )
    ```

    **Overlapping CSS rules are deduplicated.** Rules are applied in the order they appear in `extraction_rules`, against a single parsed document. Once a rule extracts an element, elements nested inside it are skipped by later rules, so content covered by a broad selector is not emitted a second time by a narrower one. With the default rules, for example, `article` runs before `paragraphs`, so a `<p>` inside an `<article>` is extracted once as part of the article rather than repeated. Deduplication works in one direction only: a broad rule listed *after* a narrower one is not suppressed by it and re-emits the shared content anyway, so list broader selectors before narrower ones — as the default rules do — to avoid duplicates.

    Deduplication only skips *descendants* of already-extracted elements. Two rules whose selectors match the exact same element still extract that element twice. Matches within a single rule are likewise not deduplicated against each other, so one selector matching both an element and its descendant emits the shared text twice.

    For XPath-based extraction (requires `pip install lxml`):

    ```python theme={null}
    await cognee.remember(
        data=["page.html"],
        preferred_loaders=[
            {
                "beautiful_soup_loader": {
                    "extraction_rules": {
                        "content": {"xpath": "//div[@class='content']//p"}
                    }
                }
            }
        ]
    )
    ```

    <Note>
      XPath rules are evaluated against a separate `lxml` tree and do not take part in the deduplication described above. An XPath rule never suppresses a later CSS rule, and it is never suppressed by an earlier one — so mixing XPath and CSS rules that cover the same elements can still repeat content.
    </Note>
  </Accordion>

  <Accordion title="Configuring Vision Models for ImageLoader">
    `ImageLoader` uses your configured LLM to describe image content — there is no separate VLM configuration. To process images, set `LLM_MODEL` to a vision-capable model.

    **Vision-capable models by provider:**

    | Provider       | Example model                |
    | -------------- | ---------------------------- |
    | OpenAI         | `gpt-4o`, `gpt-4o-mini`      |
    | Google Gemini  | `gemini/gemini-2.0-flash`    |
    | Anthropic      | `claude-3-5-sonnet-20241022` |
    | Azure OpenAI   | `azure/gpt-4o`               |
    | Ollama (local) | `llava`, `llava-llama3`      |

    ```dotenv theme={null}
    # .env — enable vision by choosing a vision-capable model
    LLM_PROVIDER="openai"
    LLM_MODEL="gpt-4o-mini"
    LLM_API_KEY="sk-..."
    ```

    If your `LLM_MODEL` does not support vision, remembering an image file will fail at the description step. Switch to a vision-capable model and retry.

    <Info>
      **Ollama users**: Pull a vision-capable model (e.g. `ollama pull llava`) and set `LLM_MODEL="llava"`. Text-only models such as `llama3.1` cannot process images. See [LLM Providers](/setup-configuration/llm-providers) for full Ollama setup.
    </Info>

    For the prompt, token cap, and optional OCR pass used during transcription, see the "Image transcription and OCR" accordion below.
  </Accordion>

  <Accordion title="Image transcription and OCR">
    `ImageLoader` turns an image into text in two steps: a vision-LLM transcription, and — when enabled — a local OCR pass whose recognized text is appended to that transcription. Both are controlled by environment variables.

    | Variable                                    | Default                         | Description                                                                                                                                                                                                                           |
    | ------------------------------------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `IMAGE_EXTRACTION_ENABLED`                  | `"true"`                        | Transcribe with an extraction-oriented prompt instead of a short caption. Set to `"false"` to restore the legacy `"What's in this image?"` prompt and its 300-token cap; the three `IMAGE_TRANSCRIPTION_*` settings are then ignored. |
    | `IMAGE_TRANSCRIPTION_PROMPT_PATH`           | `"transcribe_image_prompt.txt"` | Prompt template used for the transcription.                                                                                                                                                                                           |
    | `IMAGE_TRANSCRIPTION_MAX_COMPLETION_TOKENS` | `1024`                          | Completion-token cap for the transcription request.                                                                                                                                                                                   |
    | `IMAGE_TRANSCRIPTION_REASONING_EFFORT`      | `"low"`                         | Reasoning effort hint: `minimal`, `low`, `medium`, or `high`. Ignored by models without reasoning support.                                                                                                                            |
    | `IMAGE_OCR_ENABLED`                         | `"false"`                       | Run a local OCR pass and append its text to the transcription. Requires `pip install cognee[rapidocr]`.                                                                                                                               |

    **Extraction-oriented transcription (default).** The default prompt asks the vision model for concise, factual text aimed at graph extraction: the entities shown and their attributes, the relationships between them, all visible text, numbers, dates, and labels transcribed verbatim, and structured content (tables as rows, charts as series and data points, diagrams as how the elements connect). This produces longer text than a caption, which is why the token cap defaults to `1024`.

    ```dotenv theme={null}
    # .env — restore the previous short-caption behavior
    IMAGE_EXTRACTION_ENABLED="false"
    ```

    **Optional local OCR.** OCR is off by default. When switched on, Cognee runs [RapidOCR](https://github.com/RapidAI/RapidOCR) locally — a pip-only dependency, no system binary — and appends the recognized text to the vision transcription under an `[OCR extracted text]` heading:

    ```
    A bar chart titled "Quarterly revenue" with four bars...

    [OCR extracted text]
    Quarterly revenue
    Q1 1.2M
    Q2 1.8M
    ```

    Because the OCR text is part of the loaded text, it flows through chunking, extraction, and storage like any other content. This is most useful for screenshots, scanned pages, and dense charts where the vision model paraphrases labels instead of reproducing them.

    ```bash theme={null}
    pip install cognee[rapidocr]
    ```

    ```dotenv theme={null}
    # .env
    IMAGE_OCR_ENABLED="true"
    ```

    <Note>
      OCR text is truncated at 8000 characters (the tail is replaced with `...`). If the OCR pass itself fails, Cognee logs an error and continues with the vision transcription alone rather than failing the ingestion.
    </Note>

    **Custom transcription prompt.** `IMAGE_TRANSCRIPTION_PROMPT_PATH` accepts either a file name inside Cognee's built-in prompt directory (`cognee/infrastructure/llm/prompts`) or an absolute path, so you can keep your own prompt file anywhere on disk:

    ```dotenv theme={null}
    # .env
    IMAGE_TRANSCRIPTION_PROMPT_PATH="/etc/cognee/prompts/my_image_prompt.txt"
    ```

    <Warning>
      **Empty transcriptions on reasoning models.** `IMAGE_TRANSCRIPTION_MAX_COMPLETION_TOKENS` caps reasoning tokens as well as output tokens, so on a reasoning model (including the default `openai/gpt-5-mini`) a small cap can consume the whole budget on reasoning and return empty content. Cognee logs a warning naming the file and suggesting a higher `IMAGE_TRANSCRIPTION_MAX_COMPLETION_TOKENS`, then continues with empty text for that image. If images ingest but contribute nothing to the graph, raise the cap.
    </Warning>
  </Accordion>

  <Accordion title="Video Loader">
    `VideoLoader` ingests a video by transcribing its **audio track** and inlining per-segment `[HH:MM:SS]` timestamps into the resulting text, for example:

    ```
    [00:00:00] Welcome to the walkthrough.
    [00:00:12] First we configure the environment.
    ```

    Because the timestamps are part of the text, they survive chunking and stay searchable. From there the transcript flows through the normal `TextDocument` pipeline (chunking, entity/relationship extraction, graph + vector storage) — there is no separate video document type, so a video becomes queryable memory with no special handling downstream.

    `VideoLoader` is a core loader, available in every installation. Supported extensions: `.mp4`, `.m4v`, `.mov`, `.webm`, `.mkv`, `.avi`.

    ```python theme={null}
    import cognee

    await cognee.remember(data=["walkthrough.mp4"])
    ```

    <Note>
      **`ffmpeg` requirement.** `.mp4` and `.webm` do not require any extra tooling: if `ffmpeg` is unavailable, Cognee can send those containers straight to the transcription endpoint. Other containers (`.mov`, `.mkv`, `.avi`, `.m4v`) require system `ffmpeg` on your `PATH` to extract the audio track first. When `ffmpeg` is present it is also used for `.mp4`/`.webm` to keep the upload small.

      If a container needs `ffmpeg` and none is found, `remember()` raises a `RuntimeError` explaining that `ffmpeg` must be installed and on your `PATH`, or that you can supply the video as `.mp4` or `.webm` instead. There is no `cognee[video]` extra — install `ffmpeg` through your system package manager.
    </Note>

    <Info>
      Inline `[HH:MM:SS]` markers require a transcription model that supports segmented (`verbose_json`) output, such as `whisper-1`. Providers or models without segmented output fall back to a plain transcript with no timestamp markers; the transcript is still ingested normally.
    </Info>
  </Accordion>

  <Accordion title="Registering Custom Loaders">
    If you need to handle a custom file format, you can create your own loader class and register it with Cognee.

    <Note>
      `supported_extensions` values must **not** include a leading dot — use bare extensions like `"custom"` and `"jpeg"`, not `".custom"` or `".jpeg"`. Loader matching compares against the file's dot-free extension, so a dotted value would never match.
    </Note>

    ```python theme={null}
    from cognee.infrastructure.loaders import use_loader
    from cognee.infrastructure.loaders.LoaderInterface import LoaderInterface

    class MyCustomLoader(LoaderInterface):
        loader_name = "my_custom_loader"
        supported_extensions = ["custom"]
        supported_mime_types = ["application/x-custom"]

        async def load(self, file_path, **kwargs):
            # Your custom logic to read the file and return text
            return "Extracted text content"

    # Register the loader so Cognee can use it
    use_loader("my_custom_loader", MyCustomLoader)

    await cognee.remember("data/file.custom")
    ```

    **`load()` receives ingestion context.** Beyond the loader-specific options you pass via `preferred_loaders`, Cognee forwards the current ingestion context to `load()` as keyword arguments: `dataset_name`, `dataset_id`, `user`, and `original_file_name` (the user's real file name, which can differ from `file_path` when the bytes are a localized copy of an `s3://` source or an upload). Accept `**kwargs` — as the example above does — and ignore what you don't need.

    **Returning a `LoaderResult`.** `load()` normally returns the stored derived-text path as a plain `str`. A loader that also owns the record's identity and routing can instead return a `LoaderResult`:

    ```python theme={null}
    from cognee.infrastructure.loaders.LoaderInterface import LoaderInterface, LoaderResult

    class MyCustomLoader(LoaderInterface):
        ...
        async def load(self, file_path, **kwargs):
            return LoaderResult(
                file_path=stored_text_path,   # required: the derived-text path
                data_id=my_stable_uuid,       # optional: pins the record's id
                system_metadata={...},        # optional: routing stamp for cognify
            )
    ```

    | Field             | Type                     | Description                                                                                                                                 |
    | ----------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
    | `file_path`       | `str`                    | Path to the stored derived text — the same value a plain-`str` return would provide                                                         |
    | `data_id`         | `UUID` or `None`         | Pins the ingested record to this id instead of the one ingestion mints, so repeated runs update the same record                             |
    | `system_metadata` | `dict` or `None`         | Stamped onto the record and used by cognify's per-item routing                                                                              |
    | `file_metadata`   | `FileMetadata` or `None` | Describes the derived text the loader just wrote — content hash, size, mime type, and name — computed while the content was still in memory |

    Omitting `file_metadata` (or returning a plain `str`) makes ingestion re-open the stored file to derive the hash, size, and mime type it needs for the `Data` row; over the S3 backend that read-back costs an extra HEAD plus a full GET of content the loader already had. Loaders that write their text through the `store_derived_text` helper get the field filled in for free — it stores and describes in one step.

    Returning a plain string remains fully supported; only loaders that need stable identity or custom routing need `LoaderResult`. The built-in `DltCsvLoader` uses it to carry a CSV manifest's stable `data_id` and its no-LLM cognify route stamp.
  </Accordion>
</AccordionGroup>
