---
title: Workers | Runware Docs
url: https://runware.ai/docs/serverless/api-workers
description: Inspect the workers currently serving an application.
relatedDocuments:
  - https://runware.ai/docs/serverless/api
  - https://runware.ai/docs/serverless/compute-and-scaling
  - https://runware.ai/docs/serverless/monitoring
---
## Introduction

A worker is one running copy of your app, and **it serves one task at a time**, so the number of workers is the whole of your throughput.

The states these routes report are **where a cold start becomes visible**: a worker waiting for capacity is not the same as one whose pod came up and never became ready. [Monitoring](https://runware.ai/docs/serverless/monitoring) covers what each state means and which one to act on.

## List workers

GETapi.serverless.runware.ai/v1/apps/{appId}/workers

Returns a newest-first page of workers observed for the app (including terminal `stopped` rows until purged). Omitted `versionId` scopes the page to the app's `activeVersionId`. An app with no active version therefore answers an empty default page, not that it has no workers, only that none are pinned. Optional `state`, `status` and `q` narrow the page further. A cursor must be replayed under the same filters it was issued with.

### [Request](#list-workers-request)

Path

### [appId](#list-workers-path-appid)

- **Type**: `string`
- **Required**: true
- **Min**: `6`
- **Max**: `30`

Immutable app identifier, unique among the authenticated organization's live apps.

Query

### [limit](#list-workers-query-limit)

- **Type**: `integer`
- **Format**: `int32`
- **Min**: `1`
- **Max**: `100`
- **Default**: `20`

Maximum number of items to return.

### [cursor](#list-workers-query-cursor)

- **Type**: `string`

Opaque pagination cursor returned by a previous call, as `nextCursor` or, on the operations that offer one, `prevCursor`.

### [versionId](#list-workers-query-versionid)

- **Type**: `string`
- **Min**: `1`

Scope the page to one version. The default is the app's `activeVersionId`. When that field is unset the default page is empty: the app has no active version, not that it has no workers. Send `all` (any case) to include every version. An empty value is refused. A cursor must be replayed under the same version scope it was issued with.

### [state](#list-workers-query-state)

- **Type**: `string`
- **Default**: `all`

Narrow the page by worker state. The default, `all`, keeps the terminal `stopped` rows in the page. `live` drops them. A `state` of `live` with a `status` of `stopped` is a contradiction and is refused, because an empty page would read as "this app has never run".

**Allowed values**: `live` `all`

### [status](#list-workers-query-status)

- **Type**: `string`

Worker lifecycle status. Also the type of `UsageEvent.eventType`, which records a ledger subset of these states (see that field: `busy` never appears there). `unhealthy` means the pod exists but failed to become or stay ready, not an intentional drain or stop.

**Allowed values**: `pending` `pulling` `loading` `ready` `busy` `unhealthy` `draining` `stopping` `stopped`

### [q](#list-workers-query-q)

- **Type**: `string`
- **Min**: `1`
- **Max**: `100`

Case-insensitive literal substring match against `id`, `podName`, `nodeName` and `versionId`. A worker matching any field is returned within the selected version, state and status filters. Omit `q` to disable search. Cursors must retain the same search term, ignoring case.

### [Response](#list-workers-response)

### [nextCursor](#list-workers-response-nextcursor)

- **Type**: `string (nullable)`

Cursor for the next page. Null when there are no more items.

### [data](#list-workers-response-data)

- **Type**: `object[] (14 properties)`

#### [id](#list-workers-response-data-id)

- **Type**: `string (uuid)`
- **Required**: true
- **Format**: `UUID v4`

#### [appId](#list-workers-response-data-appid)

- **Type**: `string`
- **Required**: true
- **Min**: `6`
- **Max**: `30`

Immutable app identifier. Unique among the authenticated organization's live apps: it cannot be changed after creation, and it becomes available again once the app it named reaches `deleted`.

#### [versionId](#list-workers-response-data-versionid)

- **Type**: `string (uuid)`
- **Required**: true
- **Format**: `UUID v4`

#### [podName](#list-workers-response-data-podname)

- **Type**: `string`
- **Required**: true

#### [nodeName](#list-workers-response-data-nodename)

- **Type**: `string (nullable)`

Kubernetes node the pod is scheduled on. Omitted while still pending.

#### [status](#list-workers-response-data-status)

- **Type**: `string`
- **Required**: true

Worker lifecycle status. Also the type of `UsageEvent.eventType`, which records a ledger subset of these states (see that field: `busy` never appears there). `unhealthy` means the pod exists but failed to become or stay ready, not an intentional drain or stop.

**Possible values**: `pending` `pulling` `loading` `ready` `busy` `unhealthy` `draining` `stopping` `stopped`

#### [statusReason](#list-workers-response-data-statusreason)

- **Type**: `string (nullable)`

Kubernetes-facing reason for the current status when unhealthy or otherwise notable (e.g. `ImagePullBackOff`, `CrashLoopBackOff`). An `unhealthy` worker whose start failed and is being retried reports how its last run ended (`Error`, `OOMKilled`, `Completed`) until it becomes ready. A `pending` worker the cluster could not place reports `Unschedulable`, which means no node could give it the GPUs the app asks for. A worker the platform stopped (idle scale-down, a rollout, an app stop or delete, a credit drain) reports `stop-requested` rather than the exit status its process answered the stop with. `Error` on a stopped worker means its process exited with a non-zero code for some other cause, which includes a cluster disruption such as a node drain or a preemption.

#### [statusOccurredAt](#list-workers-response-data-statusoccurredat)

- **Type**: `string (date-time)`
- **Required**: true
- **Format**: `date-time`

When the worker entered its current status. For a `stopped` worker this is when it stopped, so it is also the end of the worker's uptime.

#### [gpuCount](#list-workers-response-data-gpucount)

- **Type**: `integer`
- **Required**: true
- **Format**: `int32`
- **Min**: `0`

GPUs attached to this worker at observation time. Zero means the snapshot is unknown: the worker is still unscheduled, or a terminal observation could not read a GPU pair. A known snapshot is `>= 1` and travels with `gpuType`. An empty pair is incomplete, not a CPU worker. CPU workloads are not supported.

#### [gpuType](#list-workers-response-data-gputype)

- **Type**: `string (nullable)`
- **Min**: `1`
- **Max**: `64`

GPU catalogue code of the type the pod was observed on. It differs from `configuration.gpuType` when the worker was placed on `fallbackGpuType`, and also when the worker still runs an earlier configuration. Omitted until a GPU snapshot exists (the worker is still unscheduled, or the observation could not read a type). Not a CPU-worker marker. CPU workloads are not supported.

#### [gpuAvailability](#list-workers-response-data-gpuavailability)

- **Type**: `string (nullable)`

How the catalog currently provisions this worker's `gpuType`. Omitted when there is no `gpuType`, or when the catalog no longer holds the code. Unlike `gpuType` this is read now rather than snapshotted, so it tells you how that GPU is supplied today, not how it was supplied when the worker started.

**Possible values**: `available` `restricted` `custom`

#### [lastSeenAt](#list-workers-response-data-lastseenat)

- **Type**: `string (date-time) (nullable)`
- **Format**: `date-time`

Worker-reported liveness. Omitted until the worker reports. Stays absent until a heartbeat path exists (phase 2).

#### [createdAt](#list-workers-response-data-createdat)

- **Type**: `string (date-time)`
- **Required**: true
- **Format**: `date-time`

Pod creation time (`metadata.creationTimestamp`), not insert time. It is also the start of the worker's uptime.

#### [updatedAt](#list-workers-response-data-updatedat)

- **Type**: `string (date-time)`
- **Format**: `date-time`

### [Errors](#list-workers-errors)

<table><tbody><tr><td data-rw-cid-pz7tphoj="true">Status</td><td data-rw-cid-pz7tphoj="true">When</td></tr><tr><td data-rw-cid-pz7tphoj="true"><code data-rw-cid-pz7tphoj="">400</code></td><td data-rw-cid-pz7tphoj="true">The request was malformed and could not be parsed (e.g. invalid JSON). A well-formed request that fails validation returns <code class="component-Code">422</code> instead.</td></tr><tr><td data-rw-cid-pz7tphoj="true"><code data-rw-cid-pz7tphoj="">401</code></td><td data-rw-cid-pz7tphoj="true">Missing or invalid credentials</td></tr><tr><td data-rw-cid-pz7tphoj="true"><code data-rw-cid-pz7tphoj="">404</code></td><td data-rw-cid-pz7tphoj="true">Resource not found</td></tr><tr><td data-rw-cid-pz7tphoj="true"><code data-rw-cid-pz7tphoj="">422</code></td><td data-rw-cid-pz7tphoj="true">The request was well-formed but semantically invalid (e.g. a missing or out-of-range field). A request that could not be parsed returns <code class="component-Code">400</code>. The <code class="component-Code">errors</code> array carries one entry per offending field.</td></tr><tr><td data-rw-cid-pz7tphoj="true"><code data-rw-cid-pz7tphoj="">500</code></td><td data-rw-cid-pz7tphoj="true">Unexpected server error</td></tr><tr><td data-rw-cid-pz7tphoj="true"><code data-rw-cid-pz7tphoj="">503</code></td><td data-rw-cid-pz7tphoj="true">A required service is temporarily unavailable</td></tr></tbody></table>

## Get a worker

GETapi.serverless.runware.ai/v1/apps/{appId}/workers/{workerId}

Returns one worker by id within the app. The id is the Kubernetes pod UID recorded by the reconciler. A worker that belongs to another app (or tenant) is not found.

### [Request](#get-worker-request)

Path

### [appId](#get-worker-path-appid)

- **Type**: `string`
- **Required**: true
- **Min**: `6`
- **Max**: `30`

Immutable app identifier, unique among the authenticated organization's live apps.

### [workerId](#get-worker-path-workerid)

- **Type**: `string (uuid)`
- **Required**: true
- **Format**: `UUID v4`

Worker identifier, the Kubernetes pod UID recorded by the reconciler. This is the stable key to address a worker with. `podName` is the value to show a person.

### [Response](#get-worker-response)

### [id](#get-worker-response-id)

- **Type**: `string (uuid)`
- **Required**: true
- **Format**: `UUID v4`

### [appId](#get-worker-response-appid)

- **Type**: `string`
- **Required**: true
- **Min**: `6`
- **Max**: `30`

Immutable app identifier. Unique among the authenticated organization's live apps: it cannot be changed after creation, and it becomes available again once the app it named reaches `deleted`.

### [versionId](#get-worker-response-versionid)

- **Type**: `string (uuid)`
- **Required**: true
- **Format**: `UUID v4`

### [podName](#get-worker-response-podname)

- **Type**: `string`
- **Required**: true

### [nodeName](#get-worker-response-nodename)

- **Type**: `string (nullable)`

Kubernetes node the pod is scheduled on. Omitted while still pending.

### [status](#get-worker-response-status)

- **Type**: `string`
- **Required**: true

Worker lifecycle status. Also the type of `UsageEvent.eventType`, which records a ledger subset of these states (see that field: `busy` never appears there). `unhealthy` means the pod exists but failed to become or stay ready, not an intentional drain or stop.

**Possible values**: `pending` `pulling` `loading` `ready` `busy` `unhealthy` `draining` `stopping` `stopped`

### [statusReason](#get-worker-response-statusreason)

- **Type**: `string (nullable)`

Kubernetes-facing reason for the current status when unhealthy or otherwise notable (e.g. `ImagePullBackOff`, `CrashLoopBackOff`). An `unhealthy` worker whose start failed and is being retried reports how its last run ended (`Error`, `OOMKilled`, `Completed`) until it becomes ready. A `pending` worker the cluster could not place reports `Unschedulable`, which means no node could give it the GPUs the app asks for. A worker the platform stopped (idle scale-down, a rollout, an app stop or delete, a credit drain) reports `stop-requested` rather than the exit status its process answered the stop with. `Error` on a stopped worker means its process exited with a non-zero code for some other cause, which includes a cluster disruption such as a node drain or a preemption.

### [statusOccurredAt](#get-worker-response-statusoccurredat)

- **Type**: `string (date-time)`
- **Required**: true
- **Format**: `date-time`

When the worker entered its current status. For a `stopped` worker this is when it stopped, so it is also the end of the worker's uptime.

### [gpuCount](#get-worker-response-gpucount)

- **Type**: `integer`
- **Required**: true
- **Format**: `int32`
- **Min**: `0`

GPUs attached to this worker at observation time. Zero means the snapshot is unknown: the worker is still unscheduled, or a terminal observation could not read a GPU pair. A known snapshot is `>= 1` and travels with `gpuType`. An empty pair is incomplete, not a CPU worker. CPU workloads are not supported.

### [gpuType](#get-worker-response-gputype)

- **Type**: `string (nullable)`
- **Min**: `1`
- **Max**: `64`

GPU catalogue code of the type the pod was observed on. It differs from `configuration.gpuType` when the worker was placed on `fallbackGpuType`, and also when the worker still runs an earlier configuration. Omitted until a GPU snapshot exists (the worker is still unscheduled, or the observation could not read a type). Not a CPU-worker marker. CPU workloads are not supported.

### [gpuAvailability](#get-worker-response-gpuavailability)

- **Type**: `string (nullable)`

How the catalog currently provisions this worker's `gpuType`. Omitted when there is no `gpuType`, or when the catalog no longer holds the code. Unlike `gpuType` this is read now rather than snapshotted, so it tells you how that GPU is supplied today, not how it was supplied when the worker started.

**Possible values**: `available` `restricted` `custom`

### [lastSeenAt](#get-worker-response-lastseenat)

- **Type**: `string (date-time) (nullable)`
- **Format**: `date-time`

Worker-reported liveness. Omitted until the worker reports. Stays absent until a heartbeat path exists (phase 2).

### [createdAt](#get-worker-response-createdat)

- **Type**: `string (date-time)`
- **Required**: true
- **Format**: `date-time`

Pod creation time (`metadata.creationTimestamp`), not insert time. It is also the start of the worker's uptime.

### [updatedAt](#get-worker-response-updatedat)

- **Type**: `string (date-time)`
- **Format**: `date-time`

### [Errors](#get-worker-errors)

<table><tbody><tr><td data-rw-cid-pz7tphoj="true">Status</td><td data-rw-cid-pz7tphoj="true">When</td></tr><tr><td data-rw-cid-pz7tphoj="true"><code data-rw-cid-pz7tphoj="">400</code></td><td data-rw-cid-pz7tphoj="true">The request was malformed and could not be parsed (e.g. invalid JSON). A well-formed request that fails validation returns <code class="component-Code">422</code> instead.</td></tr><tr><td data-rw-cid-pz7tphoj="true"><code data-rw-cid-pz7tphoj="">401</code></td><td data-rw-cid-pz7tphoj="true">Missing or invalid credentials</td></tr><tr><td data-rw-cid-pz7tphoj="true"><code data-rw-cid-pz7tphoj="">404</code></td><td data-rw-cid-pz7tphoj="true">Resource not found</td></tr><tr><td data-rw-cid-pz7tphoj="true"><code data-rw-cid-pz7tphoj="">422</code></td><td data-rw-cid-pz7tphoj="true">The request was well-formed but semantically invalid (e.g. a missing or out-of-range field). A request that could not be parsed returns <code class="component-Code">400</code>. The <code class="component-Code">errors</code> array carries one entry per offending field.</td></tr><tr><td data-rw-cid-pz7tphoj="true"><code data-rw-cid-pz7tphoj="">500</code></td><td data-rw-cid-pz7tphoj="true">Unexpected server error</td></tr><tr><td data-rw-cid-pz7tphoj="true"><code data-rw-cid-pz7tphoj="">503</code></td><td data-rw-cid-pz7tphoj="true">A required service is temporarily unavailable</td></tr></tbody></table>