Turn PDFs, scans, and Office files into clean text. Headings, lists, and tables come through as structure, not a jumble of lines, and understand mode keeps complex columns in reading order. Ready to index, search, or hand to an LLM or agent, and free to try with 5,000 credits each month.
Try it live
Pick a sample document and compare the rendered and raw Markdown with the original page. To run it on your own PDF, open Parse Studio (a temporary workspace needs no signup). The playground processes the first 10 pages by default.
Beyond copy and paste
A PDF stores text as positioned fragments, not as a story. Basic extractors read those fragments in storage order, and that’s where pipelines break:
Two-column pages turn into interleaved sentences. Nutrient’s understand mode returns reading order.
Rows and cells collapse into one line of numbers. Nutrient keeps tables as structure, returned as HTML tables in the Markdown.
Page furniture shows up in every chunk. In spatial output, each paragraph carries a role — such as title, section header, caption, or footer — so a pipeline can filter page furniture out.
Scanned pages and photos have no text layer. Nutrient’s OCR modes read them, in more than 100 languages.
In understand mode, multicolumn layouts and tables come back in reading order, not in whatever order the PDF happens to store them.
Structure, understand, and agentic modes run OCR on image-based pages in more than 100 languages, so a scanned contract and a digital report come back in the same shape.
One credit per page in text mode for born-digital PDFs. Step up to OCR modes only for the scans and complex layouts that need them.
Auditable output
In spatial output, Nutrient returns every element with a confidence score, its bounds, and its page. Your retrieval, agents, and reviewers can check any value against the source page.
Three steps
Every extraction is a single call to the Data Extraction API parse endpoint.
Create a free account for a key. In text mode, the free plan’s 5,000 monthly credits cover up to 5,000 born-digital pages.
Use text mode for born-digital PDFs, or structure or understand mode for scans, with Markdown as the output format.
The whole document comes back as one string in output.markdown, ready to save or send to the next step.
This example extracts the text from a PDF with the Data Extraction API. The text comes back in output.markdown; the JavaScript, Java, C#, and PHP samples write it to result.md. The Python sample prints it, and the shell samples print the full JSON response.
document.pdf. Then run the sample from the same folder.curl -X POST https://api.nutrient.io/extraction/parse \ -H "Authorization: Bearer your_api_key_goes_here" \ -F "file=@document.pdf" \ -F 'instructions={"mode":"text","output":{"format":"markdown"}}'curl -X POST https://api.nutrient.io/extraction/parse ^ -H "Authorization: Bearer your_api_key_goes_here" ^ -F "file=@document.pdf" ^ -F "instructions={\"mode\":\"text\",\"output\":{\"format\":\"markdown\"}}"package com.example.nutrient;
import java.io.File;import java.io.IOException;import java.nio.file.Files;import java.nio.file.Paths;
import org.json.JSONObject;
import okhttp3.MediaType;import okhttp3.MultipartBody;import okhttp3.OkHttpClient;import okhttp3.Request;import okhttp3.RequestBody;import okhttp3.Response;
public final class NutrientParseExample { public static void main(final String[] args) throws IOException { final RequestBody body = new MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart( "file", "document.pdf", RequestBody.create( MediaType.parse("application/pdf"), new File("document.pdf") ) ) .addFormDataPart( "instructions", new JSONObject() .put("mode", "text") .put("output", new JSONObject() .put("format", "markdown") ).toString() ) .build();
final Request request = new Request.Builder() .url("https://api.nutrient.io/extraction/parse") .method("POST", body) .addHeader("Authorization", "Bearer your_api_key_goes_here") .build();
final OkHttpClient client = new OkHttpClient().newBuilder().build(); final Response response = client.newCall(request).execute();
if (response.isSuccessful()) { // The response is JSON. The Markdown is in output.markdown. final JSONObject payload = new JSONObject(response.body().string()); final String markdown = payload .getJSONObject("output") .getString("markdown");
Files.writeString(Paths.get("result.md"), markdown); } else { throw new IOException(response.body().string()); } }}using System;using System.IO;using System.Text.Json;using RestSharp;
namespace NutrientParseDemo{ class Program { static void Main(string[] args) { var client = new RestClient("https://api.nutrient.io/extraction/parse");
var request = new RestRequest(Method.POST) .AddHeader("Authorization", "Bearer your_api_key_goes_here") .AddFile("file", "document.pdf") .AddParameter( "instructions", "{\"mode\":\"text\",\"output\":{\"format\":\"markdown\"}}" );
var response = client.Execute(request);
if (response.IsSuccessful) { // The response is JSON. The Markdown is in output.markdown. using var document = JsonDocument.Parse(response.Content); var markdown = document.RootElement .GetProperty("output") .GetProperty("markdown") .GetString();
File.WriteAllText("result.md", markdown); } else { Console.Write(response.Content); } } }}// This code requires Node.js. Do not run this code directly in a web browser.import fs from "node:fs";
const form = new FormData();form.append("file", await fs.openAsBlob("document.pdf"), "document.pdf");form.append( "instructions", JSON.stringify({ mode: "text", output: { format: "markdown" } }),);
const response = await fetch("https://api.nutrient.io/extraction/parse", { method: "POST", headers: { Authorization: "Bearer your_api_key_goes_here" }, body: form,});if (!response.ok) throw new Error(await response.text());
const result = await response.json();fs.writeFileSync("result.md", result.output.markdown);import requests
response = requests.post( "https://api.nutrient.io/extraction/parse", headers={"Authorization": "Bearer your_api_key_goes_here"}, files={"file": open("document.pdf", "rb")}, data={ "instructions": '{"mode":"text","output":{"format":"markdown"}}' },)response.raise_for_status()
result = response.json()print(result["output"]["markdown"])<?php
$curl = curl_init();
curl_setopt_array($curl, array( CURLOPT_URL => 'https://api.nutrient.io/extraction/parse', CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_POSTFIELDS => array( 'instructions' => '{"mode":"text","output":{"format":"markdown"}}', 'file' => new CURLFILE('document.pdf') ), CURLOPT_HTTPHEADER => array( 'Authorization: Bearer your_api_key_goes_here' ),));
$response = curl_exec($curl);$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
if ($status !== 200) { throw new Exception($response);}
// The response is JSON. The Markdown is in output.markdown.$payload = json_decode($response, true);
file_put_contents('result.md', $payload['output']['markdown']);Example output
The text comes back in output.markdown as one string, with headings, lists, and tables kept as structure rather than flattened into one block.
{ "status": 200, "requestId": "GNw3M_7TfNWolS8ABHbx", "output": { "markdown": "# ACME Bank Credit Card Limit Application\n\n\n<table>\n <tr>\n <th>Limit Increase</th>\n <th>Limit Decrease</th>\n </tr>\n <tr>\n <td>Limit/Month Requested €</td>\n <td>Term Requested</td>\n </tr>\n <tr>\n <td>15,000.00</td>\n <td>18 months</td>\n </tr>\n</table>\n\n\n## Application Agreement\n\n\nEach Signer has read and agrees to all applicable provisions\r\non the reverse side of this Application (whether or not that\r\nreverse side is faxed back to ACME Bank), including the\r\ngranting of a security interest in deposit accounts, and\r\nunderstands that this Application may be approved or\r\ndenied. ACME Bank is authorized to obtain credit records\r\nand other credit and employment information about the\n\n\n<table>\n <tr>\n <th>Officer</th>\n <td>449-09-0000</td>\n <td>05/10/1989</td>\n </tr>\n <tr>\n <th>First Name<br/>Joanna R.</th>\n <td>Email</td>\n <td>Credit Card №</td>\n </tr>\n <tr>\n <th>Last Name<br/>Simonitti</th>\n <td>jo.simonitti89@gmail.com</td>\n <td>5500 0000 0000 0004</td>\n </tr>\n <tr>\n <th>Phone Address</th>\n <td>City</td>\n <td>ZIP Code</td>\n </tr>\n <tr>\n <th>+1 214 291 4003 313 Stone Avenue</th>\n <td>New York City, NY</td>\n <td>10001</td>\n </tr>\n <tr>\n <th>Vehicle ownership VIN number</th>\n <td>Gross Annual Income €</td>\n <td>Net Worth €</td>\n </tr>\n <tr>\n <th>Yes, Ford Sedan JN8AZ1MU4CW113789</th>\n <td>328,000.00</td>\n <td>3,029,028.00</td>\n </tr>\n</table>\n\n\nSigners personally (now and in the future), including from\r\nstate and federal tax authorities, for deciding whether to\r\napprove the requested limit.\n\n\n## Applicant Information\n\n\n## Terms and Conditions\n\n\nSignature of Applicant\n\n\nSocial Security Number\n\n\nAs reported on last Tax Return\n\n\nDate of Birth\n\n\nExcluding business assets\n\n\nBy signing below, Signer agrees to be personally responsible for any credit\r\ngranted pursuant to this Application. For ACME Bank Cash Flow Manager and ACME\r\nBank Guaranteed Cash Flow Manager Lines of Credit, this Application constitutes\r\nGuaranty under which the individual signing is a Guarantor, and individually\r\nguarantees the payment of all present and future obligations of the Business to\r\nACME Bank in accordance with the provisions on the reverse side and the Terms and\r\nConditions and Letter Agreement governing such Line of Credit.\n\n\nDate of Signature\n\n\nPage 1/1\n\n\nhttps://www.pspdfkit.com" }, "metrics": { "pagesProcessed": 1, "processingTimeMs": 304 }, "usage": { "data_extraction_credits": { "cost": 1.0, "remainingCredits": 504999.0 } }, "configuration": { "apiVersion": "2026-05-25", "engine": {}, "mode": "text", "outputFormat": "markdown" }}OCR built in
Scanned PDFs and photos have no text layer, so text mode returns nothing for them. Switch to an OCR mode and the same call reads the page. OCR covers more than 100 languages, detected automatically or set per request with options.language — see the supported languages.
| Mode | Credits per page | OCR | Use it for |
|---|---|---|---|
| Text | 1 | No | Born-digital PDFs with a clean text layer; the fastest, cheapest route |
| Structure | 1.5 | Yes | Straightforward scans, with word-level bounds and confidence |
| Understand | 9 | Yes | Tables, forms, multicolumn layouts, formulas, and printed handwriting |
| Agentic | 18 | Yes | Cursive handwriting, faxes, degraded scans, and low-quality images |
Most pipelines start with text and grow from there. The same Data Extraction API returns:
As Markdown, for search indexes, RAG, and LLM prompts.
As spatial JSON, where every element carries its type, bounds, confidence, and page. See the PDF-to-JSON API.
Mapped to your own JSON Schema through the extract endpoint, each with a citation. See the Data Extraction API.
Nutrient is SOC 2 Type 2 audited annually. See the security documentation and privacy documentation for details on data handling.
All API communication uses HTTPS with TLS encryption. Unencrypted HTTP requests are rejected.
Most common next steps
Use the following:
Get started:
Platform resources:
Nutrient’s text extraction API is a REST endpoint that reads a PDF, scan, or Office file and returns its text as clean Markdown. It’s part of the Data Extraction API, so the same call can also return structure or specific fields when a pipeline needs more than text.
Nutrient extracts text in one request. Post the PDF to the Data Extraction API parse endpoint with a mode and Markdown output. Then read the text from output.markdown in the JSON response. This page has samples in shell, Windows shell, Python, JavaScript, Java, C#, and PHP.
Nutrient reads scanned PDFs and images with OCR: Send the file with structure, understand, or agentic mode instead of text mode. Text mode reads only born-digital PDFs, while the OCR modes cover more than 100 languages.
Yes, Nutrient’s understand mode reads printed handwriting, and agentic mode reads cursive and freeform handwriting, including on degraded scans and faxes.
Yes, Nutrient returns text in reading order in understand mode, so columns and tables don’t run together. Use understand mode for complex multicolumn layouts. Structure mode can miss multicolumn relationships.
Nutrient is built for when the answer has to be reliable at scale. Nutrient returns the full text of every page, with reading order, confidence, and page context in spatial output, as part of an auditable pipeline.
Nutrient’s Data Extraction API returns Markdown, which reads as plain text with light formatting for headings, lists, and tables. The whole document comes back as one string in output.markdown.
Yes, Nutrient’s Data Extraction API free plan includes 5,000 credits each month, with no credit card required. Text mode costs 1 credit per page, so that’s up to 5,000 born-digital pages a month. See Data Extraction API pricing.
Nutrient’s Data Extraction API accepts PDFs (digital, scanned, and image-based), images, and Office files, including Word, Excel, and PowerPoint.
Create an account to get your Data Extraction API key and start making API calls.