---
title: "Create a job (upload or URL)"
method: POST
path: "/api/v2/jobs"
tags: ["v2 Jobs"]
---

# Create a job (upload or URL)

`POST /api/v2/jobs`

## Request

- Base URL: `https://pdf2md.dev`
- URL: `https://pdf2md.dev/api/v2/jobs`
- Auth: HTTP bearer

EP-023-T3 (implemented). In v2.0.0, creating a job requires an API key (scope jobs:create; owner_type=api_key) — there is no web UI for creating jobs, and a session actor gets 403. Idempotency-Key is optional: a repeat with the same key within the account returns the same job (200), without duplicating it.

## Headers

- `Idempotency-Key` string

## Request body

- V2CreateJobFromUrlRequest
  - `url` string, uri, required
  - `file_name` string
  - `options` V2JobOptions: Per-job conversion options (additive, like v1 device_settings). Applied by the v2 create endpoints (EP-026); each unset field falls back to the server default. Unlike v1 (which copies the device's stored /settings at create), v2 API jobs are deviceless, so options are passed per request here.
    - `image_mode` 'embedded' | 'placeholder': Image handling mode in Markdown
    - `ocr_enabled` boolean
    - `engine` 'docling' | 'mineru': Conversion engine (EP-015). Both engine containers are running; at most one is resident (single-resident, EP-016). The old extension does not send this field → the backend uses the default. EP-016: default `mineru` (reliable on scans/Cyrillic); changed via env DEFAULT_ENGINE. `docling` — optional for simple PDFs.
    - `conversion_mode` 'simple' | 'auto' | 'full': Conversion mode / fast-path (EP-014). `simple` — the lightweight docling pipeline, `auto` — autodetect + fallback to full, `full` — the full pipeline. Backward compatibility: when the field is absent the backend uses `full` (behavior identical to before). The new extension's UI defaults to `auto`.
    - `ocr_lang` 'ru' | 'en' | 'ja' | 'es' | 'pt' | 'de' | 'fr': OCR language hint (EP-070, additive). Selects the OCR recognition model for scanned pages / forced OCR in BOTH engines (MinerU maps ja→japan and es/pt/de/fr→latin; docling builds a per-request EasyOCR list <lang>+en). Only affects OCR: PDFs with a digital text layer are extracted, not recognized, and ignore this hint. Absent → the engine's env default (current ru+en behavior). Unknown values are rejected with 400.
  - `external_id` string
  - `tags` object
  - `callback_url` string, uri: v2 §6.8 per-job callback (optional)
  - `callback_secret` string: secret for signing the per-job callback

## Response `200`

Idempotent repeat — existing job (same Idempotency-Key)

- V2Job
  - `job_id` string, uuid, required
  - `status` 'queued' | 'processing' | 'ready' | 'error' | 'canceled' | 'deleted', required: Job status
  - `file_name` string, required
  - `created_at` string, date-time, required
  - `status_since` string, date-time
  - `pages` integer, nullable
  - `truncated` boolean
  - `output_size` integer, nullable
  - `download_url` string, uri, nullable: populated for ready
  - `tier` 'anonymous' | 'free' | 'lifetime' | 'builder' | 'pro' | 'business' | 'enterprise', required: EP-069: `lifetime` is the one-time unlock, ranked BETWEEN free and builder — it raises the interactive limits and carries 600 pages/month, but grants no API keys, hosted MCP, webhooks or batch, so a subscription is genuinely more and its holder may still buy one.
  - `slot_usage` object
    - `used` integer
    - `limit` integer
  - `external_id` string, nullable
  - `tags` object, nullable

## Other responses

- `201`: Created
- `400`: Invalid data (not a PDF, no url/file, broken link)
- `401`: Missing/invalid API key or session
- `403`: API key required / no jobs:create scope
- `409`: slots_full — all slots are in use (clears as jobs finish) | quota_exceeded — the monthly page allowance is spent (clears on the 1st). EP-070-T7: the quota body also carries `used`, `limit` and `reset_at` (RFC3339, start of the next UTC month) so a client can say which limit and when, in its own language. 409 rather than 429 on purpose: 429 renders as "retry in a moment", which is wrong for something that returns next month.
- `413`: File exceeds the tier limit (max_file_size)
- `429`: Rate limit exceeded

---

[API](https://skmtc.dev/pdf2md/apis/pdf-to-markdown-api-facade.md) · [All operations](https://skmtc.dev/pdf2md/apis/pdf-to-markdown-api-facade/llms.txt) · [OpenAPI document](https://skmtc.dev/pdf2md/apis/pdf-to-markdown-api-facade/revisions/4636782fb8b6?raw)
