---
title: "Retrieve a job"
description: "Get the current status and full payload of a single conversion job."
---

import { Tabs, TabItem, Aside } from '@astrojs/starlight/components';

Fetch the current state of a single conversion job. This is what you poll after submitting.

| | |
|---|---|
| **Method** | `GET` |
| **Path**   | `/api/conversions/:id` |

The `:id` is the value returned in `data.id` from any conversion submission ([3D](/conversions/3d-to-simready), [2D](/conversions/2d-to-simready), [Text](/conversions/text-to-simready), [Simulate](/conversions/simulate)).

The endpoint also synchronises the latest status from the upstream pipeline before returning, so you always get a fresh `progress`, `stage`, and `status`.

## Examples

<Tabs syncKey="lang">
  <TabItem label="curl">
  ```bash
  curl https://api.rigyd.com/api/conversions/abc123... \
    -H "Authorization: Bearer rgyd_live_..."
  ```
  </TabItem>
  <TabItem label="JavaScript">
  ```js
  // Simple polling loop
  async function waitFor(jobId) {
    while (true) {
      const res = await fetch(`https://api.rigyd.com/api/conversions/${jobId}`, {
        headers: { Authorization: `Bearer ${process.env.RIGYD_API_KEY}` },
      });
      const { data } = await res.json();
      if (data.status === 'completed') return data;
      if (data.status === 'failed') throw new Error(data.error || 'failed');
      await new Promise((r) => setTimeout(r, 3000));
    }
  }
  ```
  </TabItem>
  <TabItem label="Python">
  ```python
  import os, time, requests

  def wait_for(job_id):
      headers = {"Authorization": f"Bearer {os.environ['RIGYD_API_KEY']}"}
      while True:
          data = requests.get(
              f"https://api.rigyd.com/api/conversions/{job_id}",
              headers=headers,
          ).json()["data"]
          if data["status"] == "completed":
              return data
          if data["status"] == "failed":
              raise RuntimeError(data.get("error") or "failed")
          time.sleep(3)
  ```
  </TabItem>
</Tabs>

## Response

```json
{
  "data": {
    "id": "abc123...",
    "physiq_job_id": "phy_...",
    "status": "completed",
    "filename": "toolbox.glb",
    "file_size_bytes": 1245678,
    "stage": "export",
    "progress": 100,
    "error": null,
    "timing": {
      "queued_at": "2026-05-06T12:00:01.000Z",
      "started_at": "2026-05-06T12:00:05.000Z",
      "completed_at": "2026-05-06T12:01:32.000Z"
    },
    "parameters": { "target_triangle_count": 50000 },
    "report": { /* validation + pipeline metadata */ },
    "job_type": "glb_to_simready",
    "credits_charged": 1,
    "input": {
      "model": { "url": "https://assets.rigyd.com/...", "name": "toolbox.glb" },
      "images": [],
      "metadata": null
    },
    "preprocess": {
      "status": "skipped",
      "steps": null,
      "started_at": null,
      "completed_at": null,
      "error": null,
      "input_stats": null,
      "telemetry": null,
      "intermediate_glb": null
    },
    "output": {
      "model": { "url": "https://assets.rigyd.com/.../toolbox.usd", "name": "toolbox.usd", "size": 982341 },
      "textures": [
        { "url": "https://assets.rigyd.com/.../diffuse.png", "name": "diffuse.png" }
      ],
      "mjcf_package": { "url": "...", "name": "toolbox-mjcf.zip", "size": 1234 },
      "sim_video": null,
      "sim_gif": null,
      "sim_log": null
    },
    "source_job": null,
    "simulations": [],
    "createdAt": "2026-05-06T12:00:00.000Z",
    "updatedAt": "2026-05-06T12:01:32.000Z"
  }
}
```

See [Job lifecycle](/reference/job-lifecycle) for the full status enum, the meaning of each field, and how `preprocess` relates to non-GLB inputs.

<Aside type="tip">
  **Polling cadence**: every 3-5 seconds is plenty. Most jobs finish under two minutes. We do not currently rate-limit polling but you should still back off when not actively waiting on a result.
</Aside>

## When the job is done

Continue to [Download result](/jobs/download).