---
seo_title: Datei-Upload-API: Multipart, URLs & Größenlimits | EnConvert
meta_desc: Jeder Weg, Bytes an die EnConvert-API zu übergeben: Multipart-Upload oder eine URL, die die API abruft, dazu Größenlimits, 413-Handling und Dateinamen-Regeln.
keywords: datei upload api multipart, 413 payload too large api, maximale dateigröße pro plan api, datei von url konvertieren api, magic bytes prüfung beim upload, dateiname der ausgabe festlegen api, mehrere dateien hochladen api, multipart form data datei senden
---

# Datei-Ingestion

Es gibt zwei Wege, Bytes an EnConvert zu übergeben: Lade die Datei selbst als `multipart/form-data` hoch, oder übergib eine `url` und lass die API die Ressource abrufen. Welchen du nutzen kannst, hängt vom Endpunkt ab, nicht von deinem Plan.

---

## Welche Endpunkte was entgegennehmen

| Endpunkt-Familie | Wie die Bytes ankommen | Feldname |
|---|---|---|
| [Datenformate](/de/docs/endpoints/convert/data-formats.md), [Dokumente](/de/docs/endpoints/convert/documents.md), [Bilder](/de/docs/endpoints/convert/images.md) | `multipart/form-data`-Upload, eine Datei pro Anfrage | `file` |
| [Webseiten](/de/docs/endpoints/convert/web-pages.md) (`url-to-pdf`, `url-to-screenshot`, `url-to-markdown`, `website-to-pdf`, `website-to-screenshot`) | JSON-Body, EnConvert ruft die Seite ab | `url` |
| [`POST /v2/ingest/files`](/de/docs/endpoints/ingest.md) | `multipart/form-data`, viele Dateien pro Job | `files` |
| [`POST /v2/perceive`](/de/docs/endpoints/perceive.md), [`POST /v2/ingest`](/de/docs/endpoints/ingest.md) | JSON-Body, EnConvert ruft die Seite ab | `url` |

Einen dritten Weg gibt es nicht. Die Datei-Upload-Endpunkte rufen keine URL für dich ab, auch nicht die Sammelrouten [anything-to-pdf](/de/docs/endpoints/convert/documents/anything-to-pdf.md) und [anything-to-markdown](/de/docs/endpoints/convert/documents/anything-to-markdown.md). Um eine Live-Seite in ein PDF zu verwandeln, rufe stattdessen [url-to-pdf](/de/docs/endpoints/convert/web-pages/url-to-pdf.md) auf. Welche Formate jeder Endpunkt akzeptiert, steht unter [Unterstützte Formate](/de/docs/reference/supported-formats.md).

---

## Eine lokale Datei hochladen

Das Formularfeld heißt `file`, und jeder V1-Datei-Upload-Endpunkt nimmt genau eines entgegen. Alles Weitere im Formular ist optional.

| Formularfeld | Typ | Beschreibung |
|-----------|------|-------------|
| `file` | file | Die zu konvertierende Datei. Ihre Dateiendung muss vom Endpunkt akzeptiert werden. |
| `output_filename` | `string` | Eigener Basisname für die Ausgabe. Die Zielendung wird für dich angehängt. |
| `job_id` | `string` | Vom Client vergebene Job-ID für die Timeout-Wiederherstellung. Frage `GET /v1/convert/status/{job_id}` ab, falls die Verbindung abbricht. |
| `pdf_options` | `string` | JSON-String mit PDF-Optionen, bei Endpunkten, die ein PDF erzeugen. |
| `direct_download` | `boolean` | Wird nur akzeptiert, damit die Request-Form zu den URL-Endpunkten passt. Hier hat es keine Wirkung: Ein Upload antwortet immer mit der JSON-Antwort unten, egal was du sendest. |

### curl

```bash
curl -X POST https://api.enconvert.com/v1/convert/anything-to-pdf \
  -H "X-API-Key: sk_your_private_key" \
  -F "file=@quarterly-report.docx" \
  -F "direct_download=false"
```

Die Antwort ist JSON mit einem vorsignierten Download-Link:

```json
{
    "presigned_url": "https://spaces.example.com/...signed...",
    "object_key": "env/files/{project_id}/anything-to-pdf/quarterly-report_20260714_101530123.pdf",
    "filename": "quarterly-report_20260714_101530123.pdf",
    "file_size": 51240,
    "conversion_time_seconds": 2.1,
    "job_id": null
}
```

Dieselben Werte werden in den Response-Headern `X-Object-Key`, `X-File-Size`, `X-Conversion-Time` und `X-Filename` gespiegelt, du kannst sie also lesen, ohne den Body zu parsen. Hole die Datei zügig ab: Der Link ist kurzlebig, und [Signierte URLs](/de/docs/concepts/signed-urls.md) erklärt genau, wie kurz.

### Python

```python
import requests

with open("quarterly-report.docx", "rb") as f:
    response = requests.post(
        "https://api.enconvert.com/v1/convert/anything-to-pdf",
        headers={"X-API-Key": "sk_your_private_key"},
        files={"file": ("quarterly-report.docx", f)},
        data={"direct_download": "false"},
    )

response.raise_for_status()
result = response.json()

# Download the PDF from the pre-signed URL.
pdf = requests.get(result["presigned_url"]).content
with open("quarterly-report.pdf", "wb") as out:
    out.write(pdf)
```

### Node.js

```javascript
import { readFile, writeFile } from "node:fs/promises";

const form = new FormData();
form.append(
    "file",
    new Blob([await readFile("quarterly-report.docx")]),
    "quarterly-report.docx"
);
form.append("direct_download", "false");

const response = await fetch(
    "https://api.enconvert.com/v1/convert/anything-to-pdf",
    { method: "POST", headers: { "X-API-Key": "sk_your_private_key" }, body: form }
);

const result = await response.json();
const pdf = await fetch(result.presigned_url).then((r) => r.arrayBuffer());
await writeFile("quarterly-report.pdf", Buffer.from(pdf));
```

### Viele Dateien in einem Aufruf

`POST /v2/ingest/files` ist der einzige Endpunkt, der mehr als eine Datei entgegennimmt. Wiederhole das Feld `files`, bis zu 200 Dateien pro Job:

```bash
curl -X POST https://api.enconvert.com/v2/ingest/files \
  -H "X-API-Key: sk_your_private_key" \
  -F "files=@handbook.pdf" \
  -F "files=@pricing.xlsx" \
  -F "files=@faq.docx" \
  -F "max_words=700" \
  -F "webhook_url=https://your-app.example.com/hooks/ingest"
```

Jede Datei wird zu Markdown konvertiert, gechunkt und zu einem einzigen JSONL-Artefakt zusammengesetzt. Der Job ist immer asynchron und antwortet mit `202 Accepted` und einer `job_id`. Alle Details stehen auf der [Seite zum Ingest-Endpunkt](/de/docs/endpoints/ingest.md).

---

## EnConvert die Datei abrufen lassen

Bei den URL-Endpunkten sendest du einen JSON-Body statt eines Formulars, und die API übernimmt das Abrufen:

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

`url` akzeptiert einen String oder ein Array von Strings. Ein Array schaltet die Anfrage auf asynchron um und wird unter [Batch-Verarbeitung](/de/docs/guides/batch-processing.md) behandelt.

### Quellen hinter einem Login

Drei optionale Felder erlauben es dem Abruf, Anmeldedaten mitzuführen. Alle drei erfordern einen Plan mit Basic-Auth-Zugriff (Indie und höher).

| Parameter | Typ | Standard | Beschreibung |
|-----------|------|---------|-------------|
| `auth` | `object` | `null` | HTTP-Basic-Auth-Anmeldedaten: `{"username": "...", "password": "..."}`. |
| `cookies` | `array` | `null` | Array von Cookie-Objekten, die vor der Navigation injiziert werden. Max. 50 pro Anfrage. Jedes benötigt `name`, `value` und entweder `domain` oder `url`. |
| `headers` | `object` | `null` | Eigene HTTP-Header, die mit den Anfragen gesendet werden. Max. 20 pro Anfrage. Blockierte Header sind nicht erlaubt: `host`, `content-length`, `transfer-encoding`, `connection`, `upgrade`, `te`, `trailer`. |

<div class="alert alert-info">
<strong>Scope der Anmeldedaten:</strong> Anmeldedaten aus dem <code>auth</code>-Objekt und ein über <code>headers</code> übergebener <code>Authorization</code>-Header (etwa ein Bearer-Token) gehen <strong>nur an den Ziel-Origin</strong>, niemals an Drittanbieter-Subressourcen, die die Seite anfordert. Das verhindert, dass Anmeldedaten an Werbe-, Analytics- oder CDN-Hosts abfließen.
</div>

### Private und interne Adressen

Die `url` muss eine öffentliche `http://`- oder `https://`-Adresse sein. Bevor irgendetwas abgerufen wird, wird sie geprüft und mit `400 Bad Request` abgelehnt, wenn sie:

- ein anderes Schema als `http`/`https` verwendet;
- Anmeldedaten einbettet, etwa `https://user:pass@host/` (nutze stattdessen das Feld `auth`);
- auf `localhost`, einen Cloud-Metadaten-Hostnamen oder eine IP zielt, die in einen privaten, Loopback-, Link-Local-, reservierten oder anderweitig nicht öffentlichen Bereich auflöst;
- eine nicht standardmäßige IP-Notation (oktal, hexadezimal oder als gepackte Ganzzahl) verwendet, die mehrdeutig auflösen könnte.

Das gilt für die Seed-URL, für jede URL in einem Batch und für die Seiten, die die `website-to-*`-Crawl-Endpunkte entdecken.

Klartext: EnConvert läuft außerhalb deines Netzwerks. Es erreicht weder `http://10.0.0.5/report.docx` noch einen `.internal`-Hostnamen noch irgendetwas, das nur innerhalb deiner VPC auflöst. Mach die Datei entweder aus dem öffentlichen Internet erreichbar, oder lies die Bytes selbst und lade sie hoch.

---

## Was mit deinem Dateinamen passiert

Der Name, den du sendest, erledigt zwei Aufgaben.

**Er wählt den Konverter.** Die Dateiendung entscheidet, welcher Eingabepfad läuft, benenne die Datei also korrekt. Eine Datei namens `report` ohne Endung wird von jedem Endpunkt abgelehnt, der eine Endungs-Allowlist hat.

**Er bildet die Grundlage für den Ausgabenamen.** Der Ausgabedateiname wird so gebaut:

```
{base}_{YYYYMMDD_HHMMSSmmm}.{ext}
```

Der UTC-Zeitstempel wird immer angehängt, zwei Konvertierungen derselben Datei kollidieren also nie. `base` wird in dieser Reihenfolge aufgelöst:

1. `output_filename`, falls du eines gesendet hast. Wenn du die Zielendung darin mitgeschickt hast, wird die Endung zuerst entfernt, damit du nicht `report.pdf_20260405_123456789.pdf` bekommst.
2. Der Name der hochgeladenen Datei ohne ihre Endung. `report.docx` erzeugt `report_20260405_123456789.pdf`.
3. Bei URL-Konvertierungen die Domain. `https://example.com/page` erzeugt `example_20260405_123456789.pdf`.
4. Wenn nichts davon greift, das wörtliche `output`.

Der Storage-Key wird bereinigt, bevor das Ergebnis geschrieben wird: Nur der Basisname bleibt übrig, `..` wird entfernt, die Zeichen `<>:"|?*` werden gestrippt, und Leerzeichen werden zu Unterstrichen. Lade `My Report (final).docx` hoch, und das PDF landet unter `My_Report_(final)_20260405_123456789.pdf`. Dieser Pfad wird dir als `object_key` zurückgegeben und hat die Form `{env}/files/{project_id}/{endpoint}/{filename}`.

Dateinamen, die an `POST /v2/ingest/files` gesendet werden, sind zusätzlich auf 255 Zeichen begrenzt.

---

## Größenobergrenze und der 413

Die Upload-Obergrenze gilt je Plan und für jede einzelne Datei.

| Plan | Max. Upload-Größe | Bytes |
|------|-----------------|-------|
| Founding | 5 MB | 5242880 |
| Indie | 15 MB | 15728640 |
| Studio | 50 MB | 52428800 |
| Production | 150 MB | 157286400 |
| Enterprise | Verhandelbar | Laut Vertrag |

Die Größe wird am hochgeladenen Part selbst gemessen, während der Body hereinströmt, nicht an einem Header, den du kontrollierst; ein Chunked-Upload ohne `Content-Length` wird also genauso geprüft. Eine Überschreitung liefert `413 Payload Too Large`, bevor irgendeine Konvertierungsarbeit passiert und bevor Ops berechnet werden:

```json
{
    "detail": {
        "error": "File too large",
        "file_size": 10485760,
        "max_size": 5242880,
        "tier": "free",
        "key_type": "private"
    }
}
```

`file_size` und `max_size` sind beide in Bytes. `tier` ist der Plan-Slug (`free`, `starter`, `pro`, `business`, `enterprise`), nicht der Anzeigename, den du auf der Preisseite siehst; ein Studio-Projekt meldet also `"tier": "pro"`. `key_type` ist `private`, `public` oder `dashboard`, mit Rückfall auf `unknown`.

<div class="alert alert-warning">
<strong>5 MB sind schnell weg.</strong> Im Founding-Plan liegt ein gescanntes 40-seitiges PDF oder ein Deck mit ein paar randlosen Fotos meist schon darüber. Es gibt keinen Weg für Chunked- oder fortsetzbare Uploads: Die Lösung ist eine kleinere Datei oder ein größerer Plan.
</div>

Das Größen-Gate zu passieren ist nicht die letzte Hürde. Ein aufgebrauchtes Monatskontingent antwortet mit `402 Payment Required`, und zu viele Anfragen in einem Zeitfenster antworten mit `429`; beides ist unter [Rate-Limits und Kontingente](/de/docs/reference/rate-limits.md) beschrieben.

---

## Content-Type und Magic Bytes

Uploads durchlaufen zwei Prüfungen, in dieser Reihenfolge.

**1. Die Endungs-Allowlist.** Jeder Endpunkt deklariert, welche Dateiendungen er akzeptiert. Eine Abweichung ist `400 Bad Request`:

```json
{
    "detail": "Invalid file format '.pdf' for png-to-jpeg. Allowed: .png"
}
```

**2. Der Magic-Byte-Test.** Die ersten Bytes der Datei werden mit der Gruppe verglichen, die ihre Endung behauptet. Eine Abweichung mit hoher Konfidenz ist ebenfalls `400`:

```json
{
    "detail": "File content does not match the 'png-to-jpeg' input type."
}
```

Genau das bekommst du, wenn du ein PDF in `.png` umbenennst und hochlädst: Die Bytes beginnen mit `%PDF-`, die Endung sagt PNG, und beide widersprechen sich. Die Prüfung existiert, weil die Endung deine Anfrage routet. Ohne sie erreichen PDF-Bytes einen Bild-Decoder, und du bekommst einen undurchsichtigen Fehler tief im Konverter statt eines klaren 400 an der Tür, und eine absichtlich falsch bezeichnete Datei landet bei einem Parser, der sie nie zu sehen bekommen sollte.

Zwei Dinge, die das **nicht** tut, beide wissenswert:

- Der `Content-Type` des Parts wird nie inspiziert. Es gibt nirgends im Upload-Pfad eine MIME-Allowlist, `application/octet-stream` ist also in Ordnung. Die Dateiendung ist das Einzige, was die Anfrage routet, und genau deshalb schlägt eine Datei ohne Endung fehl.
- Textformate haben keine verlässliche Signatur und überspringen den Test komplett: `.json`, `.csv`, `.xml`, `.yaml`, `.toml`, `.md`, `.html`, `.svg`, `.txt`. Eine `.json`-Datei, die in Wahrheit CSV enthält, wird an dieser Stelle akzeptiert und scheitert später, im Parser.

Die erkannten Binärsignaturen sind PNG, JPEG, GIF, WebP, HEIC/HEIF, PDF und die Office-Gruppe (ein ZIP-Container für `.docx`/`.xlsx`/`.pptx`/ODF/EPUB, oder das alte OLE2 für `.doc`/`.xls`/`.ppt`). Der Test ist bewusst fail-open: Bytes, die er nicht erkennt, werden durchgelassen statt blockiert.

---

## Große Dateien: die Checkliste

1. **Prüfe zuerst die Obergrenze.** Ein 413 ist für die API billig und für dich teuer, denn du hast den kompletten Body hochgeladen, um ihn zu bekommen.
2. **Rechne mit einem synchronen Upload.** `async_mode` gibt es nur bei `url-to-pdf`, `url-to-screenshot` und `url-to-markdown`. Datei-Upload-Endpunkte ignorieren es und konvertieren immer innerhalb der Anfrage. Siehe [Synchrone und asynchrone Jobs](/de/docs/concepts/sync-and-async.md).
3. **Sende eine selbst erzeugte `job_id`.** Wenn ein vorgelagerter Proxy die Verbindung kappt, bevor die Konvertierung fertig ist, läuft die Arbeit trotzdem zu Ende. Frage `GET /v1/convert/status/{job_id}` ab, und du bekommst `{"status": "processing"}`, dann entweder `{"status": "success", "presigned_url": ..., "object_key": ...}` oder `{"status": "failed", "error": ...}`. Deine eigene ID erneut zu verwenden startet diesen Job neu; die ID eines anderen Projekts zu verwenden liefert `409`.
4. **Kalkuliere die Timeouts ein.** Eine Anfrage ist Ende zu Ende auf 300 Sekunden gedeckelt, danach bekommst du `504` mit `{"error": "Request timeout"}`. Office-Konvertierungen auf LibreOffice-Basis haben ihre eigene 120-Sekunden-Grenze, die ebenfalls als `504` erscheint.
5. **Behandle `503` mit `Retry-After: 10`.** Dateikonvertierungen laufen hinter einem Admission-Gate mit einer begrenzten Warteschlange. Ist die Warteschlange voll, wird die Anfrage sofort abgelehnt, statt sich anzustellen; wiederhole sie also nach dem Intervall aus dem Header.
6. **Bei vielen Dokumenten: wechsle den Endpunkt.** `POST /v2/ingest/files` nimmt bis zu 200 Dateien, antwortet sofort mit `202` und einer `job_id` und akzeptiert eine `webhook_url`, sodass du nie pollen musst. Siehe [Webhooks](/de/docs/guides/webhooks.md).
7. **Lade zügig herunter.** Ausgabelinks sind signiert und laufen ab. Ist deiner abgelaufen, lies den Status-Endpunkt erneut: Jede Abfrage erzeugt einen frischen Link auf dasselbe gespeicherte Objekt.

---

## Häufig gestellte Fragen

### Welchen Feldnamen erwartet die EnConvert-API für einen Datei-Upload?

`file`, als `multipart/form-data` gesendet, eine Datei pro Anfrage, bei jedem V1-Konvertierungsendpunkt, der einen Upload entgegennimmt. Die Ausnahme ist `POST /v2/ingest/files`, das `files` verwendet und bis zu 200 Dateien pro Job akzeptiert.

### Kann EnConvert die Datei von einer URL herunterladen, statt dass ich sie hochlade?

Nur bei den URL-Endpunkten (`url-to-pdf`, `url-to-screenshot`, `url-to-markdown`, `website-to-pdf`, `website-to-screenshot`) und den V2-Endpunkten `/v2/perceive` und `/v2/ingest`. Die Datei-Upload-Konvertierungsendpunkte haben keinen `url`-Parameter. Jede URL, die du übergibst, muss öffentlich erreichbar sein: Private, Loopback-, Link-Local- und Cloud-Metadaten-Adressen werden mit `400` abgelehnt.

### Warum hat mein Upload 413 Payload Too Large zurückgegeben?

Die Datei war größer als die Obergrenze pro Datei deines Plans, die bei Founding 5 MB, bei Indie 15 MB, bei Studio 50 MB und bei Production 150 MB beträgt. Der Response-Body trägt ein `detail`-Objekt mit `error`, `file_size`, `max_size`, `tier` und `key_type`, sodass du dem Aufrufer die exakten Zahlen zeigen kannst.

### Warum scheitert mein PNG-Upload mit „File content does not match“?

Die ersten Bytes der Datei gehören zu einem anderen Format als dem, das ihre Endung behauptet, zum Beispiel ein PDF, das in `.png` umbenannt wurde. Sende die Datei unter ihrer echten Endung. Textformate wie `.json` und `.csv` werden nie byteweise geprüft, dieser Fehler erscheint also nur bei Binärtypen.

### Kann ich eine große Datei asynchron hochladen?

Nicht bei den V1-Datei-Upload-Endpunkten: Sie konvertieren immer innerhalb der Anfrage. Sende eine clientseitig erzeugte `job_id` und frage `GET /v1/convert/status/{job_id}` ab, um einen Verbindungsabbruch zu überstehen, oder nutze `POST /v2/ingest/files`, das per Design asynchron ist und einen Webhook aufrufen kann, wenn der Job fertig ist.
