> For the complete documentation index, see [llms.txt](https://docs.perceptis.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.perceptis.ai/perceptis-api-v1/quickstart-single-slide.md).

# Quickstart: single slide

This quickstart starts a single-slide generation job, polls until it finishes, and reads the download URL from the completed status response.

## Set environment variables

Set `PERCEPTIS_API_BASE_URL` to the API origin only. Do not include `/api`; the examples append `/api/v1/...`.

```bash
export PERCEPTIS_API_BASE_URL="https://app.perceptis.ai"
export PERCEPTIS_API_KEY="sk-live-per-..."
```

Install the Python HTTP client used by this quickstart:

```bash
python3 -m pip install requests
```

## Start generation

Use this as `quickstart_single_slide.py`:

```python
import os
import time
import uuid

import requests


base_url = os.environ["PERCEPTIS_API_BASE_URL"].rstrip("/")
api_key = os.environ["PERCEPTIS_API_KEY"]

response = requests.post(
    f"{base_url}/api/v1/generate",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "prompt": "One slide summarizing Q3 revenue drivers",
        "output_type": "single_slide",
    },
    timeout=60,
)
response.raise_for_status()

job_id = response.json()["job_id"]
print(f"Job ID: {job_id}")


def poll_status(job_id, timeout_sec=300):
    deadline = time.monotonic() + timeout_sec

    while time.monotonic() < deadline:
        status_response = requests.get(
            f"{base_url}/api/v1/status/{job_id}",
            headers={"Authorization": f"Bearer {api_key}"},
            timeout=60,
        )
        status_response.raise_for_status()

        body = status_response.json()
        if body["status"] in {"completed", "failed"}:
            return body

        time.sleep(3)

    raise TimeoutError(f"Job did not finish within {timeout_sec} seconds")


final = poll_status(job_id)

if final["status"] == "failed":
    raise RuntimeError(final.get("error") or "Generation failed")

for download in final.get("downloads") or []:
    print(download["url"])
```

The response includes a `job_id`:

```json
{
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "pending",
  "output_type": "single_slide",
  "downloads": null,
  "error": null
}
```

## Poll status

Poll `GET /api/v1/status/{job_id}` with the same API key that created the job. The example above checks every 3 seconds and stops after 5 minutes. For retries, backoff, and `Retry-After` handling, see the [full client script example](/perceptis-api-v1/examples.md).

## Downloads

When `status` is `completed`, each item in `downloads` includes a short-lived `url` for a `.pptx` file. If a URL expires, poll status again for fresh links while the generated files remain available. Use the same API key that created the job.

Example completed response:

```json
{
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "completed",
  "output_type": "single_slide",
  "downloads": [
    {
      "url": "https://app.perceptis.ai/downloads/550e8400-e29b-41d4-a716-446655440000.pptx?expires=3600&signature=...",
      "format": "pptx",
      "variant": 1
    }
  ],
  "error": null
}
```

## Optional: reference images

For single-slide generation, include `reference_images` when you want the prompt to use a visual reference:

```json
"reference_images": [
  {
    "data": "<base64>",
    "mime_type": "image/png"
  }
]
```

You can also include `variant_count`, `template_name`, `use_web_search`, or `use_knowledge_base` when needed. See the [`POST /api/v1/generate` reference](/api-reference/post-generate.md) for the full request body.
