A developer using Claude Code to build an agent pipeline that reads PPTX files needs slide-structured chunks with section metadata, not flat paragraph text that loses the slide boundaries. Extracting a PPTX with python-pptx returns paragraphs from each slide’s text frames with no slide structure, no heading hierarchy, and nothing to populate metadata a retrieval layer can filter on. For a workflow that must reason over a product deck, a sales presentation, or a research brief, flat paragraph text without slide context produces content the agent cannot attribute to a specific slide or filter by section. EnConvert’s Ingest endpoint takes the uploaded PPTX and returns JSONL chunks where each slide becomes a ## Slide N: Title Markdown section, each carrying its headings_path breadcrumb, section name, and chunk index in metadata, so the deck is ready for a Claude Code workflow to read and reason over by slide in one call.
How to Ingest PPTX into Claude Code with EnConvert
Use EnConvert to ingest a PPTX into a Claude Code workflow as slide-structured JSONL chunks with section metadata the agent can read and reason over by slide
Get API keyExample
python
import json
import os
import time
from pathlib import Path
import requests
HEADERS = {"X-API-Key": os.environ["ENCONVERT_API_KEY"]}
PPTX_PATH = Path("/path/to/your/product-deck.pptx")
OUTPUT_PATH = PPTX_PATH.with_suffix(".chunks.jsonl")
# Step 1: Upload the PPTX (multipart field name is "files")
# Each slide with text becomes a "## Slide N: Title" section
# Slides with no text, such as image-only slides, produce no chunks
with PPTX_PATH.open("rb") as f:
upload = requests.post(
"https://api.enconvert.com/v2/ingest/files",
headers=HEADERS,
files={
"files": (
PPTX_PATH.name,
f,
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
)
},
timeout=120,
)
upload.raise_for_status()
job_id = upload.json()["job_id"]
# Step 2: Poll until the job completes
deadline = time.monotonic() + 900
while True:
status_response = requests.get(
f"https://api.enconvert.com/v2/ingest/{job_id}",
headers=HEADERS,
timeout=30,
)
status_response.raise_for_status()
job = status_response.json()
if job["status"] == "completed":
output_url = job["output_url"]
break
if job["status"] in ("failed", "canceled"):
raise RuntimeError(
job.get("error_message") or f"Ingest job {job_id} {job['status']}"
)
if time.monotonic() >= deadline:
raise TimeoutError(f"Job {job_id} is still running; resume polling later")
time.sleep(5)
# Step 3: Download the JSONL output
# The signed URL is valid for 15 minutes. Plain GET, no API key.
jsonl = requests.get(output_url, timeout=120)
jsonl.raise_for_status()
# Save to disk so a Claude Code session can read it directly from the project folder
OUTPUT_PATH.write_bytes(jsonl.content)
# One chunk per line. Split on "\n" only: str.splitlines() also breaks on
# characters such as U+2028 that can appear inside a JSON string
chunks = [
json.loads(line)
for line in jsonl.content.decode("utf-8").split("\n")
if line.strip()
]
print(f"Loaded {len(chunks)} slide chunks saved to {OUTPUT_PATH}")
# Step 4: Print each slide's section and content
# Pass selected chunks into a Claude Code context or load into a vector store
for chunk in chunks:
metadata = chunk["metadata"]
print(f"[{metadata['section']} | chunk {metadata['chunk_index']}]")
print(" / ".join(metadata["headings_path"]))
print(chunk["content"])
print()
What you get back
Ingest returns the PPTX as JSONL chunks where each slide becomes a ## Slide N: Title Markdown section, each carrying its headings_path breadcrumb, section name, and chunk index, so a Claude Code workflow can retrieve and reason over content by slide. Extracting text with python-pptx returns paragraphs from each slide’s text frames with no slide structure, no heading hierarchy, and no metadata the workflow can filter on. Image-only slides are skipped.