> Source: https://txtfetch.com/extract/pptx > Plain-text twin — every page on txtfetch.com has one. https://txtfetch.com/text --- # Slide text and speaker notes, together. Most extractors keep one or the other. txtfetch reads slide bodies and speaker notes from the same request. the-problem A .pptx is an OOXML zip archive. Each slide's visible text lives in ppt/slides/slideN.xml. But speaker notes are a separate part entirely, ppt/notesSlides/, with its own XML files and its own relationship IDs back to the slide they annotate. Extractors that read slideN.xml and stop miss the notes silently. Extractors built around the notes miss the body instead. Text also hides in grouped shapes, nested inside other shapes rather than top-level. It hides in SmartArt diagrams too: their text sits in diagrams/data\*.xml, disconnected from the visible diagram layout. Tables can hide text as well, and so can text baked into slide images as pixels rather than markup. Slide order compounds all of this. It's defined by relationships in presentation.xml, not by the numeric order slide files happen to be zipped in. one-request-solution txtfetch's Office parser reads both parts of a .pptx in one pass: slide body text from ppt/slides/, and speaker notes from ppt/notesSlides/. It also pulls grouped-shape and SmartArt text out of their nested XML. All of it folds into one extracted\_text response, in the deck's real slide order. curl ```curl curl -X POST https://api.txtfetch.com/v1/extract \ -H "Authorization: Bearer $TXTFETCH_KEY" \ -F file=@quarterly-board-deck.pptx ``` Python ```python import os import requests with open("quarterly-board-deck.pptx", "rb") as f: r = requests.post( "https://api.txtfetch.com/v1/extract", headers={"Authorization": f"Bearer {os.environ['TXTFETCH_KEY']}"}, files={"file": f}, ) print(r.json()["extracted_text"]) ``` JavaScript ```javascript import { readFile } from "node:fs/promises"; const file = new Blob([await readFile("quarterly-board-deck.pptx")]); const form = new FormData(); form.append("file", file, "quarterly-board-deck.pptx"); const res = await fetch("https://api.txtfetch.com/v1/extract", { method: "POST", headers: { Authorization: `Bearer ${process.env.TXTFETCH_KEY}` }, body: form, }); const { extracted_text } = await res.json(); console.log(extracted_text); ``` Go ```go package main import ( "bytes" "encoding/json" "fmt" "io" "mime/multipart" "net/http" "os" ) type extractResponse struct { Status string `json:"status"` ExtractedText string `json:"extracted_text"` } func main() { f, err := os.Open("quarterly-board-deck.pptx") if err != nil { panic(err) } defer f.Close() var body bytes.Buffer writer := multipart.NewWriter(&body) part, err := writer.CreateFormFile("file", "quarterly-board-deck.pptx") if err != nil { panic(err) } if _, err := io.Copy(part, f); err != nil { panic(err) } writer.Close() req, err := http.NewRequest("POST", "https://api.txtfetch.com/v1/extract", &body) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer "+os.Getenv("TXTFETCH_KEY")) req.Header.Set("Content-Type", writer.FormDataContentType()) resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() var result extractResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { panic(err) } fmt.Println(result.ExtractedText) } ``` Or skip the download. Pass a `url` parameter and txtfetch fetches the document server-side: curl ```curl curl -X POST "https://api.txtfetch.com/v1/extract?url=https://example.com/decks/product-roadmap.pptx" \ -H "Authorization: Bearer $TXTFETCH_KEY" ``` Python ```python import os import requests r = requests.post( "https://api.txtfetch.com/v1/extract", headers={"Authorization": f"Bearer {os.environ['TXTFETCH_KEY']}"}, params={"url": "https://example.com/decks/product-roadmap.pptx"}, ) print(r.json()["extracted_text"]) ``` JavaScript ```javascript const endpoint = new URL("https://api.txtfetch.com/v1/extract"); endpoint.searchParams.set("url", "https://example.com/decks/product-roadmap.pptx"); const res = await fetch(endpoint, { method: "POST", headers: { Authorization: `Bearer ${process.env.TXTFETCH_KEY}` }, }); const { extracted_text } = await res.json(); console.log(extracted_text); ``` Go ```go package main import ( "encoding/json" "fmt" "net/http" "net/url" "os" ) type extractResponse struct { Status string `json:"status"` ExtractedText string `json:"extracted_text"` } func main() { endpoint, err := url.Parse("https://api.txtfetch.com/v1/extract") if err != nil { panic(err) } q := endpoint.Query() q.Set("url", "https://example.com/decks/product-roadmap.pptx") endpoint.RawQuery = q.Encode() req, err := http.NewRequest("POST", endpoint.String(), nil) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer "+os.Getenv("TXTFETCH_KEY")) resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() var result extractResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { panic(err) } fmt.Println(result.ExtractedText) } ``` ``` { "status": "success", "extracted_text": "..." } ``` what-comes-back That is the shape. This is the text. A real .pptx from the benchmark corpus, word-diffed against the raw parser output. Nothing here was written for this page. Every character comes from a committed fixture. S6 Launch Deck (PPTX) `s6-launch-deck.pptx` · application/vnd.openxmlformats-officedocument.presentationml.presentation · left pane read via Apache Tika, **right pane: the human-checked expected.json** raw parser output ``` Benchmarking the Quality Climb txtfetch S6 — accuracy harness Why it matters Structured output needs proof, not just a demo VLM tier cost only pays off if quality actually improves What ships Committed corpus across 10 categories Reproducible CLI + committed report ``` the human-checked ideal ``` Benchmarking the Quality Climb txtfetch S6 — accuracy harness Why it matters Structured output needs proof, not just a demo VLM tier cost only pays off if quality actually improves What ships Committed corpus across 10 categories Reproducible CLI + committed report ``` [See all ten documents, with the full explanation →](https://txtfetch.com/diff) formats-covered - `.pptx` - `.ppt` - `.pptm` - `.potx` - `.odp` response-options Presenting slide structure to an LLM? ?format=markdown returns headings and bullet hierarchy per slide instead of one flat block of text. faq **Does extraction include PowerPoint speaker notes, or just slide text?**: Both. Speaker notes live in a separate ppt/notesSlides/ part of the .pptx archive, apart from the slide body. txtfetch reads both parts and includes them together in the response. **What about text inside grouped shapes or SmartArt diagrams?**: Yes. Grouped shapes nest inside other shapes rather than sitting at the top level, and SmartArt text lives in its own diagrams/data*.xml part. Both are walked and included, not just top-level slide text boxes. **Does it preserve the actual slide order?**: Yes. Slide order comes from presentation.xml's relationship list, not from the filename order of the slideN.xml parts inside the zip. That filename order doesn't reliably match reading order. **Does it handle the legacy .ppt binary format too?**: Yes, and .pptm (macro-enabled) and .potx (template) too. All four route through the same endpoint and return the same response shape. go-further - [Read the .pptx guide →](https://txtfetch.com/blog/parse-office-docs-docx-pptx-xlsx-for-llms) - [Drop a real .pptx and see the exact text (and speaker notes) it extracts to, free →](https://txtfetch.com/tools/pptx-to-text) - [API quickstart →](https://txtfetch.com/docs) - [How the pipeline works →](https://txtfetch.com/how-it-works) - [Extract it from your language →](https://txtfetch.com/for) - [Get an API key →](https://app.txtfetch.com/signup) Office - [`.docx`](https://txtfetch.com/extract/docx) - [`.xlsx`](https://txtfetch.com/extract/xlsx) - [`.doc`](https://txtfetch.com/extract/legacy-office) - [`.rtf`](https://txtfetch.com/extract/rtf) - [All formats →](https://txtfetch.com/extract) ## Send a real .pptx through it. One HTTP call returns the text. Read one in your browser first, for free. [Get an API key →](https://app.txtfetch.com/signup) [Open the free .pptx reader →](https://txtfetch.com/tools/pptx-to-text)