Extract structured data from a document
Upload an unstructured file (PDF, Word document, or spreadsheet) and extract it against one or more of your templates. Reducto parses the file, an LLM designs a per-section extraction schema from the templates you name, and the extracted rows come back shaped like those templates.
The whole chain takes 30 seconds to several minutes, so this returns 202 immediately with an extraction_id. Poll GET /extractions/{extraction_id} until status is no longer processing.
This endpoint is stateless: nothing is written into your workbooks or sheets. The extracted rows are retained for 24 hours and then deleted. Capture them yourself if you need them longer.
curl --request POST \
--url https://app.vern.so/api/v1/extractions \
--header 'Content-Type: multipart/form-data' \
--header 'x-api-key: <api-key>' \
--form file='@example-file' \
--form 'templates[0]=lots' \
--form 'templates[1]=owners' \
--form 'instruction=<string>'import requests
url = "https://app.vern.so/api/v1/extractions"
files = { "file": ("example-file", open("example-file", "rb")) }
payload = {
"templates[0]": "lots",
"templates[1]": "owners",
"instruction": "<string>"
}
headers = {"x-api-key": "<api-key>"}
response = requests.post(url, data=payload, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('file', '<string>');
form.append('templates[0]', 'lots');
form.append('templates[1]', 'owners');
form.append('instruction', '<string>');
const options = {method: 'POST', headers: {'x-api-key': '<api-key>'}};
options.body = form;
fetch('https://app.vern.so/api/v1/extractions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.vern.so/api/v1/extractions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"templates%5B0%5D\"\r\n\r\nlots\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"templates%5B1%5D\"\r\n\r\nowners\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"instruction\"\r\n\r\n<string>\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Content-Type: multipart/form-data",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.vern.so/api/v1/extractions"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"templates%5B0%5D\"\r\n\r\nlots\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"templates%5B1%5D\"\r\n\r\nowners\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"instruction\"\r\n\r\n<string>\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.vern.so/api/v1/extractions")
.header("x-api-key", "<api-key>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"templates%5B0%5D\"\r\n\r\nlots\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"templates%5B1%5D\"\r\n\r\nowners\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"instruction\"\r\n\r\n<string>\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.vern.so/api/v1/extractions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"templates%5B0%5D\"\r\n\r\nlots\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"templates%5B1%5D\"\r\n\r\nowners\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"instruction\"\r\n\r\n<string>\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"extraction_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"status": "processing",
"file_name": "<string>",
"templates": [
"<string>"
],
"created_at": "2023-11-07T05:31:56Z",
"expires_at": "2023-11-07T05:31:56Z"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "Too many requests"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}Authorizations
Your Vern API key. Create one at Settings → API keys.
Body
The document to extract. Max 100 MB. Supported: pdf, docx, xlsx, xlsm, xls, xltx, xltm, csv.
Template slugs to extract against, as returned by GET /templates. Repeat the field or pass one comma-separated value. Omit to use every active template.
["lots", "owners"]
Optional free-text guidance for the extraction, e.g. "only the first schedule".
Response
Accepted. Poll the returned id for the result.
curl --request POST \
--url https://app.vern.so/api/v1/extractions \
--header 'Content-Type: multipart/form-data' \
--header 'x-api-key: <api-key>' \
--form file='@example-file' \
--form 'templates[0]=lots' \
--form 'templates[1]=owners' \
--form 'instruction=<string>'import requests
url = "https://app.vern.so/api/v1/extractions"
files = { "file": ("example-file", open("example-file", "rb")) }
payload = {
"templates[0]": "lots",
"templates[1]": "owners",
"instruction": "<string>"
}
headers = {"x-api-key": "<api-key>"}
response = requests.post(url, data=payload, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('file', '<string>');
form.append('templates[0]', 'lots');
form.append('templates[1]', 'owners');
form.append('instruction', '<string>');
const options = {method: 'POST', headers: {'x-api-key': '<api-key>'}};
options.body = form;
fetch('https://app.vern.so/api/v1/extractions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.vern.so/api/v1/extractions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"templates%5B0%5D\"\r\n\r\nlots\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"templates%5B1%5D\"\r\n\r\nowners\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"instruction\"\r\n\r\n<string>\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Content-Type: multipart/form-data",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.vern.so/api/v1/extractions"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"templates%5B0%5D\"\r\n\r\nlots\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"templates%5B1%5D\"\r\n\r\nowners\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"instruction\"\r\n\r\n<string>\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.vern.so/api/v1/extractions")
.header("x-api-key", "<api-key>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"templates%5B0%5D\"\r\n\r\nlots\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"templates%5B1%5D\"\r\n\r\nowners\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"instruction\"\r\n\r\n<string>\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.vern.so/api/v1/extractions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"templates%5B0%5D\"\r\n\r\nlots\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"templates%5B1%5D\"\r\n\r\nowners\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"instruction\"\r\n\r\n<string>\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"extraction_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"status": "processing",
"file_name": "<string>",
"templates": [
"<string>"
],
"created_at": "2023-11-07T05:31:56Z",
"expires_at": "2023-11-07T05:31:56Z"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "Too many requests"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}