> ## Documentation Index
> Fetch the complete documentation index at: https://docs.percify.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Get a generation: GET /v1/generations/{id}

> GET /v1/generations/{id} returns a Percify generation's status, output URLs and credits. Add ?wait=45 to wait on the server until it finishes.

`GET https://api.percify.io/v3/playground/v1/generations/{id}` returns the current state of a generation you started with [`POST /v1/run`](/api-reference/generations/run). When `status` is `succeeded`, the files are in `output.urls`. Add `?wait=45` and Percify holds the request open until the run finishes or 45 seconds pass, so you need fewer calls.

```http theme={"system"}
GET https://api.percify.io/v3/playground/v1/generations/{id}?wait=45
Authorization: Bearer pk_live_…
```

## Parameters

<ParamField path="id" type="string" required>
  The generation id (a UUID) from `POST /v1/run`. You can only read generations from your own account.
</ParamField>

<ParamField query="wait" type="integer">
  Seconds to wait on the server for the run to finish, from 5 to 55. Values outside that range are clamped. Leave it out to get the current state immediately.
</ParamField>

## Response

<ResponseField name="data.id" type="string">
  The generation id.
</ResponseField>

<ResponseField name="data.modelId" type="string">
  The model that ran.
</ResponseField>

<ResponseField name="data.status" type="string">
  `processing`, `succeeded` or `failed`. See [statuses](/api-reference/async-jobs#generation-statuses).
</ResponseField>

<ResponseField name="data.output" type="object | null">
  Set when `status` is `succeeded`: `type` is `image`, `video` or `audio`, and `urls` is an array of file URLs on `cdn.percify.io`.
</ResponseField>

<ResponseField name="data.error" type="string | null">
  Why the run failed, when `status` is `failed`.
</ResponseField>

<ResponseField name="data.creditsSpent" type="integer">
  Credits charged when the run started. On a failed run the same amount was refunded to your balance.
</ResponseField>

<ResponseField name="data.createdAt" type="string">
  When the run started.
</ResponseField>

<ResponseField name="data.mediaDurationSec" type="number | null">
  Length in seconds of the input audio or video that a duration-billed model was priced on, such as lip-sync. `null` for other models.
</ResponseField>

<ResponseField name="data.stillRunning" type="boolean">
  Only present with `wait`: `true` when the wait ended before the run did. Call again.
</ResponseField>

`input` and `completedAt` also appear in the object and are `null` on this endpoint.

## Example: wait for the result

<CodeGroup>
  ```bash cURL theme={"system"}
  curl -s "https://api.percify.io/v3/playground/v1/generations/3f6c2a9e-8b1d-4c57-9a0e-2d4b7f1c6e83?wait=45" \
    -H "Authorization: Bearer $PERCIFY_API_TOKEN"
  ```

  ```javascript Node.js theme={"system"}
  const API = "https://api.percify.io/v3/playground/v1";
  const headers = { Authorization: `Bearer ${process.env.PERCIFY_API_TOKEN}` };

  async function waitForGeneration(id) {
    for (;;) {
      const res = await fetch(`${API}/generations/${id}?wait=45`, { headers });
      const { data } = await res.json();
      if (data.status === "succeeded") return data.output.urls;
      if (data.status === "failed") throw new Error(data.error);
    }
  }

  console.log(await waitForGeneration("3f6c2a9e-8b1d-4c57-9a0e-2d4b7f1c6e83"));
  ```

  ```python Python theme={"system"}
  import os, requests

  API = "https://api.percify.io/v3/playground/v1"
  HEADERS = {"Authorization": f"Bearer {os.environ['PERCIFY_API_TOKEN']}"}

  def wait_for_generation(gen_id):
      while True:
          data = requests.get(f"{API}/generations/{gen_id}", params={"wait": 45},
                              headers=HEADERS, timeout=70).json()["data"]
          if data["status"] == "succeeded":
              return data["output"]["urls"]
          if data["status"] == "failed":
              raise RuntimeError(data["error"])

  print(wait_for_generation("3f6c2a9e-8b1d-4c57-9a0e-2d4b7f1c6e83"))
  ```
</CodeGroup>

```json Succeeded theme={"system"}
{
  "success": true,
  "data": {
    "id": "3f6c2a9e-8b1d-4c57-9a0e-2d4b7f1c6e83",
    "modelId": "gpt-image-2",
    "status": "succeeded",
    "input": null,
    "output": {
      "type": "image",
      "urls": ["https://cdn.percify.io/media-assets/playground/3f6c2a9e-8b1d-4c57-9a0e-2d4b7f1c6e83-0"]
    },
    "error": null,
    "creditsSpent": 4,
    "createdAt": "2026-09-16T09:41:07.512Z",
    "completedAt": null,
    "mediaDurationSec": null
  }
}
```

```json Still running after the wait theme={"system"}
{
  "success": true,
  "data": {
    "id": "3f6c2a9e-8b1d-4c57-9a0e-2d4b7f1c6e83",
    "modelId": "gpt-image-2",
    "status": "processing",
    "input": null,
    "output": null,
    "error": null,
    "creditsSpent": 4,
    "createdAt": "2026-09-16T09:41:07.512Z",
    "completedAt": null,
    "mediaDurationSec": null,
    "stillRunning": true
  }
}
```

## Errors

| Status | Message starts with                    | Cause                                                               |
| ------ | -------------------------------------- | ------------------------------------------------------------------- |
| `400`  | `Invalid generation id`                | The id is not a UUID. Use the `id` from `POST /v1/run`.             |
| `401`  | `Invalid, expired, or revoked API key` | See [Authentication](/percify/api-auth).                            |
| `404`  | `Generation not found`                 | No generation with this id exists in the account that owns the key. |
| `429`  | `API key rate limit exceeded`          | More than 60 requests a minute on this key.                         |

## Tips

* Save the files you need. `output.urls` point to `cdn.percify.io`, and your own storage is the copy you control.
* Set your HTTP client timeout above the `wait` value, for example 70 seconds for `wait=45`.
* Every poll counts toward the 60 requests a minute per key. `wait=45` keeps a single job to about one request a minute.
* For long lip-sync videos, a [webhook](/guides/webhooks) saves you from polling at all.

## Related

<CardGroup cols={2}>
  <Card title="Async jobs and polling" href="/api-reference/async-jobs">
    Statuses, time limits and refunds.
  </Card>

  <Card title="Start a generation" href="/api-reference/generations/run">
    The call that returns the id.
  </Card>

  <Card title="Webhooks" href="/guides/webhooks">
    Get the finished result pushed to you.
  </Card>

  <Card title="Errors and rate limits" href="/api-reference/errors-and-limits">
    Every status code.
  </Card>
</CardGroup>
