Back to Blog

Slide Surgery: What python-pptx Can't Do, Over One API Call

Duplicate, delete and reorder slides in an existing .pptx — the three oldest open python-pptx feature requests — now one HTTP call. Formatting untouched.

Posted by

If you automate PowerPoint in Python, you know the ritual: you reach for python-pptx, it does 90% of the job beautifully, and then you hit the same wall everyone hits. You can't duplicate a slide. You can't delete one. You can't reorder them. Those aren't obscure gaps — they are the oldest open feature requests in the entire library:

We just shipped all three as one endpoint: POST /api/powerpoint/edit.

One call, a chain of operations

Upload your .pptx and a list of operations. They run in order, each on the result of the previous one. Slide numbers are 1-based, exactly as you see them in PowerPoint:

curl -s https://powerpointengine.io/api/powerpoint/edit \
  -F 'file=@deck.pptx' \
  -F 'operations=[
    {"op": "duplicate", "slide": 2},
    {"op": "move", "slide": 5, "to": 1},
    {"op": "delete", "slide": 3}
  ]' | jq -r '.result.downloadUrl'

duplicate inserts the copy right after the source (or anywhere, with "insertAfter": 0 for the start of the deck). Every shape, chart, image, layout and master of your file stays byte-for-byte untouched — we operate on the OOXML package directly instead of parsing your deck into an object model and re-serializing it.

Why this is hard in an object model

Cloning one slide means updating four parts of the package in lockstep: the slide list in presentation.xml, the relationship table, [Content_Types].xml, and the slide part itself with its own relationships (minus the speaker notes, which legally belong to exactly one slide). An object-model library has to keep all of that consistent through its public API — which is why the issue has 89 comments and no merge. A server that owns the whole file for the duration of one request can just do it correctly.

And yes, we eat our own dog food with a sense of humor: every file our test suite edits is verified by opening it with python-pptx itself.

The template loop, finally

The #1 reason people wanted Slide.duplicate() was the classic reporting pattern: one designed template slide, N records of data. Combine /edit with our structure-preserving text replacement and that loop is two calls:

  • /edit — duplicate the template slide once per record.
  • /replace — fill {{placeholders}} in each copy. They're found even when PowerPoint has split them across text runs — the other famous python-pptx pain.

Bonus round: two more "impossible" ones

While we were in the neighborhood, we shipped the next two most-requested gaps from the same issue tracker.

Merge presentations (#403, #1036, #175)

POST /api/powerpoint/merge appends the slides of 2–5 decks in upload order — and every deck keeps its own design. Each copied slide brings its layout; the layout brings its master; the master brings its theme; charts and images travel along. Merge a corporate deck with a modern one and you get both masters, intact:

curl -s https://powerpointengine.io/api/powerpoint/merge \
  -F 'files=@intro.pptx' \
  -F 'files=@results.pptx' \
  -F 'files=@outro.pptx' | jq -r '.result.downloadUrl'

Replace an image in place (#116)

Picture.replace_image() has been requested since 2014. Add an image:<shape name> field to a /replace call and the picture swaps its pixels while keeping position, size, cropping and effects — matched by the shape's name or alt text:

curl -s https://powerpointengine.io/api/powerpoint/replace \
  -F 'file=@deck.pptx' \
  -F 'replacements={"client":"ACME"}' \
  -F 'image:Logo=@acme-logo.png'

Works for AI agents too

Everything above is also available to AI agents: the agent skill documents the endpoint, and our MCP server handles generation. An agent can generate a deck, then rearrange it, then fill in data — without a single line of COM automation or a Windows box.

Try it on the live tool or read the API docs. Free tier included — bring a deck you already have.