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

# Iniciar una ejecución

> Inicia una ejecución de Data App con entradas que satisfacen el contrato de entrada. Opcionalmente espera el resultado.

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

Autenticación: se requiere API key (`Authorization: Bearer <API Key>`).

El cuerpo de la solicitud es una instancia del contrato de entrada de la app (`input_schema` del detalle). La validación es estricta.

`wait` controla si esta llamada espera un resultado:

* **Omitido**: sigue el valor por defecto de `execution.mode` de la app. Las apps `sync` esperan hasta un estado terminal (limitado por `execution.timeout_seconds` de la app); las apps `async` devuelven un `run_id` de inmediato.
* **Explícito 0–60**: ambos modos se comportan igual y esperan como máximo `wait` segundos. `wait=0` devuelve en cuanto la ejecución está en cola. En una app `sync` puedes tomar primero el `run_id` y luego hacer long-poll con <a href="/docs/es/datahub/api/reference/runs/get-run">Obtener una ejecución</a> `wait`.

Cuando la respuesta ya alcanza un estado terminal, `sample_records` puede incluir el primer lote. Página el resultado completo con <a href="/docs/es/datahub/api/reference/runs/get-run-records">Obtener registros de ejecución</a>.

`version` fija la ejecución a un release histórico (contrato y precios siguen esa versión). `build` es para depuración del autor: fija un snapshot de build inmutable (`run_kind=test`, excluido de las estadísticas públicas, igual se factura). `version` y `build` son mutuamente excluyentes.

La puerta de ejecución coincide con la de detalle: las apps invisibles devuelven `404`; las visibles que no aceptan ejecuciones devuelven `403` (las autopruebas del autor no tienen restricción); las ejecuciones nuevas fijadas a una versión yank se rechazan (`422`).

## Solicitud

### Parámetros de ruta

<ParamField path="app_id" type="string" required>
  Referencia de app: `app_<hex>` o `<namespace>/<app_name>`.
</ParamField>

### Parámetros de consulta

<ParamField query="wait" type="number">
  Segundos máximos de espera de un estado terminal. Omite para usar el valor por defecto del modo de la app; `0` devuelve de inmediato tras el inicio.

  Rango 0 a 60.
</ParamField>

<ParamField query="max_records" type="integer">
  Máximo de registros a producir. La ejecución termina con normalidad al alcanzar el tope. Úsalo para controlar coste y duración.

  Rango ≥ 1.
</ParamField>

<ParamField query="triggered_by" type="string" default="api">
  Marcador de canal. Por defecto `api`; los SDK y MCP ponen sus propios valores. Sirve de filtro en el listado de ejecuciones y en facturación.
</ParamField>

<ParamField query="version" type="string">
  Fijar a una versión concreta. Por defecto la última versión.
</ParamField>

<ParamField query="build" type="string">
  Solo debug del autor. Fijar a un snapshot de build. Mutuamente excluyente con `version`.
</ParamField>

### Cuerpo de la solicitud

Objeto JSON con la forma de `input_schema` de la app. Lo más seguro es copiar una entrada de `examples` del detalle de la app y editarla.

### Ejemplo de solicitud

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

## Respuesta

### 200 correcto

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

La carga útil va envuelta en `data`. Campos:

<ResponseField name="run_id" type="string" required>
  Id de la ejecución. Úsalo para estado, registros y cancelación.
</ResponseField>

<ResponseField name="namespace" type="string">
  Nombre de usuario del publicador.
</ResponseField>

<ResponseField name="app_name" type="string">
  Nombre de la app.
</ResponseField>

<ResponseField name="app_version" type="string">
  Versión fijada. `null` en ejecuciones de debug.
</ResponseField>

<ResponseField name="build_id" type="string">
  Snapshot de build fijado por una ejecución de debug. `null` en ejecuciones de producción.
</ResponseField>

<ResponseField name="run_kind" type="string">
  `production` para ejecuciones normales; `test` para debug del autor.
</ResponseField>

<ResponseField name="state" type="enum" required>
  Estado de la ejecución. Valores: `PENDING` / `QUEUED` / `RUNNING` / `SUCCEEDED` / `PARTIALLY_SUCCEEDED` / `FAILED` / `CANCELLED` / `EXPIRED`.
</ResponseField>

<ResponseField name="input" type="object">
  Eco de la entrada. Los campos marcados `sensitive` en el contrato de entrada se redactan.
</ResponseField>

<ResponseField name="progress" type="object">
  Progreso. `done` / `total` los reporta la app; `status_text` es un texto de estado legible de la 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">
  Result dataset id.
</ResponseField>

<ResponseField name="partial" type="boolean">
  `true` cuando la ejecución tuvo éxito parcial o se canceló. Los registros producidos siguen siendo usables.
</ResponseField>

<ResponseField name="cancel_requested" type="boolean">
  `true` mientras se ha solicitado la cancelación y la ejecución aún se está deteniendo (parada cooperativa / recuperación de resultado parcial). Siempre `false` en estados terminales (normalizado en el servidor), así que los clientes no necesitan derivarlo de `state`.
</ResponseField>

<ResponseField name="triggered_by" type="string">
  Start channel.
</ResponseField>

<ResponseField name="upstream_ref" type="string">
  Id de la tarea upstream, si está presente.
</ResponseField>

<ResponseField name="created_at" type="string">
  Hora de inicio. También el ancla de filtros por rango temporal y de atribución de facturación.
</ResponseField>

<ResponseField name="started_at" type="string">
  Most recent execution start time. Re-stamped on retry.
</ResponseField>

<ResponseField name="first_started_at" type="string">
  Cuando un worker reclamó la ejecución por primera vez. A diferencia de `started_at`, no se reescribe en reintentos, así que es el ancla del tiempo en cola: en cola = `first_started_at - created_at`, y tiempo total de pared = `finished_at - first_started_at`. Derivar el tiempo en cola de `started_at` cuenta intentos anteriores como cola. `null` solo en ejecuciones nunca reclamadas.
</ResponseField>

<ResponseField name="finished_at" type="string">
  End time.
</ResponseField>

<ResponseField name="usage" type="object">
  Medición objetiva de uso: cuánto hizo la ejecución. Separada de la facturación; es lo que comparan las evaluaciones.

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

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

<ResponseField name="billing" type="object">
  Libro de facturación = uso × precios × reglas de facturación. `events[]` lista cada evento facturable con cantidad, precio unitario e importe; `total` es la suma; `charged` indica si se aplicó el cargo. Completo solo tras un estado terminal.

  <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">
  Objeto de error en fallo (`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[]">
  Primer lote de registros cuando la respuesta ya es terminal; si no, `null`.
</ResponseField>

<ResponseField name="warnings" type="object[]">
  Advertencias estructuradas, por ejemplo `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>

### Errores

| HTTP | `code`                   | `category`      | Descripción                                                                                                                       |
| ---- | ------------------------ | --------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| 401  | `unauthorized`           | `forbidden`     | API key ausente o no válida.                                                                                                      |
| 400  | `invalid-input`          | `invalid_input` | El cuerpo no satisface el contrato de entrada de la app. `details[]` lista cada ruta de campo y el motivo.                        |
| 404  | `app-not-found`          | `not_found`     | La app no existe, se renombró o es invisible para la credencial actual (privada / fuera del alcance de compartición).             |
| 403  | `app-not-accepting-runs` | `forbidden`     | La app está en mantenimiento y no acepta ejecuciones nuevas. `message` puede incluir una nota del publicador.                     |
| 402  | `balance-negative`       | `forbidden`     | El saldo del monedero es negativo; se bloquean ejecuciones nuevas. Recarga y reintenta la misma petición.                         |
| 503  | `billing-unavailable`    | `temporary`     | La cuenta tiene cargos pendientes y la facturación no puede verificar el saldo ahora. `retryable` es `true`; reintenta más tarde. |
| 422  | `version-yanked`         | `invalid_input` | La versión fijada fue yank por el publicador y ya no acepta ejecuciones nuevas.                                                   |

Las respuestas de error usan `{"error": {code, category, message, retryable}}`. Véase <a href="/docs/es/datahub/api/reference/introduction#errors">Errores</a>.

## Bibliotecas cliente

<CodeGroup>
  ```python Python theme={null}
  # Iniciar y esperar un estado terminal (el SDK sondea internamente)
  run = client.call("carol/probe-b", {"product": "p-9001"}, max_records=100)
  print(run["state"], run["billing"]["total"])

  # Solo iniciar; no esperar
  run = client.run("carol/probe-b", {"product": "p-9001"}, wait=0)
  run_id = run["run_id"]
  ```

  ```js JavaScript theme={null}
  // Iniciar y esperar un estado terminal (el SDK sondea internamente; timeout en ms)
  const run = await client.call("carol/probe-b", { product: "p-9001" }, { maxRecords: 100, timeout: 120_000 });
  console.log(run.state, run.billing.total);

  // Solo iniciar; no esperar
  const started = await client.run("carol/probe-b", { product: "p-9001" }, { wait: 0 });
  const runId = started.run_id;
  ```
</CodeGroup>

## Notas

* Vocabulario de estados: `PENDING`, `QUEUED`, `RUNNING`, `SUCCEEDED`, `PARTIALLY_SUCCEEDED`, `FAILED`, `CANCELLED`, `EXPIRED`. Los cinco últimos son terminales.
* No inicies una segunda ejecución para el mismo objetivo solo porque un wait agotó el tiempo. Primero sondea el `run_id` existente.
* Las ejecuciones fallidas no se facturan. El éxito parcial y la cancelación solo facturan los registros producidos.
