> Source: https://txtfetch.com/extract/xlsx > Plain-text twin — every page on txtfetch.com has one. https://txtfetch.com/text --- # Spreadsheets, resolved not raw. Cell values are indices into a shared string table. Formulas store their formula, not their answer. Tika resolves both before you ever see the response. the-problem A spreadsheet's cells don't hold their own text. In .xlsx, string cell values are indices into xl/sharedStrings.xml, a dedup table shared across the whole workbook, not inline text. So reading cells in isolation just gets you numbers pointing at other numbers. Formula cells store the formula itself, like =SUM(A1:A10). The number a human sees is a separately cached result value, and plenty of naive readers grab the wrong one, or the formula string, instead. Number formats add another layer of indirection. A cell can be stored as 0.42 and displayed as 42%. Or it can be stored as a serial integer and displayed as a date. So 'the value' and 'what's on screen' genuinely differ. Hidden sheets, hidden rows, and merged cells still carry meaning that a flat text dump can drop entirely. one-request-solution txtfetch resolves the indirection for you. Shared-string references become their actual text, and formula cells return Tika's cached computed value rather than the =SUM() source. That means the numbers in your response match what a person looking at the sheet would see. For table structure, with rows and columns kept intact instead of flattened into a wall of numbers, request ?format=markdown. curl ```curl curl -X POST https://api.txtfetch.com/v1/extract \ -H "Authorization: Bearer $TXTFETCH_KEY" \ -F file=@q1-revenue-workbook.xlsx ``` Python ```python import os import requests with open("q1-revenue-workbook.xlsx", "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("q1-revenue-workbook.xlsx")]); const form = new FormData(); form.append("file", file, "q1-revenue-workbook.xlsx"); 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("q1-revenue-workbook.xlsx") if err != nil { panic(err) } defer f.Close() var body bytes.Buffer writer := multipart.NewWriter(&body) part, err := writer.CreateFormFile("file", "q1-revenue-workbook.xlsx") 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/reports/regional-sales.xlsx" \ -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/reports/regional-sales.xlsx"}, ) print(r.json()["extracted_text"]) ``` JavaScript ```javascript const endpoint = new URL("https://api.txtfetch.com/v1/extract"); endpoint.searchParams.set("url", "https://example.com/reports/regional-sales.xlsx"); 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/reports/regional-sales.xlsx") 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 .xlsx 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. FY2026 Budget Forecast (XLSX) `fy2026-budget-forecast.xlsx` · application/vnd.openxmlformats-officedocument.spreadsheetml.sheet · left pane read via Apache Tika, **right pane: the human-checked expected.json** raw parser output ``` Opex Line item Q1 Q2 Cloud hosting 4200 4600 Payroll 61000 64500 Summary Total 65200 69100 ``` the human-checked ideal ``` Opex Line item Q1 Q2 Cloud hosting 4200 4600 Payroll 61000 64500 Summary Total 65200 69100 ``` [See all ten documents, with the full explanation →](https://txtfetch.com/diff) formats-covered - `.xlsx` - `.xls` - `.xlsm` - `.csv` - `.ods` response-options Need rows and columns, not a flat wall of numbers? ?format=markdown returns each sheet as a proper markdown table. faq **Does extraction return the formula or the calculated value?**: The calculated value. Tika reads the cached formula result that Excel stores alongside the formula, so you get 18200 instead of the literal string =SUM(A1:A10). **Do percentages and dates come back as raw numbers?**: No. The cell's number format is applied before the text is returned. A cell stored as 0.42 with a percent format comes back as 42%. A date stored as the serial number 46229 comes back as 2026-07-26. A cell with no format applied returns its stored value unchanged, which is what you want for a plain number. **Are hidden sheets and hidden rows included?**: Yes. txtfetch extracts hidden sheets and rows along with visible ones, since hiding a sheet in Excel doesn't remove its data. **Can I get real table structure instead of a flat text dump?**: Yes. Pass ?format=markdown, and rows and columns come back as a markdown table per sheet instead of space-separated cell values. go-further - [Read the .xlsx guide →](https://txtfetch.com/blog/extract-tables-for-rag) - [Drop a real .xlsx and see the exact text every sheet extracts to, free →](https://txtfetch.com/tools/xlsx-to-text) - [The wider Office-family parsing story (DOCX/PPTX/XLSX) →](https://txtfetch.com/blog/parse-office-docs-docx-pptx-xlsx-for-llms) - [Charset detection for plain .csv and other delimited exports →](https://txtfetch.com/extract/csv) - [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) - [`.pptx`](https://txtfetch.com/extract/pptx) - [`.doc`](https://txtfetch.com/extract/legacy-office) - [`.rtf`](https://txtfetch.com/extract/rtf) - [All formats →](https://txtfetch.com/extract) ## Send a real .xlsx 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 .xlsx reader →](https://txtfetch.com/tools/xlsx-to-text)