---
title: "Simulate"
description: "Run a physics simulation against a completed conversion. Returns a video, GIF, and log. Free."
---

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

Run a physics simulation against a previously completed conversion. Useful for sanity-checking that mass, friction, and collision shapes look right before you import the asset into your simulator.

| | |
|---|---|
| **Method** | `POST` |
| **Path**   | `/api/conversions/:id/simulate` |
| **Content-type** | `application/json` |
| **Job type** | `simulate_usd` |
| **Credits** | 0 (free) |

The `:id` is the `id` of a completed source job (any of [3D](/conversions/3d-to-simready), [2D](/conversions/2d-to-simready), [Text](/conversions/text-to-simready) → SimReady). You cannot simulate a simulation.

## Request fields

| Field    | Type   | Required | Notes                                                       |
| -------- | ------ | -------- | ----------------------------------------------------------- |
| `scene`  | string | no       | `"demo"` (default) or `"drop"`. Picks the test scene.       |

## Examples

<Tabs syncKey="lang">
  <TabItem label="curl">
  ```bash
  curl -X POST https://api.rigyd.com/api/conversions/abc123.../simulate \
    -H "Authorization: Bearer rgyd_live_..." \
    -H "Content-Type: application/json" \
    -d '{"scene":"drop"}'
  ```
  </TabItem>
  <TabItem label="JavaScript">
  ```js
  const res = await fetch(
    `https://api.rigyd.com/api/conversions/${jobId}/simulate`,
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.RIGYD_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ scene: 'drop' }),
    },
  );
  const { data } = await res.json();
  ```
  </TabItem>
  <TabItem label="Python">
  ```python
  import os, requests

  res = requests.post(
      f"https://api.rigyd.com/api/conversions/{job_id}/simulate",
      headers={"Authorization": f"Bearer {os.environ['RIGYD_API_KEY']}"},
      json={"scene": "drop"},
  )
  sim = res.json()["data"]
  ```
  </TabItem>
</Tabs>

## Response — `202 Accepted`

```json
{
  "data": {
    "id": "sim_xyz...",
    "status": "queued",
    "filename": "toolbox.glb",
    "progress": 0,
    "job_type": "simulate_usd",
    "credits_charged": 0,
    "source_job_id": "abc123...",
    "createdAt": "2026-05-06T12:05:00.000Z"
  }
}
```

The simulation runs as its own job — poll [`GET /api/conversions/:id`](/jobs/retrieve) on the returned `id`. When `status` is `completed`, the response includes `output.sim_video`, `output.sim_gif`, and `output.sim_log` URLs.

<Aside type="note">
  Simulations also appear nested under the source job's `simulations[]` array when you fetch the parent.
</Aside>

## Errors specific to this endpoint

| Status | Body                                                     | When |
| ------ | -------------------------------------------------------- | ---- |
| `404`  | `{ "error": "Conversion job not found" }`                | Bad `id` or not your job |
| `409`  | `{ "error": "Source job must be completed before simulation" }` | Parent is still running / failed |
| `422`  | `{ "error": "Cannot simulate a simulation job" }`        | `:id` was already a `simulate_usd` job |
| `502`  | `{ "error": "Simulation service unavailable: ..." }`     | Upstream physics service is down |