---
title: "Ingest PPTX into AutoGen with EnConvert API"
description: "How to ingest PPTX files inside an AutoGen pipeline with EnConvert, returning slide-structured JSONL chunks with section and speaker note metadata for retrieval"
canonical: "https://www.enconvert.com/solutions/ingest-pptx-into-autogen-with-enconvert-api"
locale: "en"
---

# How to Ingest PPTX with AutoGen

A developer building an AutoGen agent needs to ingest PPTX presentation files before passing slide content into an agent’s context or retrieval layer. PPTX files have no native text representation an agent can consume directly: extracting slide text requires parsing the OpenXML format, handling image-only slides, and reassembling speaker notes separately, all before chunking. EnConvert’s Ingest endpoint accepts a PPTX upload and converts each text-based slide to a Markdown section headed ## Slide N: Title, with speaker notes as a ### Notes subsection and image-only and blank slides skipped. The file returns as JSONL chunks with headings_path, section, chunk_index, word_count, and source_url metadata on every chunk, ready to pass into an AutoGen agent or vector store.

## Example

```python
import json
import os
import time
import requests
from autogen import AssistantAgent, UserProxyAgent

# Step 1: Upload the PPTX file to the Ingest endpoint
with open("architecture-deck.pptx", "rb") as f:
    upload_response = requests.post(
        "https://api.enconvert.com/v2/ingest/files",
        headers={"X-API-Key": os.environ["ENCONVERT_API_KEY"]},
        files={"files": f},
    )

if upload_response.status_code == 402:
    raise RuntimeError("Ingest is not on your plan, or the monthly ops allowance is exhausted")
upload_response.raise_for_status()
job_id = upload_response.json()["job_id"]

# Step 2: Poll until the job reaches a terminal state
while True:
    status_response = requests.get(
        f"https://api.enconvert.com/v2/ingest/{job_id}",
        headers={"X-API-Key": os.environ["ENCONVERT_API_KEY"]},
    )
    status_response.raise_for_status()
    job = status_response.json()

    if job["status"] == "completed":
        break
    if job["status"] in {"failed", "canceled"}:
        raise RuntimeError(
            job.get("error_message") or f"Ingest job {job_id} ended with status {job['status']}"
        )
    time.sleep(5)

# Step 3: Download JSONL output from signed output_url (expires after 15 minutes)
jsonl_response = requests.get(job["output_url"])
jsonl_response.raise_for_status()
chunks = [json.loads(line) for line in jsonl_response.text.splitlines() if line.strip()]

# Each slide arrives as "## Slide N: Title"; speaker notes arrive as "### Notes"
# Image-only slides and blank slides are skipped entirely
# Step 4: Build context block from chunks, preserving slide and section metadata
context = "\n\n".join(
    f"[section: {c['metadata']['section']} | chunk: {c['metadata']['chunk_index']} | source: {c['metadata']['source_url']}]\n"
    f"{c.get('content')}"
    for c in chunks[:12]
)

# Step 5: Pass context into an AutoGen agent
assistant = AssistantAgent(
    name="deck_analyst",
    llm_config={"config_list": [{"model": "gpt-4.1", "api_key": os.environ["OPENAI_API_KEY"]}]},
    system_message=f"""Answer questions about the presentation using only the context provided.
Cite the slide number and section name for every claim.
Speaker notes appear as a ### Notes subsection under the slide they belong to.

Context:
{context}
""",
)

user = UserProxyAgent(
    name="user",
    human_input_mode="NEVER",
    code_execution_config=False,
)
user.initiate_chat(assistant, message="What are the key architecture decisions described in this deck?")
```

## Output

The Ingest endpoint returns each slide converted to a Markdown section headed ## Slide N: Title, speaker notes as a ### Notes subsection, image-only and blank slides skipped, and the result as JSONL chunks with headings_path, section, chunk_index, word_count, and source_url. Extracting text from a PPTX directly requires parsing OpenXML, assembling slide text manually, and handling speaker notes as a separate pass, with no metadata and no consistent output schema.

## Get started

[Get free API key — 500 ops per month, no card required.](https://www.enconvert.com/auth?mode=signup)

## Related solutions

- [How to Scrape Wikipedia with LlamaIndex](https://www.enconvert.com/solutions/scrape-wikipedia-with-llamaindex-enconvert.md)
- [How to Ingest Markdown into LangChain with EnConvert](https://www.enconvert.com/solutions/ingest-markdown-into-langchain-with-heading-context.md)
- [Best JigsawStack Alternative for Structured Data Extraction](https://www.enconvert.com/solutions/jigsawstack-alternative-for-structured-data-enconvert.md)
- [How to Ingest Audio Transcripts into Qdrant with EnConvert](https://www.enconvert.com/solutions/ingest-audio-transcripts-into-qdrant-enconvert-api.md)
- [How to Ingest a CSV into pgvector Without Losing Row Context](https://www.enconvert.com/solutions/ingest-csv-into-pgvector-with-row-context-enconvert-api.md)
- [How to Ingest CSV into CrewAI with EnConvert](https://www.enconvert.com/solutions/ingest-csv-into-crewai-with-enconvert.md)
