> Source: https://txtfetch.com/solutions/contract-and-legal-review > Plain-text twin — every page on txtfetch.com has one. https://txtfetch.com/text --- # Read a whole contract, attachments and all. Legal teams review old files: .doc contracts, .msg and .eml threads with attachments, and scanned signature pages inside a digital PDF. txtfetch reads all of them. the-problem Legal operations and e-discovery deal with an old corpus. A contract might be a .doc file from 2009, or a .msg thread with three attachments. A signature page is often a scan glued inside an otherwise digital PDF. Generic extraction breaks the reading order on multi-column exhibits and loses the thread on nested email attachments. how-txtfetch-solves-it txtfetch reads .doc, .msg, and .eml threads, attachments included, plus scanned signature pages inside a digital PDF. It extracts in reading order for most PDFs, and /fixes/columns-out-of-order covers the multi-column exhibits that still need a check. It does not classify clauses and it does not redact text. Pricing is per document, not per page, which matters when a contract runs to 300 pages. - One endpoint reads .doc, .msg, and .eml threads with attachments. - Multi-column exhibits can still interleave, and /fixes/columns-out-of-order covers how to check. - OCR runs automatically on scanned pages inside an otherwise digital-native PDF. - Per-document pricing means a 300-page contract costs the same as a one-pager. - Async job and webhook mode handles large discovery batches. - The response text keeps attachment boundaries clear, so one email thread never blends into the next. - The same endpoint reads a native .docx exhibit and a scanned signature page with no format check on your side. curl ```curl curl -X POST https://api.txtfetch.com/v1/extract \ -H "Authorization: Bearer $TXTFETCH_KEY" \ -F file=@contract.doc ``` Python ```python import os import requests with open("contract.doc", "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("contract.doc")]); const form = new FormData(); form.append("file", file, "contract.doc"); 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("contract.doc") if err != nil { panic(err) } defer f.Close() var body bytes.Buffer writer := multipart.NewWriter(&body) part, err := writer.CreateFormFile("file", "contract.doc") 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) } ``` ``` { "status": "success", "extracted_text": "..." } ``` faq **Does txtfetch classify contract clauses?**: No. txtfetch returns the contract as plain text. Clause classification and redaction are up to your own tooling. **Can txtfetch read an .msg or .eml email thread with attachments?**: Yes. It extracts the message body, headers, and each attachment's text in one call. **How does per-document pricing help with long contracts?**: A 300-page contract costs the same as a one-page NDA, so long exhibits don't multiply your bill. related-reading - [Per-document vs per-page pricing →](https://txtfetch.com/blog/per-document-vs-per-page-pricing) - [Extract text from legacy .doc/.xls/.ppt →](https://txtfetch.com/extract/legacy-office) - [Extract text from .msg email →](https://txtfetch.com/extract/msg) - [Extract text from .eml email →](https://txtfetch.com/extract/email) - [Extract text from a scanned PDF →](https://txtfetch.com/extract/scanned-pdf) - [Fix: columns out of order →](https://txtfetch.com/fixes/columns-out-of-order) - [See how txtfetch compares →](https://txtfetch.com/compare/per-page-pricing) - [Get an API key →](https://app.txtfetch.com/signup) other-solutions - [RAG & LLM ingestion →](https://txtfetch.com/solutions/rag-ingestion) - [Search indexing →](https://txtfetch.com/solutions/search-indexing) - [Document workflows & automation →](https://txtfetch.com/solutions/document-workflows) - [Invoice & receipt processing →](https://txtfetch.com/solutions/invoice-and-receipt-processing) - [Resume & CV parsing →](https://txtfetch.com/solutions/resume-and-cv-parsing) - [Research & academic papers →](https://txtfetch.com/solutions/research-and-academic-papers) ## Start on the free plan. Run your own documents through it before you commit. The Hobby plan needs no card. [Get started →](https://app.txtfetch.com/signup) [See the pricing →](https://txtfetch.com/pricing)