> ## Documentation Index
> Fetch the complete documentation index at: https://www.octoparse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Run starten

> Data-App-Run mit Inputs starten, die dem Input-Vertrag genügen. Optional auf das Ergebnis warten.

**`POST`** `https://api-datahub.octoparse.com/v1/data-apps/{app_id}/runs`

Authentifizierung: API-Schlüssel erforderlich (`Authorization: Bearer <API Key>`).

Der Request-Body ist eine Instanz des App-Input-Vertrags (`input_schema` aus dem App-Detail). Validierung ist strikt.

`wait` steuert, ob dieser Aufruf auf ein Ergebnis wartet:

* **Weggelassen**: dem App-Default `execution.mode` folgen. `sync`-Apps warten bis zu einem terminalen Status (gedeckelt durch `execution.timeout_seconds`); `async`-Apps geben sofort eine `run_id` zurück.
* **Explizit 0–60**: beide Modi verhalten sich gleich und warten höchstens `wait` Sekunden. `wait=0` kehrt zurück, sobald der Run gequeued ist. Bei einer `sync`-App können Sie zuerst die `run_id` nehmen und dann mit <a href="/docs/de/datahub/api/reference/runs/get-run">Run abrufen</a> `wait` long-pollen.

Erreicht die Antwort bereits einen terminalen Status, kann `sample_records` den ersten Batch enthalten. Das volle Ergebnis seitenweise mit <a href="/docs/de/datahub/api/reference/runs/get-run-records">Run-Datensätze abrufen</a> lesen.

`version` pinnt den Run auf ein historisches Release (Vertrag und Preise folgen dieser Version). `build` ist für Autor-Debugging: pinnt einen immutable Build-Snapshot (`run_kind=test`, aus öffentlichen Stats ausgeschlossen, weiterhin billable). `version` und `build` schließen sich gegenseitig aus.

Das Run-Gate entspricht dem Detail-Gate: unsichtbare Apps liefern `404`; sichtbare Apps, die keine Runs annehmen, liefern `403` (Autor-Selbsttests uneingeschränkt); neue Runs auf eine geyankte Version werden abgelehnt (`422`).

## Anfrage

### Pfadparameter

<ParamField path="app_id" type="string" required>
  App-Referenz: `app_<hex>` oder `<namespace>/<app_name>`.
</ParamField>

### Abfrageparameter

<ParamField query="wait" type="number">
  Maximale Sekunden bis zu einem terminalen Status. Weglassen für App-Mode-Default; `0` kehrt sofort nach Start zurück.

  Bereich 0 bis 60.
</ParamField>

<ParamField query="max_records" type="integer">
  Maximale Anzahl zu erzeugender Datensätze. Der Run endet normal, wenn das Cap erreicht ist. Zur Kosten- und Dauersteuerung.

  Bereich ≥ 1.
</ParamField>

<ParamField query="triggered_by" type="string" default="api">
  Kanalmarker. Standard `api`; SDKs und MCP setzen eigene Werte. Als Filter in Run-Liste und Abrechnung nutzbar.
</ParamField>

<ParamField query="version" type="string">
  Auf eine bestimmte Version pinnen. Standard: neueste Version.
</ParamField>

<ParamField query="build" type="string">
  Nur Autor-Debug. Auf einen Build-Snapshot pinnen. Gegenseitig exklusiv mit `version`.
</ParamField>

### Anfrage-Body

JSON-Objekt gemäß App-`input_schema`. Sicherster Start: einen Eintrag aus `examples` im App-Detail kopieren und anpassen.

### Beispielanfrage

```bash theme={null}
curl -X POST \
  -H "Authorization: Bearer $OCTOPARSE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"product": "p-9001"}' \
  "https://api-datahub.octoparse.com/v1/data-apps/carol/probe-b/runs?wait=60"
```

## Antwort

### 200 Erfolg

```json theme={null}
{
  "data": {
    "run_id": "run_c62bc0fb8df2",
    "namespace": "carol",
    "app_name": "probe-b",
    "app_version": "0.1.0",
    "build_id": null,
    "run_kind": "production",
    "state": "SUCCEEDED",
    "input": {
      "product": "p-now"
    },
    "progress": {
      "done": 20,
      "total": null,
      "status_text": null
    },
    "dataset_id": "ds_0bd5345d13d0",
    "partial": false,
    "cancel_requested": false,
    "triggered_by": "api",
    "upstream_ref": null,
    "created_at": "2026-09-15T07:45:40.411993+00:00",
    "started_at": "2026-09-15T07:45:40.419610+00:00",
    "first_started_at": "2026-09-15T07:45:40.419610+00:00",
    "finished_at": "2026-09-15T07:45:40.822250+00:00",
    "usage": {
      "metrics": {
        "records_collected": 20
      },
      "duration_ms": 402
    },
    "billing": {
      "events": [
        {
          "event": "record",
          "label": "One record",
          "qty": 20.0,
          "unit_price": 0.001,
          "amount": 0.02,
          "unit_size": null,
          "raw_qty": null
        }
      ],
      "total": 0.02,
      "currency": "USD",
      "charged": true
    },
    "error": null,
    "sample_records": null,
    "warnings": []
  }
}
```

Payload ist in `data` gewrappt. Felder:

<ResponseField name="run_id" type="string" required>
  Run-ID. Für Status, Datensätze und Abbruch verwenden.
</ResponseField>

<ResponseField name="namespace" type="string">
  Publisher-Benutzername.
</ResponseField>

<ResponseField name="app_name" type="string">
  App-Name.
</ResponseField>

<ResponseField name="app_version" type="string">
  Gepinnte Version. `null` bei Debug-Runs.
</ResponseField>

<ResponseField name="build_id" type="string">
  Vom Debug-Run gepinnter Build-Snapshot. `null` bei Production-Runs.
</ResponseField>

<ResponseField name="run_kind" type="string">
  `production` für normale Runs; `test` für Autor-Debug-Runs.
</ResponseField>

<ResponseField name="state" type="enum" required>
  Run-Status. Werte: `PENDING` / `QUEUED` / `RUNNING` / `SUCCEEDED` / `PARTIALLY_SUCCEEDED` / `FAILED` / `CANCELLED` / `EXPIRED`.
</ResponseField>

<ResponseField name="input" type="object">
  Input-Echo. Im Input-Vertrag als `sensitive` markierte Felder sind redacted.
</ResponseField>

<ResponseField name="progress" type="object">
  Fortschritt. `done` / `total` meldet die App; `status_text` ist ein menschenlesbarer Statusstring der App.

  <Expandable title="fields">
    <ResponseField name="done" type="integer">
      —
    </ResponseField>

    <ResponseField name="total" type="integer">
      —
    </ResponseField>

    <ResponseField name="status_text" type="string">
      —
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="dataset_id" type="string">
  Ergebnis-Dataset-ID.
</ResponseField>

<ResponseField name="partial" type="boolean">
  `true`, wenn der Run teilweise erfolgreich war oder abgebrochen wurde. Erzeugte Datensätze bleiben nutzbar.
</ResponseField>

<ResponseField name="cancel_requested" type="boolean">
  `true`, solange ein Abbruch angefordert wurde und der Run noch ausläuft (kooperativer Stopp / Wiederherstellung von Teilergebnissen). In Endzuständen immer `false` (serverseitig normalisiert), sodass Clients es nicht aus `state` ableiten müssen.
</ResponseField>

<ResponseField name="triggered_by" type="string">
  Startkanal.
</ResponseField>

<ResponseField name="upstream_ref" type="string">
  Upstream-Task-ID, falls vorhanden.
</ResponseField>

<ResponseField name="created_at" type="string">
  Startzeit. Auch Anker für Zeitfilter und Abrechnungszuordnung.
</ResponseField>

<ResponseField name="started_at" type="string">
  Zeit des letzten Ausführungsstarts. Bei Retry neu gestempelt.
</ResponseField>

<ResponseField name="first_started_at" type="string">
  Wann ein Worker den Run zuerst geclaimed hat. Anders als `started_at` wird dies bei Retry nie überschrieben — Anker für Queue-Zeit: queued = `first_started_at - created_at`, Gesamtwandzeit = `finished_at - first_started_at`. Queue-Zeit aus `started_at` abzuleiten zählt frühere Attempts als Queuing. `null` nur bei nie geclaimten Runs.
</ResponseField>

<ResponseField name="finished_at" type="string">
  Endzeit.
</ResponseField>

<ResponseField name="usage" type="object">
  Objektives Usage-Metering: wie viel der Run geleistet hat. Getrennt von der Abrechnung; das vergleichen Evaluations.

  <Expandable title="fields">
    <ResponseField name="metrics" type="object">
      —
    </ResponseField>

    <ResponseField name="duration_ms" type="integer">
      —
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="billing" type="object">
  Abrechnungs-Ledger = Usage × Pricing × Billing-Regeln. `events[]` listet jedes billable Event mit Menge, Stückpreis und Betrag; `total` ist die Summe; `charged` gibt an, ob die Belastung angewendet wurde. Vollständig erst nach einem terminalen Status.

  <Expandable title="fields">
    <ResponseField name="events" type="object[]">
      —

      <Expandable title="fields">
        <ResponseField name="event" type="string" required>
          —
        </ResponseField>

        <ResponseField name="label" type="string">
          —
        </ResponseField>

        <ResponseField name="qty" type="number" required>
          —
        </ResponseField>

        <ResponseField name="unit_price" type="number" required>
          —
        </ResponseField>

        <ResponseField name="amount" type="number" required>
          —
        </ResponseField>

        <ResponseField name="unit_size" type="integer">
          —
        </ResponseField>

        <ResponseField name="raw_qty" type="number">
          —
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="total" type="number">
      —
    </ResponseField>

    <ResponseField name="currency" type="string">
      —
    </ResponseField>

    <ResponseField name="charged" type="boolean">
      —
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="error" type="object">
  Fehlerobjekt bei Failure (`code` / `category` / `message` / `retryable`).

  <Expandable title="fields">
    <ResponseField name="code" type="string" required>
      —
    </ResponseField>

    <ResponseField name="category" type="string">
      —
    </ResponseField>

    <ResponseField name="message" type="string" required>
      —
    </ResponseField>

    <ResponseField name="retryable" type="boolean">
      —
    </ResponseField>

    <ResponseField name="retry_after" type="number">
      —
    </ResponseField>

    <ResponseField name="item_index" type="integer">
      —
    </ResponseField>

    <ResponseField name="details" type="object[]">
      —
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="sample_records" type="object[]">
  Erster Datensatz-Batch, wenn die Antwort bereits terminal ist; sonst `null`.
</ResponseField>

<ResponseField name="warnings" type="object[]">
  Strukturierte Warnings, z. B. `billing-qty-missing`.

  <Expandable title="fields">
    <ResponseField name="code" type="string" required>
      —
    </ResponseField>

    <ResponseField name="event" type="string">
      —
    </ResponseField>

    <ResponseField name="detail" type="object">
      —
    </ResponseField>

    <ResponseField name="count" type="integer">
      —
    </ResponseField>

    <ResponseField name="updated_at" type="string">
      —
    </ResponseField>
  </Expandable>
</ResponseField>

### Fehler

| HTTP | `code`                   | `category`      | Beschreibung                                                                                                                             |
| ---- | ------------------------ | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| 401  | `unauthorized`           | `forbidden`     | Fehlender oder ungültiger API-Schlüssel.                                                                                                 |
| 400  | `invalid-input`          | `invalid_input` | Request-Body erfüllt den App-Input-Vertrag nicht. `details[]` listet jeden Feldpfad und Grund.                                           |
| 404  | `app-not-found`          | `not_found`     | App existiert nicht, wurde umbenannt oder ist für die aktuelle Credential unsichtbar (private / außerhalb Share-Scope).                  |
| 403  | `app-not-accepting-runs` | `forbidden`     | App ist in Wartung und nimmt keine neuen Runs an. `message` kann eine Publisher-Notiz enthalten.                                         |
| 402  | `balance-negative`       | `forbidden`     | Wallet-Saldo ist negativ; neue Runs sind blockiert. Aufladen, dann denselben Request erneut.                                             |
| 503  | `billing-unavailable`    | `temporary`     | Das Konto hat offene Belastungen und die Abrechnung kann den Saldo gerade nicht prüfen. `retryable` ist `true`; später erneut versuchen. |
| 422  | `version-yanked`         | `invalid_input` | Die gepinnte Version wurde vom Publisher geyankt und nimmt keine neuen Runs mehr an.                                                     |

Fehlerantworten nutzen `{"error": {code, category, message, retryable}}`. Siehe <a href="/docs/de/datahub/api/reference/introduction#errors">Fehler</a>.

## Client-Bibliotheken

<CodeGroup>
  ```python Python theme={null}
  # Start and wait for a terminal state (SDK polls internally)
  run = client.call("carol/probe-b", {"product": "p-9001"}, max_records=100)
  print(run["state"], run["billing"]["total"])

  # Start only; do not wait
  run = client.run("carol/probe-b", {"product": "p-9001"}, wait=0)
  run_id = run["run_id"]
  ```

  ```js JavaScript theme={null}
  // Start and wait for a terminal state (SDK polls internally; timeout is in ms)
  const run = await client.call("carol/probe-b", { product: "p-9001" }, { maxRecords: 100, timeout: 120_000 });
  console.log(run.state, run.billing.total);

  // Start only; do not wait
  const started = await client.run("carol/probe-b", { product: "p-9001" }, { wait: 0 });
  const runId = started.run_id;
  ```
</CodeGroup>

## Hinweise

* Status-Vokabular: `PENDING`, `QUEUED`, `RUNNING`, `SUCCEEDED`, `PARTIALLY_SUCCEEDED`, `FAILED`, `CANCELLED`, `EXPIRED`. Die letzten fünf sind terminal.
* Starten Sie keinen zweiten Run für dasselbe Ziel nur weil ein Wait getimed out ist. Zuerst die bestehende `run_id` pollen.
* Fehlgeschlagene Runs werden nicht abgerechnet. Teilerfolg und Cancel billen nur erzeugte Datensätze.
