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

# Poll and download results

> Read existing content and tasks, check successful completion, and retrieve media download links.

Use [API key authentication](/api-reference/authentication) for all queries on
this page. Retain the content ID and task ID returned by the generation mutation.
Poll with reads, which do not submit another generation.

## Read an existing generation

```graphql GenerationResult theme={null}
query GenerationResult($contentId: ID!) {
  getUserByPrivateId {
    content(contentId: $contentId) {
      id
      task {
        id
        status
        displayErrorMessage
        progress {
          current
          total
          queued
          finishCode
        }
      }
      medias {
        id
        fileType
        title
      }
    }
  }
}
```

```json Variables theme={null}
{
  "contentId": "<CONTENT_ID>"
}
```

Use the account's nested `content` field for this flow. Media visibility is
checked for the authenticated account. A `null` content result provides no
successful-completion proof; inspect errors and the saved receipt before taking
another action.

## Interpret task status

| Status | Meaning / action |
| - | - |
| `onHold`, `preparing`, `preprocessing`, `running` | Not a successful final result. Continue bounded polling when appropriate. |
| `paused` | Processing is paused. Do not treat it as success or automatically submit again. |
| `finished` | Check `progress.finishCode`; success requires `0`. |
| `cancelled`, `error` | The task did not successfully complete. Inspect `displayErrorMessage` and the finish code. |

`current` and `total` describe progress; `queued` indicates a waiting running
task. A full progress counter alone does not prove success. `finishCode` can be
absent before completion. Common finish codes are `0` (success), `1` (failure),
`2` (pause), and `3` (cancel).

Use a bounded interval and waiting deadline chosen for your client. Reaching
that deadline does not cancel server-side work. Resume reading the same content
instead of repeating the creation mutation.

### Read a known task

```graphql TaskById theme={null}
query TaskById($taskId: ID!) {
  getUserByPrivateId {
    task(id: $taskId) {
      id
      resourceId
      status
      displayErrorMessage
      progress { current total queued finishCode }
    }
  }
}
```

```json Variables theme={null}
{
  "taskId": "<TASK_ID>"
}
```

For a content-generation task, `resourceId` is the content ID. Use the top-level
task from submission rather than an internal processing task.

## Obtain download URLs

After successful completion, request a download URL for an output media ID:

```graphql DownloadMedia theme={null}
query DownloadMedia($mediaIds: [ID!]!) {
  listDownloadableUrlForMedias(mediaIds: $mediaIds)
}
```

```json Variables theme={null}
{
  "mediaIds": ["<OUTPUT_MEDIA_ID>"]
}
```

The result is a list of URLs. Missing, inaccessible, or unavailable outputs can
be omitted. For an unambiguous ID-to-URL association, request one media ID at a
time; do not zip a multi-ID input with the returned array. An empty list means
there is no eligible URL in that response.

Links expire. Query the same media ID to get a fresh link; this does not create
new media. Watermark behavior can depend on the requesting account and output
owner. Inspect the downloaded media before using it.

## Reconcile your media history

When a submission receipt is missing, inspect account history rather than
automatically resubmitting:

```graphql PersonalMediaHistory theme={null}
query PersonalMediaHistory($page: MediaCursorPageInput!) {
  personalMedias(pageFetchingInfo: $page) {
    list {
      id
      contentId
      createdTime
      task { id status progress { finishCode } }
    }
    hasMore
    nextCursorId
  }
}
```

```json Variables theme={null}
{
  "page": { "pageSize": 20 }
}
```

Send `nextCursorId` as `page.previousId` for the next page and stop when
`hasMore` is false. Keep filters unchanged across pages. Optional
`filter: PersonalMediaFilter` accepts `modelId`, `mediaTypes`, and `fileTypes`.
Omitted `mediaTypes` uses the personal history's default media types; omitted
`fileTypes` allows all file types. An empty list matches no media. History can
help identify an existing operation, but an empty page
does not prove that an uncertain mutation was never accepted.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.