Sim job status
View as MarkdownPoll 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
The following fields are included in a successful response. View in full in API Reference.
The status of the job. Terminal values: completed, failed, cancelled, timed_out.
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_cancelledinternalHuman-readable explanation paired with errorCode. Null when the job is not terminal or no additional detail is available.
SimC process exit code. Null when the job is not terminal.
When this job entered the queue. For a retried job, this reflects the current attempt, not the original submission.
Estimated seconds until this job begins running. Updated on each poll; not a guarantee. Null when no estimate is available.
When this estimate was last computed.
Estimated completion percentage (0–100). Null when not yet running.
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.
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.
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.
The log line text.
Epoch milliseconds (UTC) when this line was captured.
When this job began executing. Null when the job has not yet started.
When this status was last updated.
True if this job was retried automatically because of a platform error. The response reflects the latest retry attempt.
#Polling
- Stop polling once
statusis terminal (completed,failed,cancelled,timed_out) and fetch the final result fromGET /v1/simc/jobs/{jobId}/result. - Aggressive polling can hit your read rate limit. Responses include
X-RateLimit-RemainingandX-RateLimit-Resetheaders. - 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}`)
}