---
title: "Browserless Alternative for Document Parsing | EnConvert"
description: "EnConvert is a Browserless alternative for document parsing that returns heading-aware JSONL chunks from files and Markdown from URLs behind a single API"
canonical: "https://www.enconvert.com/solutions/browserless-alternative-for-document-parsing-enconvert"
locale: "en"
---

# Best Browserless Alternative for Document Parsing

A developer using Browserless for document parsing is working around the tool: Browserless is a headless browser API for automation, scraping, screenshots, and PDF capture, not a document ingestion API. It can extract page content as rendered HTML or as Markdown through its smart scraper, and it can render a URL to PDF, but it does not parse uploaded files and its output has no heading-aware chunking and no metadata a retrieval layer can filter on. For a developer building a RAG pipeline or an agent that reads documents, the extracted content still needs chunking, metadata, and structure before it can be loaded into a vector store. EnConvert’s Ingest endpoint accepts file uploads and returns JSONL chunks split at heading boundaries with full metadata, and its Perceive endpoint converts any URL to clean Markdown, so document content is ready for retrieval from one API without browser infrastructure.

## Example

```python
import json
import os
import time

import requests

BROWSERLESS_API_TOKEN = os.environ["BROWSERLESS_API_TOKEN"]
ENCONVERT_API_KEY = os.environ["ENCONVERT_API_KEY"]
PAGE_URL = os.environ["PAGE_URL"]  # the web page URL to extract
FILE_PATH = "/path/to/your/report.pdf"  # a local PDF, DOCX, PPTX, XLSX, CSV, or EPUB

# Step 1: Browserless /content returns the rendered HTML of the page
# Raw markup, navigation, and scripts are still in the output.
# Content is not chunked or labelled with sections.
browserless_response = requests.post(
    "https://production-sfo.browserless.io/content",
    params={"token": BROWSERLESS_API_TOKEN},
    json={"url": PAGE_URL},
    timeout=120,
)
browserless_response.raise_for_status()
raw_html = browserless_response.text
# raw_html requires extraction, chunking, and metadata enrichment
# before it can be loaded into a vector store.
print(f"Browserless returned {len(raw_html)} characters of raw HTML")

# Step 2: EnConvert Perceive, same URL, returns clean Markdown
perceive_response = requests.post(
    "https://api.enconvert.com/v2/perceive",
    headers={
        "X-API-Key": ENCONVERT_API_KEY,
        "Content-Type": "application/json",
    },
    json={
        "url": PAGE_URL,
        "outputs": ["markdown"],
        "only_main_content": True,
    },
    timeout=60,
)
perceive_response.raise_for_status()
perceived = perceive_response.json()

# A blocked page, login wall, or HTTP error page comes back as a 200 with no outputs
if perceived.get("error") or "markdown" not in perceived.get("outputs", {}):
    raise RuntimeError(
        f"Perceive failed for {PAGE_URL}: "
        f"{perceived.get('error') or perceived.get('deductions')}"
    )

# The markdown is a signed artifact URL (valid 15 minutes). Plain GET, no API key.
markdown_response = requests.get(perceived["outputs"]["markdown"]["url"], timeout=60)
markdown_response.raise_for_status()
markdown = markdown_response.text
# Clean Markdown with headings, tables, and code blocks preserved and navigation
# chrome stripped. It is one file for the whole page, not yet split into chunks.
print(markdown[:500])

# Step 3: EnConvert Ingest, upload a document, get heading-aware chunks back
# For web pages, POST /v2/ingest with "mode": "urls" returns the same JSONL.
with open(FILE_PATH, "rb") as document:
    ingest_response = requests.post(
        "https://api.enconvert.com/v2/ingest/files",
        headers={"X-API-Key": ENCONVERT_API_KEY},
        files=[("files", (os.path.basename(FILE_PATH), document))],
        timeout=120,
    )
ingest_response.raise_for_status()
job_id = ingest_response.json()["job_id"]

# Step 4: Poll the job until it finishes (queued, processing, then a final status)
while True:
    status_response = requests.get(
        f"https://api.enconvert.com/v2/ingest/{job_id}",
        headers={"X-API-Key": ENCONVERT_API_KEY},
        timeout=60,
    )
    status_response.raise_for_status()
    job = status_response.json()
    if job["status"] in ("completed", "failed", "canceled"):
        break
    time.sleep(5)

if job["status"] != "completed":
    raise RuntimeError(f"Ingest {job['status']}: {job.get('error_message')}")

# Step 5: 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()
chunks = [json.loads(line) for line in jsonl_response.iter_lines() if line]
print(f"{job['total_chunks']} chunks from {FILE_PATH}")

# Each chunk is {"id", "content", "metadata"}. The metadata is what a retrieval
# layer can filter on. headings_path is a list, so join it for display or for a
# vector store that only accepts scalar metadata values.
for chunk in chunks[:3]:
    metadata = chunk["metadata"]
    print(f"{metadata['source_url']} | {metadata['title']}")
    print(
        f"  headings_path: {' > '.join(metadata['headings_path'])} | "
        f"section: {metadata['section']} | "
        f"chunk_index: {metadata['chunk_index']} | "
        f"word_count: {metadata['word_count']}"
    )
    print(f"  {chunk['content'][:200]}")
```

## Output

| Feature | EnConvert | Browserless |
| --- | --- | --- |
| Primary use case | File and URL ingestion into RAG-ready chunks and clean Markdown for AI agents | Headless Chrome API for browser automation, scraping, screenshots, and PDF generation |
| File ingestion (PDF, DOCX, XLSX, CSV) | Ingest: PDF, DOCX, PPTX, XLSX, CSV, Markdown, plain text, TXT, HTML files, and audio transcripts | Not supported; Browserless does not accept file uploads for parsing |
| Web page to clean Markdown | Perceive: any URL to clean Markdown, navigation chrome stripped | Smart scraper returns Markdown or HTML; /content returns rendered HTML |
| RAG-ready chunked output | JSONL chunks per job, split at H1 to H3 heading boundaries with 512 word soft cap | Not documented |
| Heading-aware metadata | headings_path, section, chunk_index, word_count, source_url, and title on every chunk | Not documented |
| JavaScript-rendered pages | Yes, Perceive handles JS-rendered pages without browser setup | Yes, built-in headless Chrome rendering |
| Setup required | API key only | API token for the cloud service, or a Docker image to self-host |
| Pricing model | Monthly ops allowance by tier; 500 ops free with no card required | Usage-based units; free tier then paid plans |

**WHEN TO USE BROWSERLESS AND WHEN TO USE ENCONVERT**

Use Browserless when the task requires full browser automation, such as form interaction, screenshots, or PDF generation from a URL. It is designed for controlling a browser and fits workflows that go beyond extracting document content for retrieval.

Use EnConvert when document content needs to be structured for retrieval. Ingest accepts supported file uploads and returns heading-aware JSONL chunks with section metadata. Perceive converts any URL to clean Markdown without requiring browser infrastructure, and both endpoints use the same flat op counter.

## Get started

[Get free API key — 500 ops per month, no card required.](https://www.enconvert.com/auth?mode=signup)

## Related solutions

- [How to Ingest Markdown Files into LlamaIndex with EnConvert](https://www.enconvert.com/solutions/ingest-markdown-files-into-llamaindex-with-enconvert.md)
- [How to Ingest Web Pages into Vertex AI with EnConvert](https://www.enconvert.com/solutions/ingest-web-pages-into-vertex-ai-with-enconvert.md)
- [How to Ingest Markdown into OpenAI File Search with EnConvert](https://www.enconvert.com/solutions/ingest-markdown-into-openai-file-search-enconvert.md)
- [How to Ingest HTML Files into LlamaIndex with EnConvert](https://www.enconvert.com/solutions/ingest-html-files-into-llamaindex-with-enconvert.md)
- [How to Ingest DOCX into CrewAI with EnConvert](https://www.enconvert.com/solutions/ingest-docx-into-crewai-with-enconvert.md)
- [How to Ingest a Research Paper into LlamaIndex with EnConvert](https://www.enconvert.com/solutions/ingest-a-research-paper-into-llamaindex-enconvert.md)
