Tutorial2026-04-03Updated 2026-07-149 min

Generate PowerPoint in Python (2026): python-pptx, an API — or both

Three working approaches side-by-side: a 60-line python-pptx script, a typed SlideForge intent that renders in under a second, and running your own python-pptx code in a managed sandbox with themes, widgets, and output validation. Updated July 2026.

There are three ways to generate PowerPoint files from Python in 2026: the python-pptx library (free, full control, you own every pixel), a structured rendering API like SlideForge (typed data in, validated consulting-grade .pptx out, $0.05/slide), or — new since mid-2026 — both at once: writing python-pptx code and running it in SlideForge's sandbox, where it gets themes, chart widgets, and output verification for free. This tutorial covers all three with working code.

1. python-pptx: The Standard Library

python-pptxis the de facto Python library for creating and modifying PowerPoint files. It's free, open source (MIT license), and gives you full programmatic control over every element in a .pptx file.

Install and create a basic slide

pip install python-pptx
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor

# Create presentation
prs = Presentation()
prs.slide_width = Inches(13.333)
prs.slide_height = Inches(7.5)

# Add a blank slide
slide = prs.slides.add_slide(prs.slide_layouts[6])

# Add a title
title = slide.shapes.add_textbox(Inches(0.5), Inches(0.3), Inches(12), Inches(0.6))
tf = title.text_frame
tf.text = "Q1 2026 Performance"
tf.paragraphs[0].font.size = Pt(28)
tf.paragraphs[0].font.bold = True

# Save
prs.save("basic_slide.pptx")

This works. But the output looks like it was made by a developer, not a designer. No color harmony, no spacing system, no visual hierarchy beyond font size.

Building a real slide: KPI Dashboard

Here's what it takes to build a consulting-quality KPI dashboard with python-pptx:

from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN

prs = Presentation()
prs.slide_width = Inches(13.333)
prs.slide_height = Inches(7.5)
slide = prs.slides.add_slide(prs.slide_layouts[6])

# Title
title = slide.shapes.add_textbox(
    Inches(0.5), Inches(0.3), Inches(12), Inches(0.6)
)
tf = title.text_frame
tf.text = "Q1 2026 Performance Summary"
tf.paragraphs[0].font.size = Pt(28)
tf.paragraphs[0].font.bold = True

# KPI metrics — repeat for each one
metrics = [
    ("$12.4M", "Revenue", "+18% YoY"),
    ("847", "New Clients", "+23%"),
    ("94.2%", "Retention Rate", ""),
    ("4.7", "NPS Score", "+0.3"),
]

for i, (value, label, trend) in enumerate(metrics):
    left = Inches(0.5 + i * 3.1)
    top = Inches(1.5)
    width = Inches(2.8)
    height = Inches(2.0)

    # Background box
    box = slide.shapes.add_shape(1, left, top, width, height)
    box.fill.solid()
    box.fill.fore_color.rgb = RGBColor(0xF5, 0xF5, 0xF5)
    box.line.fill.background()

    # Value text
    val_box = slide.shapes.add_textbox(left, Inches(1.7), width, Inches(0.8))
    val_tf = val_box.text_frame
    val_tf.text = value
    val_tf.paragraphs[0].font.size = Pt(36)
    val_tf.paragraphs[0].font.bold = True
    val_tf.paragraphs[0].alignment = PP_ALIGN.CENTER

    # Label text
    lbl_box = slide.shapes.add_textbox(left, Inches(2.5), width, Inches(0.4))
    lbl_tf = lbl_box.text_frame
    lbl_tf.text = label.upper()
    lbl_tf.paragraphs[0].font.size = Pt(9)
    lbl_tf.paragraphs[0].font.color.rgb = RGBColor(0x99, 0x99, 0x99)
    lbl_tf.paragraphs[0].alignment = PP_ALIGN.CENTER

    # ... and trend text, accent lines, spacing adjustments...

prs.save("kpi_dashboard.pptx")
# ~60 lines for a basic version. A polished version is 100+.

That's ~60 lines for a basic KPI dashboard. A polished version with accent lines, trend arrows, tinted backgrounds, and a takeaway banner runs 100+ lines. And this is one slide type — a consulting deck needs 10-15 different layouts.

The pain points

  • Alignment is manual. Every coordinate is hardcoded in inches. Move one element and everything else needs adjusting.
  • No design system. Colors, fonts, and spacing are ad-hoc. Consistency across slides requires discipline and shared constants.
  • Limited chart support. No waterfall, treemap, or sunburst charts. Combo charts can corrupt files.
  • No animations. The #1 requested feature (GitHub issue #1106) — still unsupported.
  • Nothing checks the output. If your loop drops a metric or a textbox overflows the canvas, python-pptx saves the file happily. You find out in the meeting.
  • Maintenance burden. Every new slide type is a multi-day project. Bug fixes in one template can break another.

2. SlideForge API: Typed Data In, Validated Slide Out

SlideForge renders the same KPI dashboard from a typed request: pick a form from the catalog (150+ layouts — KPI boards, waterfalls, Gantt plans, org charts, funnels…), put your numbers in the documented fields, and the engine binds them verbatim — your values appear exactly as sent, deterministically, with no LLM in the loop:

import requests

API = "https://api.slideforge.dev"
KEY = {"Authorization": "Bearer sf_live_YOUR_KEY"}

resp = requests.post(f"{API}/v1/render/intent", headers=KEY, json={
    "form": "kpi_metrics",
    "headline": "Q1 2026: all four KPIs trending up",
    "data": {
        "metrics": [
            {"value": "$12.4M", "label": "Revenue", "trend": "+18% YoY"},
            {"value": "847", "label": "New Clients", "trend": "+23%"},
            {"value": "94.2%", "label": "Retention Rate"},
            {"value": "4.7", "label": "NPS Score", "trend": "+0.3"},
        ],
    },
    "takeaway": "On track for the $50M full-year target",
})
result = resp.json()
print(result["status"])    # "complete"
print(result["fidelity"])  # "verbatim" — every value bound exactly as sent
job_id = result["job_id"]

# Download with the same Bearer key
pptx = requests.get(f"{API}/v1/jobs/{job_id}/pptx", headers=KEY)
open("kpi_dashboard.pptx", "wb").write(pptx.content)

Rendering completes in well under a second at $0.05/slide, and the output is a native .pptx with editable shapes — open it in PowerPoint and change anything.

The part python-pptx can't give you is the honesty layer: every response declares what happened. fidelity: verbatim means every supplied value rendered exactly. If your waterfall's deltas don't sum to the declared total, you don't get a wrong slide — you get status: completed_with_errorswith the exact discrepancy ("start+deltas = 49, declared end = 70"), and you aren't billed. A render that isn't usable is free.

For content that doesn't arrive as typed data yet, send a prose brief and let the router pick the form:

resp = requests.post(f"{API}/v1/render/auto", headers=KEY, json={
    "brief": "Revenue bridge Q4 to Q1: start $10.2M. New clients +$2.1M, "
             "upsell +$1.3M, churn -$0.8M, FX -$0.2M.",
})
# Routed to the waterfall form, numbers bound verbatim, <1.5s, $0.05

3. Both: Your python-pptx Code, Run in the SlideForge Sandbox

If you already write python-pptx — or your layout doesn't exist in any catalog — you don't have to choose. POST /v1/render/code executes your build() function in a sandbox where it gets what raw python-pptx lacks: a theme, pre-built chart widgets, chrome (title/takeaway furniture), a geometry linter, and the same output verification as the structured path.

code = '''
def build(prs, slide):
    # Chrome (title, takeaway band) is already rendered — draw the body.
    # add_widget draws SlideForge's own theme-correct parts:
    add_widget(slide, "kpi_metrics", x=0.6, y=1.4, w=12.1, h=4.6, content={
        "metrics": [
            {"value": "$12.4M", "label": "Revenue", "trend": "+18% YoY"},
            {"value": "847", "label": "New Clients", "trend": "+23%"},
            {"value": "94.2%", "label": "Retention Rate"},
            {"value": "4.7", "label": "NPS Score", "trend": "+0.3"},
        ],
    }, theme=THEME)
'''

resp = requests.post(f"{API}/v1/render/code", headers=KEY, json={
    "code": code,
    "headline": "Q1 2026: all four KPIs trending up",
    "takeaway": "On track for the $50M full-year target",
})
# Your code, their theme + widgets + linter. $0.05, ~2s, no LLM involved.

The widget catalog covers the engine's own chart primitives — waterfall, Gantt, funnel, org chart and the rest — so "custom layout" stops meaning "hand-place every rectangle." Static analysis catches overlapping and off-canvas shapes before you open the file, and geometry that can't work (negative sizes, impossible constraints) is rejected with the exact numbers instead of silently producing a broken slide.

4. When to Use Which

Criteriapython-pptxSlideForge intentSlideForge code mode
CostFree$0.05/slide$0.05/slide
Setup timeDays-weeksMinutesMinutes (it's your python-pptx skill)
Layout workAll yoursNone — pick a formYours, with widgets + theme + linter
Output validationNoneReconciliation, capacity, overflow + fidelity flagGeometry linter + content checks
Charts (waterfall, Gantt, funnel…)Build by handBuilt inadd_widget()
Offline / air-gappedYesNo (API)No (API)
Full XML controlYesNoNearly — your code draws the body
MaintenanceYou maintain itManagedYour code, managed runtime

Use python-pptx alone if you need full control in an air-gapped environment, or have zero budget and unlimited time.

Use the intent API if your data is structured and the deck must be right unattended — reports, agents, SaaS export. Typed fields in, verbatim slide out, validated.

Use code mode if you think in python-pptx but want the theme, the chart widgets, and something checking the output — or your layout is genuinely outside any catalog.

They're also compatible in the other direction: every SlideForge output is a standard .pptx that python-pptx can open and post-process.

Getting Started

Sign up free (60 free slides, no card) and follow the quickstart guide to render your first slide. Browse the form catalog and per-form schemas in the template gallery, or read the detailed comparison of SlideForge vs python-pptx.

Frequently asked questions

What is the best way to generate PowerPoint files from Python?

For full offline control, python-pptx (free, but you write all layout code and nothing validates the output). For structured data that must render correctly unattended, the SlideForge API renders typed intents into validated, editable .pptx at $0.05/slide in under a second. If you already write python-pptx, SlideForge's code mode runs your build() function in a sandbox with themes, chart widgets, and a geometry linter.

Can I run my own python-pptx code in SlideForge?

Yes — POST /v1/render/code executes your python-pptx build() function in a sandbox with the SlideForge theme, pre-built chart widgets (waterfall, Gantt, funnel, org chart via add_widget), title/takeaway chrome, and static geometry checks. $0.05 per render, ~2 seconds, no LLM involved.

Does python-pptx work with the SlideForge output?

Yes. Every SlideForge render is a standard .pptx with editable shapes — python-pptx can open and post-process it like any other PowerPoint file.

Try SlideForge free

60 free slides, no card required. Generate your first slide in under a minute.