> ## 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.

# Avvia un’esecuzione

> Avvia un’esecuzione Data App con input conformi al contratto di input. Opzionalmente attendi il risultato.

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

Autenticazione: API key obbligatoria (`Authorization: Bearer <API Key>`).

Il body della richiesta è un’istanza del contratto di input dell’app (`input_schema` dal dettaglio app). La validazione è strict.

`wait` controlla se questa chiamata attende un risultato:

* **Omesso**: segui il default `execution.mode` dell’app. Le app `sync` attendono uno stato terminale (limitato da `execution.timeout_seconds`); le app `async` restituiscono subito un `run_id`.
* **Esplicito 0–60**: entrambi i modi si comportano uguale e attendono al massimo `wait` secondi. `wait=0` ritorna appena la run è in coda. Per un’app `sync` puoi prendere prima il `run_id` e poi fare long-poll con <a href="/docs/it/datahub/api/reference/runs/get-run">Ottieni un’esecuzione</a> `wait`.

Se la risposta è già in stato terminale, `sample_records` può includere il primo batch. Scorri il risultato completo con <a href="/docs/it/datahub/api/reference/runs/get-run-records">Ottieni record dell’esecuzione</a>.

`version` pinna la run a un release storico (contratto e pricing seguono quella versione). `build` è per debug dell’autore: pinna uno snapshot di build immutabile (`run_kind=test`, escluso dalle stats pubbliche, comunque fatturato). `version` e `build` sono mutuamente esclusivi.

Il gate della run coincide col gate di dettaglio: app invisibili restituiscono `404`; app visibili che non accettano run restituiscono `403` (i self-test dell’autore non hanno restrizioni); nuove run pinnate a una versione yanked sono rifiutate (`422`).

## Richiesta

### Parametri di percorso

<ParamField path="app_id" type="string" required>
  Riferimento app: `app_<hex>` oppure `<namespace>/<app_name>`.
</ParamField>

### Parametri di query

<ParamField query="wait" type="number">
  Secondi massimi di attesa di uno stato terminale. Ometti per il default della mode app; `0` ritorna subito dopo lo start.

  Intervallo da 0 a 60.
</ParamField>

<ParamField query="max_records" type="integer">
  Numero massimo di record da produrre. La run termina normalmente al raggiungimento del cap. Usalo per controllare costo e durata.

  Intervallo ≥ 1.
</ParamField>

<ParamField query="triggered_by" type="string" default="api">
  Marker di canale. Default `api`; SDK e MCP impostano i propri valori. Usabile come filtro su lista run e fatturazione.
</ParamField>

<ParamField query="version" type="string">
  Pinna a una versione specifica. Default: ultima versione.
</ParamField>

<ParamField query="build" type="string">
  Solo debug autore. Pinna a uno snapshot di build. Mutuamente esclusivo con `version`.
</ParamField>

### Body della richiesta

Oggetto JSON conforme a `input_schema` dell’app. Partenza più sicura: copia una entry da `examples` nel dettaglio app e modificala.

### Esempio di richiesta

```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"
```

## Risposta

### 200 successo

```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": []
  }
}
```

Il payload è wrappato in `data`. Campi:

<ResponseField name="run_id" type="string" required>
  ID esecuzione. Usalo per stato, record e cancel.
</ResponseField>

<ResponseField name="namespace" type="string">
  Username del publisher.
</ResponseField>

<ResponseField name="app_name" type="string">
  Nome dell'app.
</ResponseField>

<ResponseField name="app_version" type="string">
  Versione pinnata. `null` per run di debug.
</ResponseField>

<ResponseField name="build_id" type="string">
  Snapshot di build pinnato da una run di debug. `null` per run production.
</ResponseField>

<ResponseField name="run_kind" type="string">
  `production` per run normali; `test` per run di debug dell’autore.
</ResponseField>

<ResponseField name="state" type="enum" required>
  Stato run. Valori: `PENDING` / `QUEUED` / `RUNNING` / `SUCCEEDED` / `PARTIALLY_SUCCEEDED` / `FAILED` / `CANCELLED` / `EXPIRED`.
</ResponseField>

<ResponseField name="input" type="object">
  Echo dell’input. I campi marcati `sensitive` nel contratto di input sono redatti.
</ResponseField>

<ResponseField name="progress" type="object">
  Progresso. `done` / `total` sono riportati dall’app; `status_text` è una stringa di stato leggibile dall’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">
  ID dataset risultato.
</ResponseField>

<ResponseField name="partial" type="boolean">
  `true` quando la run è parzialmente riuscita o è stata annullata. I record prodotti restano usabili.
</ResponseField>

<ResponseField name="cancel_requested" type="boolean">
  `true` mentre l'annullamento è stato richiesto e il run è ancora in fase di chiusura (arresto cooperativo / recupero dei risultati parziali). Sempre `false` negli stati terminali (normalizzato lato server), quindi i client non devono ricavarlo da `state`.
</ResponseField>

<ResponseField name="triggered_by" type="string">
  Canale di avvio.
</ResponseField>

<ResponseField name="upstream_ref" type="string">
  ID task upstream, se presente.
</ResponseField>

<ResponseField name="created_at" type="string">
  Ora di start. Anche ancora per filtri temporali e attribution di fatturazione.
</ResponseField>

<ResponseField name="started_at" type="string">
  Ora di start dell’esecuzione più recente. Re-stampata al retry.
</ResponseField>

<ResponseField name="first_started_at" type="string">
  Quando un worker ha claimato la run la prima volta. A differenza di `started_at` non viene mai riscritto al retry: ancora per il tempo in coda: queued = `first_started_at - created_at`, wall time totale = `finished_at - first_started_at`. Derivare il tempo in coda da `started_at` conta i tentativi precedenti come queueing. `null` solo per run mai claimate.
</ResponseField>

<ResponseField name="finished_at" type="string">
  Ora di fine.
</ResponseField>

<ResponseField name="usage" type="object">
  Metering usage oggettivo: quanto ha fatto la run. Separato dalla fatturazione; è ciò che le evaluation confrontano.

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

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

<ResponseField name="billing" type="object">
  Ledger di fatturazione = usage × pricing × regole di billing. `events[]` elenca ogni evento billable con quantità, prezzo unitario e importo; `total` è la somma; `charged` indica se l’addebito è stato applicato. Completo solo dopo uno stato terminale.

  <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">
  Oggetto errore in caso di 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[]">
  Primo batch di record quando la risposta è già terminale; altrimenti `null`.
</ResponseField>

<ResponseField name="warnings" type="object[]">
  Warning strutturati, ad esempio `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>

### Errori

| HTTP | `code`                   | `category`      | Descrizione                                                                                                              |
| ---- | ------------------------ | --------------- | ------------------------------------------------------------------------------------------------------------------------ |
| 401  | `unauthorized`           | `forbidden`     | API key mancante o non valida.                                                                                           |
| 400  | `invalid-input`          | `invalid_input` | Il body non soddisfa il contratto di input dell’app. `details[]` elenca ogni path di campo e motivo.                     |
| 404  | `app-not-found`          | `not_found`     | L’app non esiste, è stata rinominata o è invisibile alla credenziale corrente (private / fuori dallo share scope).       |
| 403  | `app-not-accepting-runs` | `forbidden`     | L’app è in manutenzione e non accetta nuove run. `message` può includere una nota del publisher.                         |
| 402  | `balance-negative`       | `forbidden`     | Il saldo wallet è negativo; le nuove run sono bloccate. Ricarica, poi ritenta la stessa request.                         |
| 503  | `billing-unavailable`    | `temporary`     | L’account ha addebiti aperti e la fatturazione non può verificare il saldo ora. `retryable` è `true`; riprova più tardi. |
| 422  | `version-yanked`         | `invalid_input` | La versione pinnata è stata yanked dal publisher e non accetta più nuove run.                                            |

Le risposte di errore usano `{"error": {code, category, message, retryable}}`. Vedi <a href="/docs/it/datahub/api/reference/introduction#errors">Errori</a>.

## Librerie client

<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>

## Note

* Vocabolario stati: `PENDING`, `QUEUED`, `RUNNING`, `SUCCEEDED`, `PARTIALLY_SUCCEEDED`, `FAILED`, `CANCELLED`, `EXPIRED`. Gli ultimi cinque sono terminali.
* Non avviare una seconda run per lo stesso obiettivo solo perché un wait è andato in timeout. Fai prima poll del `run_id` esistente.
* Le run fallite non sono fatturate. Successo parziale e cancel fatturano solo i record prodotti.
