# Einsendungen exportieren

> Lade Einsendungen als CSV aus dem Dashboard herunter oder exportiere alle Einsendungen über die REST API.

## CSV aus dem Dashboard

Öffne **Submissions** oder den Einsendungen-Tab eines Formulars, setze die gewünschten Filter und wähle **Export CSV**. Die Datei folgt den Filtern auf dem Bildschirm.

| Filter | Werte |
| --- | --- |
| Formular | Ein Formular oder alle Formulare |
| Status | Empfangen, Spam oder beides |
| Zeitraum | Letzte 24 Stunden, letzte 7 Tage, letzte 30 Tage oder gesamter Zeitraum |

- Die Spalten sind `id`, `created_at`, `form`, `status`, danach eine Spalte pro Feld. Bei einem einzelnen Formular kommen zuerst seine definierten Felder, gefolgt von allen anderen Feldnamen, die in den exportierten Zeilen vorkommen.
- Arrays aus einfachen Werten werden mit Komma und Leerzeichen verbunden. Objekte werden als JSON geschrieben.
- Werte, die mit `=`, `+`, `-`, `@`, einem Tab oder einem Wagenrücklauf beginnen, bekommen ein vorangestelltes `'`, damit Tabellenprogramme sie nicht als Formeln ausführen.
- Die Datei ist UTF-8 mit Byte Order Mark, damit Excel Umlaute und Akzente korrekt öffnet.
- Ein Export enthält bis zu 100.000 Zeilen und 500 Feldspalten. Grenze die Filter bei größeren Workspaces ein oder nutze die API.

## Alles über die REST API

Für ein vollständiges Backup, eine geplante Synchronisierung oder mehr als 100.000 Zeilen blätterst du mit einem Secret Key, der `submissions:read` hat, durch die [REST API](https://ikiform.com/de/docs/api.md#list-submissions). Listen sind nach Neueste zuerst sortiert und bleiben stabil, während neue Einsendungen eingehen.

**Node** (`export.ts`)

```ts
import { createWriteStream } from "node:fs";
import { createClient } from "@ikiform/sdk";

const ikiform = createClient({ apiKey: process.env.IKIFORM_SECRET_KEY! });
const out = createWriteStream("submissions.jsonl");

for await (const submission of ikiform.submissions.iterate({
  formId: "frm_xxxxxxxxxxxxxxxx",
  status: "received",
  limit: 100,
})) {
  out.write(`${JSON.stringify(submission)}\n`);
}

out.end();
```

**Python** (`export.py`)

```python
import csv
import os

import requests

session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['IKIFORM_SECRET_KEY']}"
params = {"form_id": "frm_xxxxxxxxxxxxxxxx", "status": "received", "limit": 100}

with open("submissions.csv", "w", newline="", encoding="utf-8") as file:
    writer = None
    while True:
        page = session.get("https://ikiform.com/api/v1/submissions", params=params, timeout=30).json()
        for submission in page["data"]:
            row = {"id": submission["id"], "created_at": submission["created_at"], **submission["fields"]}
            if writer is None:
                writer = csv.DictWriter(file, fieldnames=list(row), extrasaction="ignore")
                writer.writeheader()
            writer.writerow(row)
        if not page["has_more"]:
            break
        params["cursor"] = page["next_cursor"]
```

> Jeder Schlüssel kann 100 Requests pro Minute stellen. Mit `limit: 100` sind das bis zu 10.000 Einsendungen pro Minute. Der Iterator des SDK wirft bei `429` einen `IkiformError` mit `retryAfter`; warte so viele Sekunden und mach ab dem letzten Cursor weiter.
