﻿# Sim Job Status

Poll `GET /v1/simc/jobs/{jobId}/status` to check on a job in flight. The response covers estimated time to start, the SimC progress percent, and a tail of recent SimC log lines. Skip to the [examples](#examples) for code samples.

## Response

`GET /v1/simc/jobs/{jobId}/status`

The following fields are included in a successful response. View in full in [API Reference](/docs/api-reference#tag/simc/GET/v1/simc/jobs/{id}/status).

**`status`** _(enum)_

The status of the job. Terminal values: `completed`, `failed`, `cancelled`, `timed_out`.

`pending` | `queued` | `starting` | `running` | `completed` | `failed` | `cancelled` | `timed_out`

**`errorCode`** _(enum | null)_

Machine-readable error code for the terminal state. Null when the job is not terminal or completed successfully.

`execution_interrupted` | `execution_timeout` | `execution_failed` | `build_unavailable` | `simulation_error` | `queue_timeout` | `input_invalid` | `insufficient_credits` | `user_cancelled` | `internal`

**`statusReason`** _(string | null)_

Human-readable explanation paired with `errorCode`. Null when the job is not terminal or no additional detail is available.

**`simcExitCode`** _(string | null)_

SimC process exit code. Null when the job is not terminal.

**`queue.estimatedStartSeconds`** _(number | null)_

Estimated seconds until this job begins running. Updated on each poll; not a guarantee. Null when no estimate is available.

**`queue.estimatedStartUpdatedAt`** _(string | null)_

When this estimate was last computed.

**`progress.percent`** _(number | null)_

Estimated completion percentage (0–100). Null when not yet running.

**`progress.stage`** _(object | null)_

Stage progress detail while the job is running. Null before the job starts running and once it reaches a terminal state. Shape: `{ current, total, label, percent }`. Single-pass jobs report `{ current: 1, total: 1, label: "initial" }` while running. See the [API Reference](/docs/api-reference#tag/simc/GET/v1/simc/jobs/{id}/status) for full field details.

**`logEntries`** _(array | null)_

Recent log lines from SimC stdout and stderr while the job is running. Each entry is tagged with its stream (`source`) and capture time (`ts`). Full logs are available as downloadable artifacts once the job completes. Null when not running or not requested via `include=logEntries`.

**`logEntries[].source`** _(enum)_

Which stream the line came from. `stderr` lines are diagnostic output: SimC warnings, errors, and notes. `stdout` lines are sim output, progress markers, and results.

`stdout` | `stderr`

**`logEntries[].message`** _(string)_

The log line text.

**`logEntries[].ts`** _(number)_

Epoch milliseconds (UTC) when this line was captured.

**`startedAt`** _(string | null)_

When this job began executing. Null when the job has not yet started.

**`updatedAt`** _(string | null)_

When this status was last updated.

**`retried`** _(boolean)_

True if this job was retried automatically because of a platform error. The response reflects the latest retry attempt.

## Polling

- Stop polling once `status` is terminal (`completed`, `failed`, `cancelled`, `timed_out`) and fetch the final result from `GET /v1/simc/jobs/{jobId}/result`.
- Aggressive polling can hit your read rate limit. Responses include `X-RateLimit-Remaining` and `X-RateLimit-Reset` headers.
- To skip polling entirely, subscribe to terminal job events via webhooks at submit time. See [Webhooks](/docs/api-advanced/webhooks).

## Examples

### Job time in queue

This example function logs the `status`, `queue`, and `progress` of a job.

```javascript
const secretKey = process.env.SIMMIT_SECRET_KEY

async function printJobQueueStatus(jobId) {
  // Send GET request to status endpoint
  const response = await fetch(
    `https://api.simmit.com/v1/simc/jobs/${jobId}/status`,
    { headers: { Authorization: `Bearer ${secretKey}` } }
  )

  if (!response.ok) {
    throw new Error(`Status failed: ${response.statusText}`)
  }

  const { status, queue } = await response.json()

  if (status === 'queued') {
    console.log(`Queue time estimate: ${queue.estimatedStartSeconds}s`)
  } else {
    console.log('Job is not in queue')
  }
}

// Use a real job ID
await printJobQueueStatus('123')
```

### Job progress percent

This example function logs the `progress.percent`. The progress percent is an estimate, derived from the sim's running output.

```javascript
const secretKey = process.env.SIMMIT_SECRET_KEY

async function printJobProgress(jobId) {
  // Send GET request to status endpoint
  const response = await fetch(
    `https://api.simmit.com/v1/simc/jobs/${jobId}/status`,
    { headers: { Authorization: `Bearer ${secretKey}` } }
  )

  if (!response.ok) {
    throw new Error(`Status failed: ${response.statusText}`)
  }

  const { status, progress } = await response.json()

  if (status !== 'running') {
    console.log(`Job is ${status}`)
    return
  }

  if (progress.percent != null) {
    console.log(`Progress: ${progress.percent.toFixed(1)}%`)
  }
}

// Use a real job ID
await printJobProgress('123')
```

### Job SimC log tail

Pass `?include=logEntries` to the status endpoint to get the most recent
`stdout`/`stderr` lines while SimC is running. Each entry is tagged with its
stream (`source`) and capture time (`ts`). `logEntries` is `null` unless the
job is running and you requested it.

```javascript
const secretKey = process.env.SIMMIT_SECRET_KEY

async function printRecentLogs(jobId) {
  const response = await fetch(
    // Pass `?include=logEntries`
    `https://api.simmit.com/v1/simc/jobs/${jobId}/status?include=logEntries`,
    { headers: { Authorization: `Bearer ${secretKey}` } }
  )

  if (!response.ok) {
    throw new Error(`Status failed: ${response.statusText}`)
  }

  const { status, logEntries } = await response.json()

  if (status !== 'running') {
    console.log(`Job is ${status}`)
    return
  }

  for (const { source, message, ts } of logEntries ?? []) {
    const time = new Date(ts).toISOString()
    console.log(`[${time}] ${source}: ${message}`)
  }
}

// Use a real job ID
await printRecentLogs('123')
```

To actually _tail_, poll on an interval and track the last `ts` you've seen, filtering entries at or below it. Stop polling once `status` is terminal (`completed`, `failed`, `cancelled`, `timed_out`).

### Job is finished

A job is terminal once its `status` is `completed`, `failed`, `cancelled`, or `timed_out`. At that point, fetch the result from `GET /v1/simc/jobs/{jobId}/result`.

**Node.js**

```javascript
const secretKey = process.env.SIMMIT_SECRET_KEY

const TERMINAL_JOB_STATUSES = ['completed', 'failed', 'cancelled', 'timed_out']

async function printIsJobTerminal(jobId) {
  // Send GET request to status endpoint
  const response = await fetch(
    `https://api.simmit.com/v1/simc/jobs/${jobId}/status`,
    { headers: { Authorization: `Bearer ${secretKey}` } }
  )

  if (!response.ok) {
    throw new Error(`Status failed: ${response.statusText}`)
  }

  const { status } = await response.json()

  if (TERMINAL_JOB_STATUSES.includes(status)) {
    console.log('Job is terminal, time to fetch the result')
  } else {
    console.log('Job is not terminal')
  }
}

// Use a real job ID
await printIsJobTerminal('123')
```

### Job polling

This example function polls a job's `status` on a fixed interval, logging queue ETA or running progress on each tick, until the job is terminal. It then fetches the result.

```javascript
const secretKey = process.env.SIMMIT_SECRET_KEY

const TERMINAL_JOB_STATUSES = ['completed', 'failed', 'cancelled', 'timed_out']

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))

async function pollJob(jobId, { intervalMs = 2000 } = {}) {
  while (true) {
    const response = await fetch(
      `https://api.simmit.com/v1/simc/jobs/${jobId}/status`,
      { headers: { Authorization: `Bearer ${secretKey}` } }
    )

    if (!response.ok) {
      throw new Error(`Status failed: ${response.statusText}`)
    }

    const { status, queue, progress } = await response.json()

    if (TERMINAL_JOB_STATUSES.includes(status)) {
      console.log(`Job ${status}`)
      return status
    }

    if (status === 'queued' && queue?.estimatedStartSeconds != null) {
      console.log(`Queued (eta ~${queue.estimatedStartSeconds}s)`)
    } else if (status === 'running' && progress.percent != null) {
      console.log(`Running (${progress.percent.toFixed(1)}%)`)
    }

    await sleep(intervalMs)
  }
}

// Poll until the job is terminal, then fetch the result
const status = await pollJob('123')
if (status === 'completed') {
  const result = await fetch(`https://api.simmit.com/v1/simc/jobs/123/result`, {
    headers: { Authorization: `Bearer ${secretKey}` }
  }).then(r => r.json())
  console.log(`DPS: ${result.result.summary.mainActor.mean}`)
}
```

---

_HTML version: https://simmit.com/docs/learning/simc-job-status · Full docs index: https://simmit.com/llms.txt_
