---
seo_title: V1 vs V2: Convert Files or Read the Web | EnConvert
meta_desc: V1 converts files and URLs between formats. V2 reads the live web for agents and RAG pipelines. Which one to call, and what is shipping next.
keywords: v1 vs v2 api, file conversion api vs web scraping api, enconvert api halves, agent ready web data api, url to markdown api, rag ingestion api, which api endpoint to use, one api key two apis
---

# V1 and V2: Convert Files or Read the Web

The EnConvert API has two halves. V1 (`/v1/convert/...`) turns a file or a URL into a format you name; V2 (`/v2/...`) reads a live web page and hands back data an agent can use. One key covers both halves at one base URL, and both bill the same meter.

<div class="alert alert-info">
<strong>Live today:</strong> all of V1, plus all six V2 endpoints. Perceive and Ingest are generally available. Distill, Lookup, Watch and Discover are callable but in private beta, documented under <a href="/docs/coming-soon">Coming Soon</a>, and their shapes can change without notice.
</div>

---

## The decision rule

If you already know the output format you want, that is V1. If you want to know what is on a page, that is V2.

| What you are doing | Half | Start here |
|---|---|---|
| Turn this URL into a PDF | V1 | [url-to-pdf](/docs/endpoints/convert/web-pages/url-to-pdf.md) |
| Turn this DOCX into a PDF | V1 | [Documents](/docs/endpoints/convert/documents.md) |
| Turn this JSON into YAML | V1 | [Data formats](/docs/endpoints/convert/data-formats.md) |
| Turn this HEIC into a WebP | V1 | [Images](/docs/endpoints/convert/images.md) |
| Read this page as Markdown for an LLM | V2 | [Perceive](/docs/endpoints/perceive.md) |
| Get Markdown, a screenshot, links and metadata from one render | V2 | [Perceive](/docs/endpoints/perceive.md) |
| Turn a whole site into RAG chunks | V2 | [Ingest](/docs/endpoints/ingest.md) |

The awkward edge: `url-to-markdown` (V1) and `perceive` (V2) overlap. Use V1 when you want one Markdown file and nothing else. Use V2 when you also want the screenshot, the links, the page metadata, or the option to stream the bytes back inline.

---

## V1: deterministic conversion

You send bytes or a URL, and the endpoint you call *is* the target format. `POST /v1/convert/png-to-webp` returns WebP. Nothing decides anything for you.

There are 49 single-target conversion endpoints across four families, plus two website crawlers that walk a whole site and return a ZIP, so 51 routes in all.

| Family | Endpoints | Input |
|---|---|---|
| [Web pages](/docs/endpoints/convert/web-pages.md) | 5 | A URL (or a list of URLs) in a JSON body |
| [Documents](/docs/endpoints/convert/documents.md) | 13 | A file upload (`multipart/form-data`) |
| [Data formats](/docs/endpoints/convert/data-formats.md) | 11 | A file upload (`multipart/form-data`) |
| [Images](/docs/endpoints/convert/images.md) | 22 | A file upload (`multipart/form-data`) |

Of those, `website-to-pdf` and `website-to-screenshot` are the two crawlers: they discover pages under a domain and always answer [asynchronously](/docs/concepts/sync-and-async.md) with `202` and a ZIP. Every other endpoint converts one input into one output. The full input-to-output map is on [the conversion matrix](/docs/endpoints/convert/matrix.md).

A V1 call looks like this:

```bash
curl -X POST https://api.enconvert.com/v1/convert/url-to-pdf \
  -H "X-API-Key: sk_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/pricing"}'
```

```json
{
    "presigned_url": "https://spaces.example.com/...signed...",
    "object_key": "env/files/{project_id}/url-to-pdf/example_20260404_123456789.pdf",
    "filename": "example_20260404_123456789.pdf",
    "file_size": 123456,
    "conversion_time_seconds": 12.5
}
```

Set `direct_download=true` on a single-URL sync request and the response body is the PDF itself instead of that JSON.

---

## V2: reading the live web

V2 renders a page in real headless Chrome (JavaScript executed, lazy content loaded) and returns what is on it: Markdown, cleaned or raw HTML, a screenshot, a PDF, the link and image inventory, structured page data, or RAG-ready chunks. You do not name an output format so much as name the outputs you want from one render.

Here is the smallest useful call. Send a URL to `/v2/perceive`, get clean Markdown and the page's structured metadata back:

```bash
curl -X POST https://api.enconvert.com/v2/perceive \
  -H "X-API-Key: sk_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/pricing",
    "outputs": ["markdown", "structured"]
  }'
```

The response carries a pre-signed download URL for the Markdown and the structured block inline:

```json
{
    "operation_id": "per_3f9a2c1b8e7d4a6f90b1c2d3e4f5a6b7",
    "status": "completed",
    "url_final": "https://example.com/pricing",
    "render_quality": 0.93,
    "outputs": {
        "markdown": {
            "url": "https://spaces.example.com/...signed...",
            "size_bytes": 8421,
            "expires_in": 900
        }
    },
    "structured": {
        "metadata": {"title": "Pricing"}
    }
}
```

Send JSON, get a result inline or a short-lived signed URL to an artifact. That is the shape of every V2 endpoint.

---

## What is live today

All six V2 endpoints are callable. Two of them are generally available: Perceive and Ingest.

| Endpoint | Status | What it does |
|---|---|---|
| [`POST /v2/perceive`](/docs/endpoints/perceive.md) | Live | Render one URL once and return every output you asked for: Markdown, cleaned or raw HTML, screenshot, PDF, links, images, structured data. |
| [`POST /v2/ingest`](/docs/endpoints/ingest.md) | Live | Crawl a site (or accept uploaded files) and emit one JSONL file of RAG-ready chunks, asynchronously behind a `job_id`. |
| Distill | Private beta | [Reference](/docs/coming-soon/distill.md) |
| Lookup | Private beta | [Reference](/docs/coming-soon/lookup.md) |
| Watch | Private beta | [Reference](/docs/coming-soon/watch.md) |
| Discover | Private beta | [Reference](/docs/coming-soon/discover.md) |

<div class="alert alert-warning">
<strong>The bottom four rows are private beta.</strong> Distill, Lookup, Watch and Discover answer real requests today, but they are not announced, not generally available, and their request and response shapes can change without notice, so keep them out of anything load-bearing. Watch needs a paid plan; the other three run on every plan, Founding included. Details are under <a href="/docs/coming-soon">Coming Soon</a>.
</div>

---

## What the two halves share

V2 is additive. The V1 endpoints are unchanged and unaffected by any of it. There is no migration: you add V2 next to V1 when you need it.

**One key.** A `sk_` private key in the `X-API-Key` header, or a `pk_` public key exchanged for a JWT bearer token, works across V1 and V2 identically. See [authentication](/docs/authentication.md) for the full flow, including domain locking and token refresh.

**One allowlist.** Each API key carries an allowed-endpoints list. A V2 path that is not on the key's list is rejected with `403`, exactly as a V1 path would be.

**One meter.** V1 conversions and V2 operations bill the same monthly ops counter. One op is one unit of work: one conversion, one URL perceived, one page ingested. There are no per-endpoint multipliers, so an expensive render costs the same op as a JSON-to-YAML conversion. Plan allowances are on [rate limits and quotas](/docs/reference/rate-limits.md).

**One delivery mechanism.** File output from either half is uploaded to storage and returned as a pre-signed URL that expires after 15 minutes (`expires_in: 900`). Re-fetch the operation, job, or batch to mint a fresh set; re-signing re-renders nothing and costs no ops. Details on [signed URLs](/docs/concepts/signed-urls.md).

---

## Design choices that hold across V2

Learn these once and they apply across V2.

**One render through a shared browser.** Perceive and Ingest render through the same headless Chrome singleton and the same capture pipeline that backs [the V1 url-to-pdf endpoint](/docs/endpoints/convert/web-pages/url-to-pdf.md). Cookie banners are dismissed, the page is scrolled to trigger lazy content, and images are given time to load.

**SSRF protection on every URL.** Before any fetch or render, each URL is screened for scheme, embedded credentials, blocked hostnames, and the resolved IP. A URL that resolves to a private, loopback, link-local, or cloud-metadata address is rejected with `400`. That applies to seeds and to crawled links alike.

**Render-quality scoring.** Every render carries a `render_quality` score from `0.0` to `1.0`. A low score flags a page that looks blocked by anti-bot protection or hidden behind a login wall, so you can tell a real capture from a challenge page.

**Credentials only where they are safe.** Perceive accepts `auth`, `cookies`, and custom `headers` for pages behind a login. Ingest deliberately does not, because its jobs are durable and resumable and nothing secret should be persisted for a resume. Need credentials for one page in an ingest set? Render it through [perceive](/docs/endpoints/perceive.md) instead.

**Reserved parameters say so.** Where a parameter is accepted by the schema but not yet wired up, V2 tells you rather than silently ignoring it. Perceive's `proxy_url`, `geolocation`, and `action_chain` return `422` today; its `prices`, `contacts`, and `technologies` extract names land in `warnings` and are dropped.

<div class="alert alert-info">
<strong>V2 is in beta.</strong> Pin your integration to the documented field names and status codes, read <code>warnings</code> on every response, and expect response bodies to gain fields before V2 leaves beta. New fields may appear; documented ones will not silently change meaning.
</div>

---

## Where to start

- **Convert a file or a URL:** [the Convert endpoints](/docs/endpoints/convert.md).
- **Read one page:** [the perceive endpoint](/docs/endpoints/perceive.md).
- **Build a RAG corpus from a site:** [the ingest endpoint](/docs/endpoints/ingest.md).
- **Try the private beta endpoints:** [Coming Soon](/docs/coming-soon.md).

If you have not made a first call yet, [the quickstart](/docs/quickstart.md) walks through getting a key and running one request end to end.

---

## Frequently asked questions

### Do I need a separate API key for V2?

No. One key covers both halves. A `sk_` private key in the `X-API-Key` header, or a JWT minted from a `pk_` public key, authenticates V1 and V2 identically, subject to the key's allowed-endpoints list.

### Does V2 replace V1?

No. V2 is additive and V1 is unchanged. If you want a named output format from a file or a URL, V1 is still the right call, and it stays that way.

### How is usage counted across V1 and V2?

Both halves bill one monthly ops counter, and one op is one unit of work: one V1 conversion, one URL perceived, one page ingested. There is no per-endpoint weighting. See [rate limits and quotas](/docs/reference/rate-limits.md).

### Which V2 endpoints can I call today?

All six. Perceive and Ingest are generally available. Distill, Lookup, Watch and Discover are in private beta: callable with your normal key, documented under [Coming Soon](/docs/coming-soon.md), free to change shape without notice, and Watch additionally needs a paid plan.
