> Source: https://txtfetch.com/extract/email > Plain-text twin — every page on txtfetch.com has one. https://txtfetch.com/text --- # Email, headers to attachments, one call. From/To/Subject headers, the message body, and attached documents all come out together. You don't orchestrate separate steps. the-problem Email is a container format wearing a text format's clothes. It has headers, a MIME-encoded body (often both plain-text and HTML parts), and arbitrary attached documents that themselves need extracting. Most extraction tools have no email code path at all. That's especially true for VLM-based tools built around single images or pages. Teams end up hand-rolling header parsing and MIME decoding just to get the text they actually want. one-request-solution txtfetch treats email as a first-class format. POST an .eml, Outlook .msg, or .mbox archive, and Apache Tika extracts headers, body, and attachment text together into one plain-text response. The request shape is the same as every other format. curl ```curl curl -X POST https://api.txtfetch.com/v1/extract \ -H "Authorization: Bearer $TXTFETCH_KEY" \ -F file=@support-thread.eml ``` Python ```python import os import requests with open("support-thread.eml", "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("support-thread.eml")]); const form = new FormData(); form.append("file", file, "support-thread.eml"); 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("support-thread.eml") if err != nil { panic(err) } defer f.Close() var body bytes.Buffer writer := multipart.NewWriter(&body) part, err := writer.CreateFormFile("file", "support-thread.eml") 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/archive/2024-q1.mbox" \ -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/archive/2024-q1.mbox"}, ) print(r.json()["extracted_text"]) ``` JavaScript ```javascript const endpoint = new URL("https://api.txtfetch.com/v1/extract"); endpoint.searchParams.set("url", "https://example.com/archive/2024-q1.mbox"); 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/archive/2024-q1.mbox") 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 .eml 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. Invoice Follow-up (EML) `invoice-followup.eml` · message/rfc822 · left pane read via Apache Tika, **right pane: the human-checked expected.json** raw parser output ``` From: Priya Shah To: Billing Subject: Re: Invoice #7734 Hi team, Following up on the invoice below — could you confirm receipt? Description Qty Amount Consulting hours 12 $1,800.00 Travel 1 $340.00 Invoice total: $2,140.00 Due date: 2026-08-15 Thanks, Priya ``` the human-checked ideal ``` From: Priya Shah To: Billing Subject: Re: Invoice #7734 Hi team, Following up on the invoice below — could you confirm receipt? Description Qty Amount Consulting hours 12 $1,800.00 Travel 1 $340.00 Invoice total: $2,140.00 Due date: 2026-08-15 Thanks, Priya ``` [See all ten documents, with the full explanation →](https://txtfetch.com/diff) formats-covered - `.eml` - `.msg` - `.mbox` faq **Can I parse a .eml email for an LLM?**: Yes. POST the .eml file to https://api.txtfetch.com/v1/extract and get back headers, body, and any attachment text as one plain-text response. It's ready to feed into a prompt or embedding step. **Does it support Outlook .msg files?**: Yes, through the same endpoint, with the same response shape. Under the hood, .msg is a completely different container: OLE2 with MAPI property streams, not RFC-822. Read the dedicated .msg page if you're debugging attachment or nesting behavior. **Can it process a whole .mbox archive?**: Yes. Apache Tika reads .mbox archives and extracts the contained messages' text. **Are email attachments included in the extracted text?**: Yes. Attached documents are parsed, and their text is folded into the response alongside the message body. go-further - [Outlook .msg internals, .pst/.ost, and nested attachments →](https://txtfetch.com/extract/msg) - [Drop a real .eml or .msg and see the extracted text, free →](https://txtfetch.com/tools/email-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) Email - [`.msg`](https://txtfetch.com/extract/msg) - [All formats →](https://txtfetch.com/extract) ## Send a real .eml 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 .eml reader →](https://txtfetch.com/tools/email-to-text)