Sim job status

View as Markdown

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

#statusenum

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

pendingqueuedstartingrunningcompletedfailedcancelledtimed_out
#errorCodeenum | null

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

execution_interruptedexecution_timeoutexecution_failedbuild_unavailablesimulation_errorqueue_timeoutinput_invalidinsufficient_creditsuser_cancelledinternal
#statusReasonstring | null

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

#simcExitCodestring | null

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

#queue.queuedAtstring

When this job entered the queue. For a retried job, this reflects the current attempt, not the original submission.

#queue.estimatedStartSecondsnumber | null

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

#queue.estimatedStartUpdatedAtstring | null

When this estimate was last computed.

#progress.percentnumber | null

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

#progress.stageobject | 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 for full field details.

#logEntriesarray | 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[].sourceenum

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.

stdoutstderr
#logEntries[].messagestring

The log line text.

#logEntries[].tsnumber

Epoch milliseconds (UTC) when this line was captured.

#startedAtstring | null

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

#updatedAtstring | null

When this status was last updated.

#retriedboolean

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.

#Examples

Job time in queue

This example function logs a queued job's queue.estimatedStartSeconds, the estimated time until it starts running.

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

Queue progress bar

Polling status lets you draw a progress bar for a queued job, computed as elapsed / (elapsed + estimatedStartSeconds) where elapsed is the time since queue.queuedAt. While the job waits, estimatedStartSeconds counts down at the same rate elapsed rises, so their sum (the estimated total wait) stays steady and the bar climbs smoothly between polls. The example clamps the percent so it never moves backwards (a live estimate can revise upward), restarts when an automatic retry re-enters the queue under a new queuedAt, and shows an indeterminate state when no estimate is available.

const secretKey = process.env.SIMMIT_SECRET_KEY

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

async function pollQueueProgress(jobId, { intervalMs = 5000 } = {}) {
  let lastPercent = 0 // clamp monotonic: a live ETA can revise upward
  let anchor = null // the queuedAt of the attempt we're tracking

  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 } = await response.json()

    // The bar only applies while queued.
    if (status !== 'queued' || !queue) {
      console.log(`Job is now ${status}`)
      return status
    }

    const { queuedAt, estimatedStartSeconds } = queue

    // A new queuedAt means a new attempt (e.g. a retry): restart the bar.
    if (queuedAt !== anchor) {
      anchor = queuedAt
      lastPercent = 0
    }

    // No estimate yet: show an indeterminate bar instead of NaN.
    if (estimatedStartSeconds == null) {
      console.log('Queued (estimating)')
      await sleep(intervalMs)
      continue
    }

    const elapsedSeconds = (Date.now() - new Date(queuedAt).getTime()) / 1000
    const estimatedTotalSeconds = elapsedSeconds + estimatedStartSeconds

    // A near-zero estimate (job about to start) can zero the denominator;
    // show a near-full bar instead of dividing by zero.
    const raw =
      estimatedTotalSeconds > 0
        ? (elapsedSeconds / estimatedTotalSeconds) * 100
        : 99

    // Hold under 100% until the job starts; never go backwards.
    const percent = Math.min(99, Math.max(lastPercent, raw))
    lastPercent = percent

    console.log(`Queued: ${percent.toFixed(0)}%`)
    await sleep(intervalMs)
  }
}

// Use a real job ID
await pollQueueProgress('123')

Job progress percent

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

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.

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.

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.

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

// Use a real job ID
const jobId = '123'

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