> For the complete documentation index, see [llms.txt](https://docs.powerpointgeneratorapi.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.powerpointgeneratorapi.com/generations/post-generator-create-async.md).

# POST /generator/create-async

Use this endpoint to create a presentation from a .pptx template file and JSON data **without waiting for the file on the same connection**. The API accepts your request, returns a job ID immediately, and builds the presentation in the background. You then poll `jobs/{jobId}/status` until the job finishes and download the result from the link it returns.

{% hint style="info" %}
**Synchronous or asynchronous?**

Both endpoints take exactly the same `files` and `jsonData` payload, use the same [token](https://docs.powerpointgeneratorapi.com/api-reference/token-create), and count towards the same [usage allowance](https://docs.powerpointgeneratorapi.com/api-reference/usage). If a synchronous call is timing out, moving to the asynchronous endpoint is a drop-in change on the request side — you only add the polling step.
{% endhint %}

|                      | <p><a href="/generations/post-generator-create.md"><strong><code>generator/create</code></strong></a> </p><p><strong>(v1.0)</strong></p> | <p><strong><code>generator/create-async</code></strong></p><p><strong>(v2.0)</strong></p> |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| Style                | Synchronous — one request                                                                                                                | Asynchronous — submit, then poll                                                          |
| Response             | The `.pptx` file as a binary stream                                                                                                      | JSON containing a job ID                                                                  |
| Connection held open | For the whole generation                                                                                                                 | Only long enough to upload the template and JSON                                          |
| Best for             | Small and medium decks, quick interactive calls                                                                                          | Large decks — many slides, large tables, charts and images                                |
| Risk                 | Client, proxy or gateway timeouts on long jobs                                                                                           | None — generation time is decoupled from the request                                      |

{% stepper %}
{% step %}

## Submit the job

### generator/create-async

<mark style="color:green;">`POST`</mark> `https://gen.powerpointgeneratorapi.com/v2.0/generator/create-async`

#### Headers

| Name                                            | Type   | Description                 |
| ----------------------------------------------- | ------ | --------------------------- |
| Content-Type<mark style="color:red;">\*</mark>  | String | multipart/form-data         |
| Authorization<mark style="color:red;">\*</mark> | String | Bearer authentication token |

#### Request Body

| Name                                       | Type                   | Description                                  |
| ------------------------------------------ | ---------------------- | -------------------------------------------- |
| files<mark style="color:red;">\*</mark>    | File or array of files | .pptx template file(s), plus any image files |
| jsonData<mark style="color:red;">\*</mark> | String                 | JSON data                                    |

{% tabs %}
{% tab title="202: Accepted" %}
The job has been queued. Keep the `job_id` — you need it to check the status and collect the file.

```json
{
  "job_id": "3f9c1a72-58d4-4f0e-9a7b-2c6d0b1e84af",
  "status": "QUEUED"
}
```

A successful submission may be returned as either `200 OK` or `202 Accepted`. Treat both as success.
{% endtab %}

{% tab title="400: Bad Request" %}
Returns a JSON error object describing what was invalid.

```json
{
    "statusCode": 400,
    "message": "Invalid input parameters",
    "details": "{more information}",
}
```

{% endtab %}

{% tab title="401: Unauthorized" %}

```json
{
  "statusCode": 401,
  "message": "Unauthorized",
  "detail": "The bearer token is missing, expired, or invalid."
}
```

{% endtab %}

{% tab title="429: Too Many Requests" %}

```json
{
  "statusCode": 429,
  "message": "Too Many Requests - usage or rate limit exceeded",
  "detail": "Too many requests: monthly slide limit exceeded."
}
```

{% endtab %}

{% tab title="500: Internal Server Error" %}

```json
{
  "statusCode": 500,
  "title": "Error",
  "detail": "An error occurred while queuing the presentation. See server logs for details."
}
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
A success response here means the job was **accepted**, not that the presentation was built successfully. Problems with your template or JSON payload surface later, on the status endpoint, as a `FAILED` job.
{% endhint %}
{% endstep %}

{% step %}

## Check the job status

### jobs/{jobId}/status

<mark style="color:green;">`GET`</mark> `https://gen.powerpointgeneratorapi.com/v2.0/jobs/{jobId}/status`

#### Path Parameters

| Name                                    | Type   | Description                                       |
| --------------------------------------- | ------ | ------------------------------------------------- |
| jobId<mark style="color:red;">\*</mark> | String | The `job_id` returned by `generator/create-async` |

#### Headers

| Name                                            | Type   | Description                 |
| ----------------------------------------------- | ------ | --------------------------- |
| Authorization<mark style="color:red;">\*</mark> | String | Bearer authentication token |

{% tabs %}
{% tab title="200: OK" %}
A job moves through `QUEUED` → `PROCESSING` → `SUCCEEDED` or `FAILED`.

| Status       | Meaning                                                        |
| ------------ | -------------------------------------------------------------- |
| `QUEUED`     | Accepted and waiting to be picked up. Keep polling.            |
| `PROCESSING` | The presentation is being generated. Keep polling.             |
| `SUCCEEDED`  | The presentation is ready to download. Stop polling.           |
| `FAILED`     | Generation failed. `error_message` explains why. Stop polling. |

**While the job is running**

```json
{
  "status": "PROCESSING",
  "error_message": null,
  "file_url": null
}
```

**When the job has succeeded**

```json
{
  "status": "SUCCEEDED",
  "error_message": null,
  "file_url": "https://pptx-gen-async.s3.eu-west-1.amazonaws.com/jobs/3f9c1a72-58d4-4f0e-9a7b-2c6d0b1e84af/result/quick_start_example.pptx?X-Amz-Expires=900&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...&X-Amz-Date=20260829T131247Z&X-Amz-SignedHeaders=host&X-Amz-Signature=..."
}
```

Download the presentation from `file_url`. It is a pre-signed link generated by the API — use it exactly as returned rather than constructing it yourself. The file is named after the `resultFileName` in your `jsonData`.

**When the job has failed**

```json
{
  "status": "FAILED",
  "error_message": "{reason the presentation could not be generated}",
  "file_url": null
}
```

{% endtab %}

{% tab title="401: Unauthorized" %}

```json
{
  "statusCode": 401,
  "message": "Unauthorized",
  "detail": "The bearer token is missing, expired, or invalid."
}
```

{% endtab %}

{% tab title="404: Not Found" %}

```json
{
  "statusCode": 404,
  "message": "Not Found",
  "detail": "No job exists with the supplied jobId, or the job has expired."
}
```

{% endtab %}

{% tab title="500: Internal Server Error" %}

```json
{
  "statusCode": 500,
  "title": "Error",
  "detail": "An error occurred while retrieving the job status. See server logs for details."
}
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
`file_url` is a pre-signed link that expires **15 minutes** after the status response is issued. Download the file promptly; if the link has expired, call the status endpoint again to get a fresh one.

Because the link is already signed, do **not** send your `Authorization` header with the download request — supplying two authentication mechanisms causes the download to be rejected.
{% endhint %}

{% hint style="info" %}
Poll every few seconds rather than in a tight loop — every 2 to 5 seconds is plenty. Always cap your polling with an overall timeout so a stuck job cannot block your application indefinitely.
{% endhint %}
{% endstep %}
{% endstepper %}

## Example request

Dummy PowerPoint template used in examples below:

{% file src="/files/3pxG0xHbIXVqbWKmgasX" %}

{% tabs %}
{% tab title="cURL" %}

```bash
# 1. Submit the job
curl --location --request POST 'https://gen.powerpointgeneratorapi.com/v2.0/generator/create-async' \
  -H 'Authorization: Bearer {add_your_token_here}' \
  -F 'files=@title_slide_template.pptx' \
  -F 'jsonData={"presentation":{"template":"title_slide_template.pptx","export_version":"Pptx2019","resultFileName":"quick_start_example","slides":[{"type":"slide","slide_index":0,"shapes":[{"name":"Title 1","content":"Your generated PowerPoint presentation"},{"name":"Subtitle 2","content":"Create, fill and manage PowerPoint documents through simple API requests."}]}]}}'

# {"job_id":"3f9c1a72-58d4-4f0e-9a7b-2c6d0b1e84af","status":"QUEUED"}

# 2. Poll the status until it reports SUCCEEDED
curl --location 'https://gen.powerpointgeneratorapi.com/v2.0/jobs/3f9c1a72-58d4-4f0e-9a7b-2c6d0b1e84af/status' \
  -H 'Authorization: Bearer {add_your_token_here}'

# {"status":"SUCCEEDED","error_message":null,"file_url":"https://pptx-gen-async.s3.eu-west-1.amazonaws.com/..."}

# 3. Download the finished presentation from the file_url returned in step 2.
#    It is a pre-signed link, so do not send the Authorization header.
curl --location '{file_url_from_step_2}' --output generated.pptx
```

{% endtab %}

{% tab title="Python" %}

```python
import json
import time
import requests

TEMPLATE_PATH = './title_slide_template.pptx'   # where the file lives on disk
TEMPLATE_NAME = 'title_slide_template.pptx'     # upload name, same as jsonData "template"
API_TOKEN = 'eyJ...'                            # your bearer token

ASYNC_URL = 'https://gen.powerpointgeneratorapi.com/v2.0/generator/create-async'
STATUS_URL = 'https://gen.powerpointgeneratorapi.com/v2.0/jobs/{jobId}/status'
HEADERS = {'Authorization': f'Bearer {API_TOKEN}'}

payload = {
    'jsonData': json.dumps({
        "presentation": {
            "template": TEMPLATE_NAME,
            "export_version": "Pptx2019",
            "resultFileName": "quick_start_example",
            "slides": [
                {
                    "type": "slide",
                    "slide_index": 0,
                    "shapes": [
                        {"name": "Title 1", "content": "Your generated PowerPoint presentation"},
                        {"name": "Subtitle 2", "content": "Create, fill and manage PowerPoint documents through simple API requests."},
                    ],
                }
            ],
        }
    })
}


def submit_async_job():
    """Submit the job and return its job ID."""
    with open(TEMPLATE_PATH, 'rb') as f:
        files = [
            ('files', (TEMPLATE_NAME, f,
                       'application/vnd.openxmlformats-officedocument.presentationml.presentation'))
        ]
        response = requests.post(
            ASYNC_URL,
            data=payload,
            files=files,
            headers=HEADERS,
            timeout=500,
        )

    # A successful submission is returned as either 200 or 202
    if response.status_code not in (200, 202):
        print(f"Error submitting async job: {response.status_code} - {response.text}")
        return None

    result = response.json()
    print(f"Async job submitted. Job ID: {result['job_id']}, initial status: {result['status']}")
    return result['job_id']


def get_job_status(job_id):
    """Retrieve the current status of an async job."""
    response = requests.get(
        STATUS_URL.format(jobId=job_id),
        headers=HEADERS,
        timeout=30,
    )
    if response.status_code != 200:
        print(f"Error fetching job status: {response.status_code} - {response.text}")
        return None
    return response.json()


def wait_for_job(job_id, poll_seconds=5, timeout_seconds=1800):
    """Poll until the job finishes, or give up after timeout_seconds."""
    deadline = time.time() + timeout_seconds

    while time.time() < deadline:
        job = get_job_status(job_id)
        if job is None:
            return None

        status = job.get("status")
        print(f"Job {job_id}: {status}")

        if status == "SUCCEEDED":
            return job
        if status == "FAILED":
            print(f"Generation failed: {job.get('error_message')}")
            return None

        time.sleep(poll_seconds)

    print(f"Timed out after {timeout_seconds}s waiting for job {job_id}")
    return None


job_id = submit_async_job()

if job_id:
    job = wait_for_job(job_id)
    if job:
        # file_url is pre-signed - do not send the Authorization header.
        # Stream the response so large presentations are not held in memory.
        with requests.get(job["file_url"], stream=True, timeout=360) as download:
            download.raise_for_status()
            with open("./generated.pptx", "wb") as out:
                for chunk in download.iter_content(chunk_size=8192):
                    out.write(chunk)
        print("saved generated.pptx")
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const TEMPLATE_NAME = "title_slide_template.pptx"; // upload name + JSON "template" (must match)
const API_TOKEN = "add_your_token_here";

const ASYNC_URL = "https://gen.powerpointgeneratorapi.com/v2.0/generator/create-async";
const STATUS_URL = (jobId) => `https://gen.powerpointgeneratorapi.com/v2.0/jobs/${jobId}/status`;
const HEADERS = { Authorization: `Bearer ${API_TOKEN}` };

const jsonData = {
  presentation: {
    template: TEMPLATE_NAME,
    export_version: "Pptx2019",
    resultFileName: "quick_start_example",
    slides: [
      {
        type: "slide",
        slide_index: 0,
        shapes: [
          { name: "Title 1", content: "Your generated PowerPoint presentation" },
          { name: "Subtitle 2", content: "Create, fill and manage PowerPoint documents through simple API requests." },
        ],
      },
    ],
  },
};

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

// templateFile is a File/Blob, e.g. from <input type="file" id="template">:
// const templateFile = document.getElementById("template").files[0];

async function submitAsyncJob(templateFile) {
  const formData = new FormData();
  formData.append("jsonData", JSON.stringify(jsonData));
  formData.append("files", templateFile, TEMPLATE_NAME);

  const response = await fetch(ASYNC_URL, {
    method: "POST",
    headers: HEADERS,
    body: formData,
  });

  // A successful submission is returned as either 200 or 202
  if (response.status !== 200 && response.status !== 202) {
    console.error(response.status, await response.text());
    return null;
  }

  const result = await response.json();
  console.log(`Async job submitted. Job ID: ${result.job_id}, initial status: ${result.status}`);
  return result.job_id;
}

async function waitForJob(jobId, pollMs = 5000, timeoutMs = 1800000) {
  const deadline = Date.now() + timeoutMs;

  while (Date.now() < deadline) {
    const response = await fetch(STATUS_URL(jobId), { headers: HEADERS });
    if (!response.ok) {
      console.error(response.status, await response.text());
      return null;
    }

    const job = await response.json();
    console.log(`Job ${jobId}: ${job.status}`);

    if (job.status === "SUCCEEDED") return job;
    if (job.status === "FAILED") {
      console.error(`Generation failed: ${job.error_message}`);
      return null;
    }

    await sleep(pollMs);
  }

  console.error(`Timed out waiting for job ${jobId}`);
  return null;
}

async function generate(templateFile) {
  const jobId = await submitAsyncJob(templateFile);
  if (!jobId) return;

  const job = await waitForJob(jobId);
  if (!job) return;

  // file_url is pre-signed - do not send the Authorization header
  const download = await fetch(job.file_url);
  const blob = await download.blob();
  const link = document.createElement("a");
  link.href = URL.createObjectURL(blob);
  link.download = "generated.pptx";
  link.click();
  URL.revokeObjectURL(link.href);
}
```

{% endtab %}

{% tab title="Postman" %}
{% stepper %}
{% step %}
Select the `Authorization` tab, enter your `Token`.
{% endstep %}

{% step %}
Select the `Body` tab, select `form-data`, add the two key-value parameters below:

| KEY           | VALUE                                             |
| ------------- | ------------------------------------------------- |
| `files`       | upload/ attach your `.pptx` file                  |
| `jsonData`    | copy and paste the data from your `.json` payload |
| {% endstep %} |                                                   |

{% step %}
`POST` to <https://gen.powerpointgeneratorapi.com/v2.0/generator/create-async> and copy the `job_id` from the response.
{% endstep %}

{% step %}
Open a new request, `GET` `https://gen.powerpointgeneratorapi.com/v2.0/jobs/{jobId}/status` with the same `Authorization` token, and hit `Send` every few seconds until `status` is `SUCCEEDED`.
{% endstep %}

{% step %}
Open a third request, `GET` the `file_url` from the status response with **no** `Authorization` header, then click the down arrow next to `Send` and select `Send and Download` to save the output as `.pptx`.
{% endstep %}
{% endstepper %}
{% endtab %}
{% endtabs %}
