Best Unstructured Alternative for PDF Ingestion

EnConvert is an Unstructured alternative for PDF ingestion that returns heading-aware JSONL chunks with section metadata from one upload and no library to run

Get API key

A developer building a RAG pipeline that ingests PDFs needs clean, heading-aware chunks from a simple API call, not a platform with several API surfaces, an open-source runtime to manage, or extraction options to tune. Unstructured is a document parsing platform with an open-source library and a managed API, and its extraction is element-level, returning typed document elements by default, with chunking as a strategy you opt into and configure. For a developer who needs PDF content as heading-aware chunks with metadata intact, that means choosing a chunking strategy, setting size limits, and mapping element metadata to the fields a vector store needs. EnConvert is a REST API that accepts a PDF upload and returns heading-aware JSONL chunks with section metadata, with no library to install and no element types to map, so you get RAG-ready chunks from any PDF in one API call.

Example

python
import os
import json
import time

import requests

# Step 1: The Unstructured Partition Endpoint partitions the PDF and returns typed elements by default
# Chunking is opt-in: add chunking_strategy "by_title" and tune size limits
with open("/path/to/your/report.pdf", "rb") as f:
    partition = requests.post(
        "https://api.unstructuredapp.io/general/v0/general",
        headers={"unstructured-api-key": os.environ["UNSTRUCTURED_API_KEY"]},
        files={"files": ("report.pdf", f, "application/pdf")},
        data={
            "strategy": "hi_res",
            # Add "chunking_strategy": "by_title" to receive chunks instead of elements
            # Add "max_characters" and "overlap" to tune chunk size
        },
        timeout=300,
    )
partition.raise_for_status()
elements = partition.json()

for element in elements:
    print(element["type"], element["text"])

# Output: typed elements such as Title, NarrativeText, Table, ListItem
# Map element types and metadata to your vector store schema before loading

# Step 2: EnConvert uploads the PDF once and queues an ingest job
with open("/path/to/your/report.pdf", "rb") as f:
    upload = requests.post(
        "https://api.enconvert.com/v2/ingest/files",
        headers={"X-API-Key": os.environ["ENCONVERT_API_KEY"]},
        files={"files": ("report.pdf", f, "application/pdf")},
        timeout=120,
    )
upload.raise_for_status()
job_id = upload.json()["job_id"]

# Step 3: Poll until the job completes (queued, processing, then completed)
while True:
    status_response = requests.get(
        f"https://api.enconvert.com/v2/ingest/{job_id}",
        headers={"X-API-Key": os.environ["ENCONVERT_API_KEY"]},
        timeout=30,
    )
    status_response.raise_for_status()
    job = status_response.json()
    if job["status"] == "completed":
        break
    if job["status"] in ("failed", "canceled"):
        raise RuntimeError(job.get("error_message") or f"Ingest job {job['status']}")
    time.sleep(2)

# Step 4: Download the JSONL, one chunk per line
# The output_url is signed (valid 15 minutes). Plain GET, no API key.
jsonl_response = requests.get(job["output_url"], timeout=60)
jsonl_response.raise_for_status()

# Split on "\n" only: str.splitlines() would also split on unicode line separators inside chunk text
chunks = [
    json.loads(line)
    for line in jsonl_response.content.decode("utf-8").split("\n")
    if line.strip()
]
print(f"Loaded {len(chunks)} chunks ready for embedding")

# Each chunk has "content" (the text to embed) plus "metadata" with source_url,
# title, headings_path (a list), section, chunk_index and word_count.
# Embed chunk["content"] and store chunk["metadata"] alongside it. If your vector
# store only accepts scalar metadata, join the list: " > ".join(headings_path)
for chunk in chunks[:3]:
    print(" > ".join(chunk["metadata"]["headings_path"]), "|", chunk["content"][:80])

What you get back

Capability

EnConvert

Unstructured

Output format

JSONL chunks split at H1 to H3 boundaries, 512 word soft cap, tables and code blocks kept whole; each chunk carries headings_path, section, chunk_index, and word_count

Typed document elements as JSON by default; chunked output available through opt-in strategies

RAG-ready chunked output

Confirmed: chunks split at H1 to H3 heading boundaries, 512 word default soft cap, tables and code blocks kept whole even when they exceed the cap; each chunk carries headings_path, section, chunk_index, and word_count

Confirmed: chunking strategies including by_title available; requires opt-in configuration

Heading structure preserved

Confirmed: chunks split at H1 to H3 boundaries, 512 word soft cap, tables and code blocks kept whole; each chunk carries headings_path, section, chunk_index, and word_count

Section boundaries detected via Title elements; no equivalent breadcrumb path documented

Web page ingestion

Perceive: any URL to clean Markdown, including JavaScript-rendered pages; Ingest also accepts URL lists, sitemaps, and website crawls returning the same JSONL chunk schema as file ingestion

Not documented for JavaScript-rendered pages

Document conversion

Convert: 45+ conversion routes across documents, images, and data formats

Not documented as a general-purpose file format conversion endpoint

MCP server

Native, exposes full ingestion toolkit as MCP tools

Available through its SDK’s MCP server

Open-source option

No; managed API only

Confirmed: open-source library alongside the managed API, Apache 2.0 licensed

Free tier

500 ops per month, no card required

Open-source library free to run; managed API trial documented as 1,000 pages per day for 14 days

WHY ENCONVERT FOR PDF INGESTION

Heading-aware JSONL chunks split at document heading boundaries so chunking respects document structure, and each chunk carries a full headings_path breadcrumb so retrieval knows exactly where in the PDF a result came from. You do not write element type mapping, configure a chunking strategy, or transform the output schema before loading into a vector store. When the pipeline grows beyond PDFs, the same API handles DOCX and XLSX with the same output schema, and web pages through Perceive with no integration changes.

Get free API key — 500 ops per month, no card required.