Production Extraction: Batch Processing, Polling, and Latency Management
Production patterns for the V2 Extract API: single-file extraction, concurrent batch processing, parse-then-extract reuse, timeouts, webhooks, and schema management.
This cookbook covers production patterns for LlamaExtract V2: extracting from single files, processing batches concurrently, composing parse-then-extract workflows, handling latency, and managing schemas programmatically.
Every example uses the V2 Extract API (client.extract), which accepts a file_input — either a File ID or Parse Job ID.
pip install llama-cloud>=2.1npm install @llamaindex/llama-cloudgo get github.com/run-llama/llama-parse-goimplementation("ai.llamaindex:llama-cloud:1.3.0")go install github.com/run-llama/llama-parse-cli/cmd/llp@latestConnect to Llama Cloud
Section titled “Connect to Llama Cloud”import osfrom llama_cloud import LlamaCloud, AsyncLlamaCloud
client = LlamaCloud(api_key=os.environ["LLAMA_CLOUD_API_KEY"])import LlamaCloud from "@llamaindex/llama-cloud";
const client = new LlamaCloud({ apiKey: process.env.LLAMA_CLOUD_API_KEY!,});import ( "context"
llamacloud "github.com/run-llama/llama-parse-go")
ctx := context.Background()client := llamacloud.NewClient() // reads LLAMA_CLOUD_API_KEY from the environmentimport ai.llamaindex.llamacloud.client.LlamaCloudClient;import ai.llamaindex.llamacloud.client.okhttp.LlamaCloudOkHttpClient;
LlamaCloudClient client = LlamaCloudOkHttpClient.fromEnv();export LLAMA_CLOUD_API_KEY=llx-xxxxxx1. Quick Start: Single File Extraction
Section titled “1. Quick Start: Single File Extraction”Upload a file, define a schema, and extract structured data.
from pydantic import BaseModel, Fieldfrom typing import Optionalimport time
# Define your schemaclass InvoiceData(BaseModel): vendor_name: str = Field(description="Name of the vendor or supplier") invoice_number: str = Field(description="Unique invoice identifier") total_amount: float = Field(description="Total amount due") currency: str = Field(description="Currency code (e.g. USD, EUR)") due_date: Optional[str] = Field(None, description="Payment due date")
# Upload the filefile_obj = client.files.create( file="./invoices/invoice_001.pdf", purpose="extract",)
# Create an extraction jobjob = client.extract.create( file_input=file_obj.id, configuration={ "data_schema": InvoiceData.model_json_schema(), "extraction_target": "per_doc", "tier": "cost_effective", },)
# Poll until completewhile job.status not in ("COMPLETED", "FAILED", "CANCELLED"): time.sleep(2) job = client.extract.get(job.id) print(f"Status: {job.status}")
if job.status == "COMPLETED": invoice = InvoiceData.model_validate(job.extract_result) print(f"Vendor: {invoice.vendor_name}") print(f"Total: {invoice.currency} {invoice.total_amount}")else: print(f"Job failed: {job.error_message}")import fs from "fs";import LlamaCloud from "@llamaindex/llama-cloud";
const client = new LlamaCloud({ apiKey: "your_api_key" });
// Upload the fileconst fileObj = await client.files.create({ file: fs.createReadStream("./invoices/invoice_001.pdf"), purpose: "extract",});
// Create an extraction joblet job = await client.extract.create({ file_input: fileObj.id, configuration: { data_schema: { type: "object", properties: { vendor_name: { type: "string", description: "Name of the vendor or supplier" }, invoice_number: { type: "string", description: "Unique invoice identifier" }, total_amount: { type: "number", description: "Total amount due" }, currency: { type: "string", description: "Currency code (e.g. USD, EUR)" }, due_date: { type: "string", description: "Payment due date", nullable: true }, }, required: ["vendor_name", "invoice_number", "total_amount", "currency"], }, extraction_target: "per_doc", tier: "cost_effective", },});
// Poll until completewhile (!["COMPLETED", "FAILED", "CANCELLED"].includes(job.status)) { await new Promise((r) => setTimeout(r, 2000)); job = await client.extract.get(job.id); console.log(`Status: ${job.status}`);}
if (job.status === "COMPLETED") { console.log("Extracted:", job.extract_result);}package main
import ( "context" "fmt" "log" "os" "time"
llamacloud "github.com/run-llama/llama-parse-go")
func main() { ctx := context.Background() client := llamacloud.NewClient()
// Define the schema as a JSON Schema literal dataSchema := map[string]*llamacloud.ExtractConfigurationDataSchemaUnionParam{ "type": {OfString: llamacloud.String("object")}, "properties": {OfAnyMap: map[string]any{ "vendor_name": map[string]any{"type": "string", "description": "Name of the vendor or supplier"}, "invoice_number": map[string]any{"type": "string", "description": "Unique invoice identifier"}, "total_amount": map[string]any{"type": "number", "description": "Total amount due"}, "currency": map[string]any{"type": "string", "description": "Currency code (e.g. USD, EUR)"}, "due_date": map[string]any{"type": "string", "description": "Payment due date"}, }}, "required": {OfAnyArray: []any{"vendor_name", "invoice_number", "total_amount", "currency"}}, }
// Upload the file f, err := os.Open("./invoices/invoice_001.pdf") if err != nil { log.Fatal(err) } defer f.Close()
fileObj, err := client.Files.New(ctx, llamacloud.FileNewParams{ File: f, Purpose: "extract", }) if err != nil { log.Fatal(err) }
// Create an extraction job job, err := client.Extract.New(ctx, llamacloud.ExtractNewParams{ ExtractV2JobCreate: llamacloud.ExtractV2JobCreateParam{ FileInput: fileObj.ID, Configuration: llamacloud.ExtractConfigurationParam{ DataSchema: dataSchema, ExtractionTarget: llamacloud.ExtractConfigurationExtractionTargetPerDoc, Tier: llamacloud.ExtractConfigurationTierCostEffective, }, }, }) if err != nil { log.Fatal(err) }
// Poll until complete for job.Status != "COMPLETED" && job.Status != "FAILED" && job.Status != "CANCELLED" { time.Sleep(2 * time.Second) job, err = client.Extract.Get(ctx, job.ID, llamacloud.ExtractGetParams{}) if err != nil { log.Fatal(err) } fmt.Printf("Status: %s\n", job.Status) }
if job.Status == "COMPLETED" { fmt.Println("Extracted:", job.ExtractResult.RawJSON()) } else { fmt.Printf("Job failed: %s\n", job.ErrorMessage) }}import ai.llamaindex.llamacloud.client.LlamaCloudClient;import ai.llamaindex.llamacloud.client.okhttp.LlamaCloudOkHttpClient;import ai.llamaindex.llamacloud.core.JsonValue;import ai.llamaindex.llamacloud.models.extract.ExtractConfiguration;import ai.llamaindex.llamacloud.models.extract.ExtractCreateParams;import ai.llamaindex.llamacloud.models.extract.ExtractGetParams;import ai.llamaindex.llamacloud.models.extract.ExtractV2Job;import ai.llamaindex.llamacloud.models.extract.ExtractV2JobCreate;import ai.llamaindex.llamacloud.models.files.FileCreateParams;import ai.llamaindex.llamacloud.models.files.FileCreateResponse;import java.nio.file.Paths;import java.util.Arrays;import java.util.Map;
LlamaCloudClient client = LlamaCloudOkHttpClient.fromEnv();
// Define the schema as a JSON Schema literalExtractConfiguration.DataSchema dataSchema = ExtractConfiguration.DataSchema.builder() .putAdditionalProperty("type", JsonValue.from("object")) .putAdditionalProperty("properties", JsonValue.from(Map.of( "vendor_name", Map.of("type", "string", "description", "Name of the vendor or supplier"), "invoice_number", Map.of("type", "string", "description", "Unique invoice identifier"), "total_amount", Map.of("type", "number", "description", "Total amount due"), "currency", Map.of("type", "string", "description", "Currency code (e.g. USD, EUR)"), "due_date", Map.of("type", "string", "description", "Payment due date")))) .putAdditionalProperty("required", JsonValue.from(Arrays.asList("vendor_name", "invoice_number", "total_amount", "currency"))) .build();
// Upload the fileFileCreateResponse fileObj = client.files().create( FileCreateParams.builder() .file(Paths.get("./invoices/invoice_001.pdf")) .purpose("extract") .build());
// Create an extraction jobExtractV2Job job = client.extract().create( ExtractCreateParams.builder() .extractV2JobCreate( ExtractV2JobCreate.builder() .fileInput(fileObj.id()) .configuration( ExtractConfiguration.builder() .dataSchema(dataSchema) .extractionTarget(ExtractConfiguration.ExtractionTarget.PER_DOC) .tier(ExtractConfiguration.Tier.COST_EFFECTIVE) .build()) .build()) .build());
// Poll until completewhile (!job.status().equals("COMPLETED") && !job.status().equals("FAILED") && !job.status().equals("CANCELLED")) { Thread.sleep(2000); job = client.extract().get(ExtractGetParams.builder().jobId(job.id()).build());}
if (job.status().equals("COMPLETED")) { System.out.println(job.extractResult());} else { System.out.println("Job failed: " + job.errorMessage().orElse("unknown error"));}# Define the schema as a JSON Schema literalDATA_SCHEMA='{ "type": "object", "properties": { "vendor_name": {"type": "string", "description": "Name of the vendor or supplier"}, "invoice_number": {"type": "string", "description": "Unique invoice identifier"}, "total_amount": {"type": "number", "description": "Total amount due"}, "currency": {"type": "string", "description": "Currency code (e.g. USD, EUR)"}, "due_date": {"type": "string", "description": "Payment due date"} }, "required": ["vendor_name", "invoice_number", "total_amount", "currency"]}'
# Upload the fileFILE_ID=$(llp files create --file ./invoices/invoice_001.pdf --purpose extract | jq -r '.id')
# Create an extraction jobJOB_ID=$(llp extract create \ --file-input "$FILE_ID" \ --configuration "{\"data_schema\": $DATA_SCHEMA, \"extraction_target\": \"per_doc\", \"tier\": \"cost_effective\"}" \ | jq -r '.id')
# Poll until completewhile true; do JOB=$(llp extract get --job-id "$JOB_ID") STATUS=$(echo "$JOB" | jq -r '.status') case "$STATUS" in COMPLETED|FAILED|CANCELLED) break ;; esac sleep 2done
echo "$JOB" | jq '.extract_result'2. Batch Extraction from Multiple Documents
Section titled “2. Batch Extraction from Multiple Documents”When you need to extract from many files, upload them all first, then create extraction jobs concurrently and poll them in parallel.
import asyncioimport timefrom pathlib import Pathfrom pydantic import BaseModel, Fieldfrom typing import Optionalfrom llama_cloud import AsyncLlamaCloud
async_client = AsyncLlamaCloud()
class InvoiceData(BaseModel): vendor_name: str = Field(description="Name of the vendor or supplier") invoice_number: str = Field(description="Unique invoice identifier") total_amount: float = Field(description="Total amount due") currency: str = Field(description="Currency code (e.g. USD, EUR)") due_date: Optional[str] = Field(None, description="Payment due date")
EXTRACT_CONFIG = { "data_schema": InvoiceData.model_json_schema(), "extraction_target": "per_doc", "tier": "cost_effective",}
async def upload_files(file_paths: list[Path]) -> list[str]: """Upload files concurrently and return file IDs.""" async def upload_one(path: Path) -> str: file_obj = await async_client.files.create( file=str(path), purpose="extract" ) return file_obj.id
file_ids = await asyncio.gather(*[upload_one(p) for p in file_paths]) return list(file_ids)
async def extract_one(file_id: str) -> dict: """Create an extract job for a single file and poll until done.""" job = await async_client.extract.create( file_input=file_id, configuration=EXTRACT_CONFIG, )
while job.status not in ("COMPLETED", "FAILED", "CANCELLED"): await asyncio.sleep(2) job = await async_client.extract.get(job.id)
if job.status == "COMPLETED": return {"file_id": file_id, "data": job.extract_result} else: return {"file_id": file_id, "error": job.error_message}
async def batch_extract(file_paths: list[Path], concurrency: int = 10) -> list[dict]: """Extract from multiple files with bounded concurrency.""" file_ids = await upload_files(file_paths) semaphore = asyncio.Semaphore(concurrency)
async def extract_with_limit(file_id: str) -> dict: async with semaphore: return await extract_one(file_id)
results = await asyncio.gather( *[extract_with_limit(fid) for fid in file_ids] ) return list(results)
# Usageasync def main(): invoice_files = list(Path("./invoices").glob("*.pdf")) print(f"Processing {len(invoice_files)} files...")
results = await batch_extract(invoice_files, concurrency=10)
succeeded = [r for r in results if "data" in r] failed = [r for r in results if "error" in r] print(f"Completed: {len(succeeded)}, Failed: {len(failed)}")
for result in succeeded: invoice = InvoiceData.model_validate(result["data"]) print(f" {invoice.vendor_name}: {invoice.currency} {invoice.total_amount}")
asyncio.run(main())import * as fs from "fs";import * as path from "path";
const EXTRACT_CONFIG = { data_schema: { type: "object", properties: { vendor_name: { type: "string", description: "Name of the vendor or supplier" }, invoice_number: { type: "string", description: "Unique invoice identifier" }, total_amount: { type: "number", description: "Total amount due" }, currency: { type: "string", description: "Currency code" }, }, required: ["vendor_name", "invoice_number", "total_amount", "currency"], }, extraction_target: "per_doc" as const, tier: "cost_effective" as const,};
async function extractOne(fileId: string) { let job = await client.extract.create({ file_input: fileId, configuration: EXTRACT_CONFIG, });
while (!["COMPLETED", "FAILED", "CANCELLED"].includes(job.status)) { await new Promise((r) => setTimeout(r, 2000)); job = await client.extract.get(job.id); }
return { fileId, status: job.status, data: job.extract_result, error: job.error_message };}
async function batchExtract(filePaths: string[], concurrency = 10) { // Upload all files const fileIds = await Promise.all( filePaths.map(async (fp) => { const fileObj = await client.files.create({ file: fs.createReadStream(fp), purpose: "extract", }); return fileObj.id; }) );
// Extract with bounded concurrency const results: any[] = []; for (let i = 0; i < fileIds.length; i += concurrency) { const batch = fileIds.slice(i, i + concurrency); const batchResults = await Promise.all(batch.map(extractOne)); results.push(...batchResults); } return results;}We can submit multiple files for extraction using concurrency control with a buffered channel:
import ( "sync")
// dataSchema is defined in the Quick Start above.extractConfig := llamacloud.ExtractConfigurationParam{ DataSchema: dataSchema, ExtractionTarget: llamacloud.ExtractConfigurationExtractionTargetPerDoc, Tier: llamacloud.ExtractConfigurationTierCostEffective,}
type extractResult struct { FileID string Data llamacloud.ExtractV2JobExtractResultUnion Err error}
// Upload one file and return its ID.uploadFile := func(path string) (string, error) { f, err := os.Open(path) if err != nil { return "", err } defer f.Close()
fileObj, err := client.Files.New(ctx, llamacloud.FileNewParams{ File: f, Purpose: "extract", }) if err != nil { return "", err } return fileObj.ID, nil}
// Create an extract job for one file and poll until done.extractOne := func(fileID string) extractResult { job, err := client.Extract.New(ctx, llamacloud.ExtractNewParams{ ExtractV2JobCreate: llamacloud.ExtractV2JobCreateParam{ FileInput: fileID, Configuration: extractConfig, }, }) if err != nil { return extractResult{FileID: fileID, Err: err} }
for job.Status != "COMPLETED" && job.Status != "FAILED" && job.Status != "CANCELLED" { time.Sleep(2 * time.Second) job, err = client.Extract.Get(ctx, job.ID, llamacloud.ExtractGetParams{}) if err != nil { return extractResult{FileID: fileID, Err: err} } } return extractResult{FileID: fileID, Data: job.ExtractResult}}
filePaths := []string{"./invoices/invoice_001.pdf", "./invoices/invoice_002.pdf"}sem := make(chan struct{}, 10) // Limit concurrency to 10results := make([]extractResult, len(filePaths))
var wg sync.WaitGroupfor i, path := range filePaths { wg.Add(1) go func(i int, path string) { defer wg.Done() sem <- struct{}{} defer func() { <-sem }()
fileID, err := uploadFile(path) if err != nil { results[i] = extractResult{Err: err} return } results[i] = extractOne(fileID) }(i, path)}wg.Wait()
succeeded, failed := 0, 0for _, r := range results { if r.Err != nil { failed++ } else { succeeded++ }}fmt.Printf("Completed: %d, Failed: %d\n", succeeded, failed)We can submit multiple files for extraction using concurrency control with a fixed thread pool:
import java.util.ArrayList;import java.util.List;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;
// dataSchema is defined in the Quick Start above.ExtractConfiguration extractConfig = ExtractConfiguration.builder() .dataSchema(dataSchema) .extractionTarget(ExtractConfiguration.ExtractionTarget.PER_DOC) .tier(ExtractConfiguration.Tier.COST_EFFECTIVE) .build();
ExecutorService pool = Executors.newFixedThreadPool(10); // Limit concurrency to 10List<String> filePaths = Arrays.asList("./invoices/invoice_001.pdf", "./invoices/invoice_002.pdf");List<Future<ExtractV2Job>> futures = new ArrayList<>();
for (String filePath : filePaths) { futures.add(pool.submit(() -> { FileCreateResponse fileObj = client.files().create( FileCreateParams.builder() .file(Paths.get(filePath)) .purpose("extract") .build());
ExtractV2Job job = client.extract().create( ExtractCreateParams.builder() .extractV2JobCreate( ExtractV2JobCreate.builder() .fileInput(fileObj.id()) .configuration(extractConfig) .build()) .build());
while (!job.status().equals("COMPLETED") && !job.status().equals("FAILED") && !job.status().equals("CANCELLED")) { Thread.sleep(2000); job = client.extract().get(ExtractGetParams.builder().jobId(job.id()).build()); } return job; }));}
for (Future<ExtractV2Job> future : futures) { System.out.println(future.get().extractResult());}pool.shutdown();We can submit multiple files for extraction using concurrency control with background jobs:
DATA_SCHEMA='{ "type": "object", "properties": { "vendor_name": {"type": "string", "description": "Name of the vendor or supplier"}, "invoice_number": {"type": "string", "description": "Unique invoice identifier"}, "total_amount": {"type": "number", "description": "Total amount due"}, "currency": {"type": "string", "description": "Currency code (e.g. USD, EUR)"}, "due_date": {"type": "string", "description": "Payment due date"} }, "required": ["vendor_name", "invoice_number", "total_amount", "currency"]}'EXTRACT_CONFIG="{\"data_schema\": $DATA_SCHEMA, \"extraction_target\": \"per_doc\", \"tier\": \"cost_effective\"}"
CONCURRENCY=10WORK_DIR=$(mktemp -d)
# Block until fewer than $CONCURRENCY background jobs are running.throttle() { while (( $(jobs -rp | wc -l) >= CONCURRENCY )); do sleep 1; done}
# Upload one file and record its ID.upload_one() { llp files create --file "$1" --purpose extract \ | jq -r '.id' > "$WORK_DIR/$(basename "$1").file_id"}
# Create an extract job for a single file and poll until done.extract_one() { local name="$1" file_id="$2" job job_id status
job_id=$(llp extract create \ --file-input "$file_id" \ --configuration "$EXTRACT_CONFIG" \ | jq -r '.id')
while true; do job=$(llp extract get --job-id "$job_id") status=$(echo "$job" | jq -r '.status') case "$status" in COMPLETED|FAILED|CANCELLED) break ;; esac sleep 2 done
if [ "$status" = "COMPLETED" ]; then echo "$job" | jq -c --arg file "$name" '{file: $file, data: .extract_result}' else echo "$job" | jq -c --arg file "$name" '{file: $file, error: .error_message}' fi > "$WORK_DIR/$name.result"}
# Upload files concurrentlyINVOICE_FILES=(./invoices/*.pdf)echo "Processing ${#INVOICE_FILES[@]} files..."for path in "${INVOICE_FILES[@]}"; do throttle upload_one "$path" &donewait
# Extract from every uploaded file with bounded concurrencyfor path in "${INVOICE_FILES[@]}"; do name=$(basename "$path") throttle extract_one "$name" "$(cat "$WORK_DIR/$name.file_id")" &donewait
SUCCEEDED=$(jq -s '[.[] | select(.data)] | length' "$WORK_DIR"/*.result)FAILED=$(jq -s '[.[] | select(.error)] | length' "$WORK_DIR"/*.result)echo "Completed: $SUCCEEDED, Failed: $FAILED"
jq -r 'select(.data) | " \(.data.vendor_name): \(.data.currency) \(.data.total_amount)"' \ "$WORK_DIR"/*.resultRate Limiting Considerations
Section titled “Rate Limiting Considerations”- Concurrency: Start with 10 concurrent jobs and adjust based on your plan limits. If you see
THROTTLEDstatus, reduce concurrency. - Credits:
cost_effectivetier costs 5 credits per page.agentictier costs 15 credits per page. Plan your budget for large batches. - File upload: The file upload endpoint has its own rate limits. Upload files before creating extract jobs to separate the two bottlenecks.
3. Parse-Then-Extract: Composable Workflow
Section titled “3. Parse-Then-Extract: Composable Workflow”When you need fine-grained control over parsing, or want to parse once and extract with different schemas, use the parse-then-extract pattern. Parse the document first, then pass the parse_job_id to extraction.
This avoids re-parsing the same document for each extraction configuration, saving both time and credits.
from pydantic import BaseModel, Fieldfrom typing import Optionalimport time
# Step 1: Parse the documentparse_job = client.parsing.create( tier="agentic", version="latest", upload_file="./contracts/master_agreement.pdf",)
# Wait for parse to completeparse_result = client.parsing.wait_for_completion( parse_job.id, verbose=True)print(f"Parse complete: {parse_result.status}")
# Step 2: Extract with the first schema (contract metadata)class ContractMetadata(BaseModel): parties: list[str] = Field(description="Names of all contracting parties") effective_date: str = Field(description="Contract effective date") termination_date: Optional[str] = Field(None, description="Contract end date") governing_law: str = Field(description="Governing law jurisdiction")
metadata_job = client.extract.create( file_input=parse_job.id, configuration={ "data_schema": ContractMetadata.model_json_schema(), "extraction_target": "per_doc", "tier": "agentic", "cite_sources": True, },)
while metadata_job.status not in ("COMPLETED", "FAILED", "CANCELLED"): time.sleep(2) metadata_job = client.extract.get(metadata_job.id)
metadata = ContractMetadata.model_validate(metadata_job.extract_result)print(f"Parties: {metadata.parties}")print(f"Governing law: {metadata.governing_law}")
# Step 3: Extract with a second schema (financial terms) from the SAME parseclass FinancialTerms(BaseModel): total_value: Optional[float] = Field(None, description="Total contract value") payment_schedule: Optional[str] = Field(None, description="Payment frequency and terms") penalties: Optional[str] = Field(None, description="Late payment or breach penalties")
financial_job = client.extract.create( file_input=parse_job.id, # Reuse the same parse result configuration={ "data_schema": FinancialTerms.model_json_schema(), "extraction_target": "per_doc", "tier": "agentic", },)
while financial_job.status not in ("COMPLETED", "FAILED", "CANCELLED"): time.sleep(2) financial_job = client.extract.get(financial_job.id)
terms = FinancialTerms.model_validate(financial_job.extract_result)print(f"Contract value: {terms.total_value}")print(f"Payment schedule: {terms.payment_schedule}")// Step 1: Parse the documentlet parseJob = await client.parsing.create({ tier: "agentic", version: "latest", upload_file: fs.createReadStream("./contracts/master_agreement.pdf"),});
// Wait for parse to completeparseJob = await client.parsing.waitForCompletion(parseJob.id);console.log(`Parse complete: ${parseJob.status}`);
// Step 2: Extract contract metadata from parse resultlet metadataJob = await client.extract.create({ file_input: parseJob.id, configuration: { data_schema: { type: "object", properties: { parties: { type: "array", items: { type: "string" }, description: "Names of all contracting parties" }, effective_date: { type: "string", description: "Contract effective date" }, governing_law: { type: "string", description: "Governing law jurisdiction" }, }, required: ["parties", "effective_date", "governing_law"], }, extraction_target: "per_doc", tier: "agentic", cite_sources: true, },});
while (!["COMPLETED", "FAILED", "CANCELLED"].includes(metadataJob.status)) { await new Promise((r) => setTimeout(r, 2000)); metadataJob = await client.extract.get(metadataJob.id);}console.log("Contract metadata:", metadataJob.extract_result);
// Step 3: Extract financial terms from the SAME parse resultlet financialJob = await client.extract.create({ file_input: parseJob.id, // Reuse the same parse result configuration: { data_schema: { type: "object", properties: { total_value: { type: "number", description: "Total contract value", nullable: true }, payment_schedule: { type: "string", description: "Payment frequency and terms", nullable: true }, }, }, extraction_target: "per_doc", tier: "agentic", },});
while (!["COMPLETED", "FAILED", "CANCELLED"].includes(financialJob.status)) { await new Promise((r) => setTimeout(r, 2000)); financialJob = await client.extract.get(financialJob.id);}console.log("Financial terms:", financialJob.extract_result);// Step 1: Parse the documentf, err := os.Open("./contracts/master_agreement.pdf")if err != nil { log.Fatal(err)}defer f.Close()
parseFile, err := client.Files.New(ctx, llamacloud.FileNewParams{ File: f, Purpose: "parse",})if err != nil { log.Fatal(err)}
parseJob, err := client.Parsing.New(ctx, llamacloud.ParsingNewParams{ Tier: llamacloud.ParsingNewParamsTierAgentic, Version: llamacloud.ParsingNewParamsVersionLatest, FileID: llamacloud.String(parseFile.ID),})if err != nil { log.Fatal(err)}
// Wait for parse to completeparseResult, err := client.Parsing.Get(ctx, parseJob.ID, llamacloud.ParsingGetParams{})if err != nil { log.Fatal(err)}for parseResult.Job.Status != "COMPLETED" && parseResult.Job.Status != "FAILED" && parseResult.Job.Status != "CANCELLED" { time.Sleep(2 * time.Second) parseResult, err = client.Parsing.Get(ctx, parseJob.ID, llamacloud.ParsingGetParams{}) if err != nil { log.Fatal(err) }}fmt.Printf("Parse complete: %s\n", parseResult.Job.Status)
// Step 2: Extract contract metadata from the parse resultmetadataSchema := map[string]*llamacloud.ExtractConfigurationDataSchemaUnionParam{ "type": {OfString: llamacloud.String("object")}, "properties": {OfAnyMap: map[string]any{ "parties": map[string]any{"type": "array", "items": map[string]any{"type": "string"}, "description": "Names of all contracting parties"}, "effective_date": map[string]any{"type": "string", "description": "Contract effective date"}, "governing_law": map[string]any{"type": "string", "description": "Governing law jurisdiction"}, }}, "required": {OfAnyArray: []any{"parties", "effective_date", "governing_law"}},}
metadataJob, err := client.Extract.New(ctx, llamacloud.ExtractNewParams{ ExtractV2JobCreate: llamacloud.ExtractV2JobCreateParam{ FileInput: parseJob.ID, Configuration: llamacloud.ExtractConfigurationParam{ DataSchema: metadataSchema, ExtractionTarget: llamacloud.ExtractConfigurationExtractionTargetPerDoc, Tier: llamacloud.ExtractConfigurationTierAgentic, CiteSources: llamacloud.Bool(true), }, },})if err != nil { log.Fatal(err)}
for metadataJob.Status != "COMPLETED" && metadataJob.Status != "FAILED" && metadataJob.Status != "CANCELLED" { time.Sleep(2 * time.Second) metadataJob, err = client.Extract.Get(ctx, metadataJob.ID, llamacloud.ExtractGetParams{}) if err != nil { log.Fatal(err) }}fmt.Println("Contract metadata:", metadataJob.ExtractResult.RawJSON())
// Step 3: Extract financial terms from the SAME parse resultfinancialSchema := map[string]*llamacloud.ExtractConfigurationDataSchemaUnionParam{ "type": {OfString: llamacloud.String("object")}, "properties": {OfAnyMap: map[string]any{ "total_value": map[string]any{"type": "number", "description": "Total contract value"}, "payment_schedule": map[string]any{"type": "string", "description": "Payment frequency and terms"}, }},}
financialJob, err := client.Extract.New(ctx, llamacloud.ExtractNewParams{ ExtractV2JobCreate: llamacloud.ExtractV2JobCreateParam{ FileInput: parseJob.ID, // Reuse the same parse result Configuration: llamacloud.ExtractConfigurationParam{ DataSchema: financialSchema, ExtractionTarget: llamacloud.ExtractConfigurationExtractionTargetPerDoc, Tier: llamacloud.ExtractConfigurationTierAgentic, }, },})if err != nil { log.Fatal(err)}
for financialJob.Status != "COMPLETED" && financialJob.Status != "FAILED" && financialJob.Status != "CANCELLED" { time.Sleep(2 * time.Second) financialJob, err = client.Extract.Get(ctx, financialJob.ID, llamacloud.ExtractGetParams{}) if err != nil { log.Fatal(err) }}fmt.Println("Financial terms:", financialJob.ExtractResult.RawJSON())import ai.llamaindex.llamacloud.models.parsing.ParsingCreateParams;import ai.llamaindex.llamacloud.models.parsing.ParsingCreateResponse;import ai.llamaindex.llamacloud.models.parsing.ParsingGetParams;import ai.llamaindex.llamacloud.models.parsing.ParsingGetResponse;
// Step 1: Parse the documentFileCreateResponse parseFile = client.files().create( FileCreateParams.builder() .file(Paths.get("./contracts/master_agreement.pdf")) .purpose("parse") .build());
ParsingCreateResponse parseJob = client.parsing().create( ParsingCreateParams.builder() .tier(ParsingCreateParams.Tier.AGENTIC) .version(ParsingCreateParams.Version.LATEST) .fileId(parseFile.id()) .build());
// Wait for parse to completeParsingGetResponse parseResult = client.parsing().get( ParsingGetParams.builder().jobId(parseJob.id()).build());while (!parseResult.job().status().equals(ParsingGetResponse.Job.Status.COMPLETED) && !parseResult.job().status().equals(ParsingGetResponse.Job.Status.FAILED) && !parseResult.job().status().equals(ParsingGetResponse.Job.Status.CANCELLED)) { Thread.sleep(2000); parseResult = client.parsing().get( ParsingGetParams.builder().jobId(parseJob.id()).build());}System.out.println("Parse complete: " + parseResult.job().status());
// Step 2: Extract contract metadata from the parse resultExtractConfiguration.DataSchema metadataSchema = ExtractConfiguration.DataSchema.builder() .putAdditionalProperty("type", JsonValue.from("object")) .putAdditionalProperty("properties", JsonValue.from(Map.of( "parties", Map.of("type", "array", "items", Map.of("type", "string"), "description", "Names of all contracting parties"), "effective_date", Map.of("type", "string", "description", "Contract effective date"), "governing_law", Map.of("type", "string", "description", "Governing law jurisdiction")))) .putAdditionalProperty("required", JsonValue.from(Arrays.asList("parties", "effective_date", "governing_law"))) .build();
ExtractV2Job metadataJob = client.extract().create( ExtractCreateParams.builder() .extractV2JobCreate( ExtractV2JobCreate.builder() .fileInput(parseJob.id()) .configuration( ExtractConfiguration.builder() .dataSchema(metadataSchema) .extractionTarget(ExtractConfiguration.ExtractionTarget.PER_DOC) .tier(ExtractConfiguration.Tier.AGENTIC) .citeSources(true) .build()) .build()) .build());
while (!metadataJob.status().equals("COMPLETED") && !metadataJob.status().equals("FAILED") && !metadataJob.status().equals("CANCELLED")) { Thread.sleep(2000); metadataJob = client.extract().get(ExtractGetParams.builder().jobId(metadataJob.id()).build());}System.out.println("Contract metadata: " + metadataJob.extractResult());
// Step 3: Extract financial terms from the SAME parse resultExtractConfiguration.DataSchema financialSchema = ExtractConfiguration.DataSchema.builder() .putAdditionalProperty("type", JsonValue.from("object")) .putAdditionalProperty("properties", JsonValue.from(Map.of( "total_value", Map.of("type", "number", "description", "Total contract value"), "payment_schedule", Map.of("type", "string", "description", "Payment frequency and terms")))) .build();
ExtractV2Job financialJob = client.extract().create( ExtractCreateParams.builder() .extractV2JobCreate( ExtractV2JobCreate.builder() .fileInput(parseJob.id()) // Reuse the same parse result .configuration( ExtractConfiguration.builder() .dataSchema(financialSchema) .extractionTarget(ExtractConfiguration.ExtractionTarget.PER_DOC) .tier(ExtractConfiguration.Tier.AGENTIC) .build()) .build()) .build());
while (!financialJob.status().equals("COMPLETED") && !financialJob.status().equals("FAILED") && !financialJob.status().equals("CANCELLED")) { Thread.sleep(2000); financialJob = client.extract().get(ExtractGetParams.builder().jobId(financialJob.id()).build());}System.out.println("Financial terms: " + financialJob.extractResult());# Step 1: Parse the documentPARSE_FILE_ID=$(llp files create --file ./contracts/master_agreement.pdf --purpose parse | jq -r '.id')
PARSE_JOB_ID=$(llp parsing create \ --tier agentic \ --version latest \ --file-id "$PARSE_FILE_ID" \ | jq -r '.id')
# Wait for parse to completewhile true; do STATUS=$(llp parsing get --job-id "$PARSE_JOB_ID" | jq -r '.job.status') case "$STATUS" in COMPLETED|FAILED|CANCELLED) break ;; esac sleep 2done
# Step 2: Extract contract metadata from the parse resultMETADATA_SCHEMA='{ "type": "object", "properties": { "parties": {"type": "array", "items": {"type": "string"}, "description": "Names of all contracting parties"}, "effective_date": {"type": "string", "description": "Contract effective date"}, "governing_law": {"type": "string", "description": "Governing law jurisdiction"} }, "required": ["parties", "effective_date", "governing_law"]}'
METADATA_JOB_ID=$(llp extract create \ --file-input "$PARSE_JOB_ID" \ --configuration "{\"data_schema\": $METADATA_SCHEMA, \"extraction_target\": \"per_doc\", \"tier\": \"agentic\", \"cite_sources\": true}" \ | jq -r '.id')
while true; do JOB=$(llp extract get --job-id "$METADATA_JOB_ID") STATUS=$(echo "$JOB" | jq -r '.status') case "$STATUS" in COMPLETED|FAILED|CANCELLED) break ;; esac sleep 2doneecho "$JOB" | jq '.extract_result'
# Step 3: Extract financial terms from the SAME parse resultFINANCIAL_SCHEMA='{ "type": "object", "properties": { "total_value": {"type": "number", "description": "Total contract value"}, "payment_schedule": {"type": "string", "description": "Payment frequency and terms"} }}'
FINANCIAL_JOB_ID=$(llp extract create \ --file-input "$PARSE_JOB_ID" \ --configuration "{\"data_schema\": $FINANCIAL_SCHEMA, \"extraction_target\": \"per_doc\", \"tier\": \"agentic\"}" \ | jq -r '.id')
while true; do JOB=$(llp extract get --job-id "$FINANCIAL_JOB_ID") STATUS=$(echo "$JOB" | jq -r '.status') case "$STATUS" in COMPLETED|FAILED|CANCELLED) break ;; esac sleep 2doneecho "$JOB" | jq '.extract_result'Page Targeting
Section titled “Page Targeting”Extract from specific pages using target_pages. This is useful for long documents where you only need data from certain pages.
job = client.extract.create( file_input=file_obj.id, configuration={ "data_schema": InvoiceData.model_json_schema(), "extraction_target": "per_doc", "tier": "cost_effective", "target_pages": "1,3,5-7", # Pages 1, 3, 5, 6, 7 },)// invoiceSchema defined earlier (see Quick Start above)const job = await client.extract.create({ file_input: fileObj.id, configuration: { data_schema: invoiceSchema, extraction_target: "per_doc", tier: "cost_effective", target_pages: "1,3,5-7", // Pages 1, 3, 5, 6, 7 },});// dataSchema and fileObj are defined in the Quick Start above.job, err := client.Extract.New(ctx, llamacloud.ExtractNewParams{ ExtractV2JobCreate: llamacloud.ExtractV2JobCreateParam{ FileInput: fileObj.ID, Configuration: llamacloud.ExtractConfigurationParam{ DataSchema: dataSchema, ExtractionTarget: llamacloud.ExtractConfigurationExtractionTargetPerDoc, Tier: llamacloud.ExtractConfigurationTierCostEffective, TargetPages: llamacloud.String("1,3,5-7"), // Pages 1, 3, 5, 6, 7 }, },})// dataSchema and fileObj are defined in the Quick Start above.ExtractV2Job job = client.extract().create( ExtractCreateParams.builder() .extractV2JobCreate( ExtractV2JobCreate.builder() .fileInput(fileObj.id()) .configuration( ExtractConfiguration.builder() .dataSchema(dataSchema) .extractionTarget(ExtractConfiguration.ExtractionTarget.PER_DOC) .tier(ExtractConfiguration.Tier.COST_EFFECTIVE) .targetPages("1,3,5-7") // Pages 1, 3, 5, 6, 7 .build()) .build()) .build());llp extract create \ --file-input "$FILE_ID" \ --configuration "{\"data_schema\": $DATA_SCHEMA, \"extraction_target\": \"per_doc\", \"tier\": \"cost_effective\", \"target_pages\": \"1,3,5-7\"}"Pages are 1-indexed. Supported formats: "1", "1,3", "1-5", "1,3,5-7,9". You are only billed for pages extracted.
Fetching Metadata with Expand
Section titled “Fetching Metadata with Expand”By default, extract_metadata (citations, field-level confidence) and metadata (usage stats) are not included in the response. Use the expand query parameter to request them.
# Get job with metadatajob = client.extract.get(job.id, expand=["extract_metadata", "metadata"])
if job.metadata and job.metadata.usage: usage = job.metadata.usage print(f"Pages extracted: {usage.num_pages_extracted}") print(f"Output tokens: {usage.num_output_tokens}")// Get job with metadataconst job = await client.extract.get(jobId, { expand: ["extract_metadata", "metadata"],});
if (job.metadata?.usage) { console.log(`Pages extracted: ${job.metadata.usage.num_pages_extracted}`);}// Get job with metadatajob, err = client.Extract.Get(ctx, job.ID, llamacloud.ExtractGetParams{ Expand: []string{"extract_metadata", "metadata"},})if err != nil { log.Fatal(err)}
if job.Metadata.Usage.NumPagesExtracted > 0 { fmt.Printf("Pages extracted: %d\n", job.Metadata.Usage.NumPagesExtracted)}// Get job with metadatajob = client.extract().get( ExtractGetParams.builder() .jobId(job.id()) .addExpand("extract_metadata") .addExpand("metadata") .build());
job.metadata() .flatMap(metadata -> metadata.usage()) .flatMap(usage -> usage.numPagesExtracted()) .ifPresent(pages -> System.out.println("Pages extracted: " + pages));# Get job with metadataJOB=$(llp extract get \ --job-id "$JOB_ID" \ --expand extract_metadata \ --expand metadata)
echo "$JOB" | jq -r '.metadata.usage.num_pages_extracted'When to Use Parse-Then-Extract
Section titled “When to Use Parse-Then-Extract”| Scenario | Approach |
|---|---|
| One file, one schema | Use file_input with file ID directly |
| One file, multiple schemas | Parse once, then extract with each schema via file_input with parse job ID |
| Need to inspect parse quality before extracting | Parse first, review, then extract |
| Batch of files with same schema | Use file ID with batch pattern from Section 2 |
| Only need specific pages | Add target_pages to configuration |
4. Handling Latency
Section titled “4. Handling Latency”Expected Latency and Cost by Tier
Section titled “Expected Latency and Cost by Tier”| Tier | Credits/Page | Typical Latency | Best For |
|---|---|---|---|
cost_effective | 4 | 5-30 seconds | High-volume, simpler documents |
agentic | 15 | 15-90 seconds | Complex documents, higher accuracy |
Latency scales with document length. A 100-page PDF on the agentic tier will take longer than a 2-page invoice.
Client-Side Timeout Pattern
Section titled “Client-Side Timeout Pattern”Wrap your polling loop with a timeout to avoid waiting indefinitely.
import time
def extract_with_timeout( client, file_id: str, configuration: dict, timeout_seconds: float = 300, poll_interval: float = 2.0,) -> dict: """Extract with a client-side timeout.""" job = client.extract.create( file_input=file_id, configuration=configuration, )
start = time.monotonic() while job.status not in ("COMPLETED", "FAILED", "CANCELLED"): elapsed = time.monotonic() - start if elapsed > timeout_seconds: raise TimeoutError( f"Extraction job {job.id} did not complete within " f"{timeout_seconds}s (last status: {job.status})" ) time.sleep(poll_interval) job = client.extract.get(job.id)
if job.status != "COMPLETED": raise RuntimeError(f"Job {job.id} ended with status: {job.status} - {job.error_message}")
return job.extract_result
# Usagetry: result = extract_with_timeout( client, file_id=file_obj.id, configuration=EXTRACT_CONFIG, timeout_seconds=120, ) print("Extracted:", result)except TimeoutError as e: print(f"Timed out: {e}")except RuntimeError as e: print(f"Failed: {e}")async function extractWithTimeout( fileId: string, configuration: any, timeoutMs: number = 300_000, pollIntervalMs: number = 2_000) { let job = await client.extract.create({ file_input: fileId, configuration, });
const start = Date.now(); while (!["COMPLETED", "FAILED", "CANCELLED"].includes(job.status)) { if (Date.now() - start > timeoutMs) { throw new Error( `Job ${job.id} did not complete within ${timeoutMs}ms (last status: ${job.status})` ); } await new Promise((r) => setTimeout(r, pollIntervalMs)); job = await client.extract.get(job.id); }
if (job.status !== "COMPLETED") { throw new Error(`Job ${job.id} failed: ${job.status} - ${job.error_message}`); } return job.extract_result;}// dataSchema and fileObj are defined in the Quick Start above.extractConfig := llamacloud.ExtractConfigurationParam{ DataSchema: dataSchema, ExtractionTarget: llamacloud.ExtractConfigurationExtractionTargetPerDoc, Tier: llamacloud.ExtractConfigurationTierCostEffective,}
extractWithTimeout := func(fileID string, timeout time.Duration) (llamacloud.ExtractV2JobExtractResultUnion, error) { var empty llamacloud.ExtractV2JobExtractResultUnion
job, err := client.Extract.New(ctx, llamacloud.ExtractNewParams{ ExtractV2JobCreate: llamacloud.ExtractV2JobCreateParam{ FileInput: fileID, Configuration: extractConfig, }, }) if err != nil { return empty, err }
deadline := time.Now().Add(timeout) for job.Status != "COMPLETED" && job.Status != "FAILED" && job.Status != "CANCELLED" { if time.Now().After(deadline) { return empty, fmt.Errorf("extraction job %s did not complete within %s (last status: %s)", job.ID, timeout, job.Status) } time.Sleep(2 * time.Second) job, err = client.Extract.Get(ctx, job.ID, llamacloud.ExtractGetParams{}) if err != nil { return empty, err } }
if job.Status != "COMPLETED" { return empty, fmt.Errorf("job %s ended with status %s: %s", job.ID, job.Status, job.ErrorMessage) } return job.ExtractResult, nil}
result, err := extractWithTimeout(fileObj.ID, 120*time.Second)if err != nil { log.Printf("Failed: %v", err)} else { fmt.Println("Extracted:", result.RawJSON())}// dataSchema and fileObj are defined in the Quick Start above.ExtractConfiguration extractConfig = ExtractConfiguration.builder() .dataSchema(dataSchema) .extractionTarget(ExtractConfiguration.ExtractionTarget.PER_DOC) .tier(ExtractConfiguration.Tier.COST_EFFECTIVE) .build();
ExtractV2Job job = client.extract().create( ExtractCreateParams.builder() .extractV2JobCreate( ExtractV2JobCreate.builder() .fileInput(fileObj.id()) .configuration(extractConfig) .build()) .build());
long timeoutMs = 120_000;long start = System.currentTimeMillis();while (!job.status().equals("COMPLETED") && !job.status().equals("FAILED") && !job.status().equals("CANCELLED")) { if (System.currentTimeMillis() - start > timeoutMs) { throw new IllegalStateException( "Extraction job " + job.id() + " did not complete within " + timeoutMs + "ms (last status: " + job.status() + ")"); } Thread.sleep(2000); job = client.extract().get(ExtractGetParams.builder().jobId(job.id()).build());}
if (!job.status().equals("COMPLETED")) { throw new IllegalStateException( "Job " + job.id() + " failed: " + job.status() + " - " + job.errorMessage().orElse(""));}System.out.println("Extracted: " + job.extractResult());TIMEOUT=120 # seconds
JOB_ID=$(llp extract create \ --file-input "$FILE_ID" \ --configuration "{\"data_schema\": $DATA_SCHEMA, \"extraction_target\": \"per_doc\", \"tier\": \"cost_effective\"}" \ | jq -r '.id')
START=$(date +%s)while true; do JOB=$(llp extract get --job-id "$JOB_ID") STATUS=$(echo "$JOB" | jq -r '.status') case "$STATUS" in COMPLETED|FAILED|CANCELLED) break ;; esac if [ $(( $(date +%s) - START )) -gt "$TIMEOUT" ]; then echo "Timed out after ${TIMEOUT}s (last status: $STATUS)" >&2 exit 1 fi sleep 2done
if [ "$STATUS" = "COMPLETED" ]; then echo "$JOB" | jq '.extract_result'else echo "Job failed: $STATUS" >&2fiUnderstanding THROTTLED Status
Section titled “Understanding THROTTLED Status”When the system is under load, your job may enter THROTTLED status before transitioning to RUNNING. This is normal. The job will proceed once capacity is available. Do not cancel and retry throttled jobs. That creates more load and pushes your job to the back of the queue.
PENDING → THROTTLED → RUNNING → COMPLETEDWebhook Integration (Alternative to Polling)
Section titled “Webhook Integration (Alternative to Polling)”For production systems where you don’t want long-lived polling connections, configure webhooks to receive notifications when jobs complete.
job = client.extract.create( file_input=file_obj.id, configuration=EXTRACT_CONFIG, webhook_configurations=[ { "webhook_url": "https://your-api.example.com/webhooks/extract", "webhook_events": ["extract.success", "extract.error"], "webhook_headers": { "Authorization": "Bearer your-webhook-secret", }, } ],)
# No polling needed. Your webhook endpoint receives:# POST https://your-api.example.com/webhooks/extract# {# "event": "extract.success",# "job_id": "...",# "status": "COMPLETED",# ...# }const job = await client.extract.create({ file_input: fileObj.id, configuration: EXTRACT_CONFIG, webhook_configurations: [ { webhook_url: "https://your-api.example.com/webhooks/extract", webhook_events: ["extract.success", "extract.error"], webhook_headers: { Authorization: "Bearer your-webhook-secret", }, }, ],});
// Your webhook endpoint receives a POST when the job completes// dataSchema and fileObj are defined in the Quick Start above.job, err := client.Extract.New(ctx, llamacloud.ExtractNewParams{ ExtractV2JobCreate: llamacloud.ExtractV2JobCreateParam{ FileInput: fileObj.ID, Configuration: llamacloud.ExtractConfigurationParam{ DataSchema: dataSchema, ExtractionTarget: llamacloud.ExtractConfigurationExtractionTargetPerDoc, Tier: llamacloud.ExtractConfigurationTierCostEffective, }, WebhookConfigurations: []llamacloud.ExtractV2JobCreateWebhookConfigurationParam{ { WebhookURL: llamacloud.String("https://your-api.example.com/webhooks/extract"), WebhookEvents: []string{"extract.success", "extract.error"}, WebhookHeaders: map[string]string{ "Authorization": "Bearer your-webhook-secret", }, }, }, },})if err != nil { log.Fatal(err)}
// No polling needed. Your webhook endpoint receives a POST when the job completes.fmt.Println("Submitted job:", job.ID)// dataSchema and fileObj are defined in the Quick Start above.ExtractConfiguration extractConfig = ExtractConfiguration.builder() .dataSchema(dataSchema) .extractionTarget(ExtractConfiguration.ExtractionTarget.PER_DOC) .tier(ExtractConfiguration.Tier.COST_EFFECTIVE) .build();
ExtractV2JobCreate.WebhookConfiguration webhook = ExtractV2JobCreate.WebhookConfiguration.builder() .webhookUrl("https://your-api.example.com/webhooks/extract") .addWebhookEvent(ExtractV2JobCreate.WebhookConfiguration.WebhookEvent.EXTRACT_SUCCESS) .addWebhookEvent(ExtractV2JobCreate.WebhookConfiguration.WebhookEvent.EXTRACT_ERROR) .webhookHeaders( ExtractV2JobCreate.WebhookConfiguration.WebhookHeaders.builder() .putAdditionalProperty("Authorization", JsonValue.from("Bearer your-webhook-secret")) .build()) .build();
ExtractV2Job job = client.extract().create( ExtractCreateParams.builder() .extractV2JobCreate( ExtractV2JobCreate.builder() .fileInput(fileObj.id()) .configuration(extractConfig) .addWebhookConfiguration(webhook) .build()) .build());
// No polling needed. Your webhook endpoint receives a POST when the job completes.System.out.println("Submitted job: " + job.id());llp extract create \ --file-input "$FILE_ID" \ --configuration "{\"data_schema\": $DATA_SCHEMA, \"extraction_target\": \"per_doc\", \"tier\": \"cost_effective\"}" \ --webhook-configuration '[{"webhook_url": "https://your-api.example.com/webhooks/extract", "webhook_events": ["extract.success", "extract.error"], "webhook_headers": {"Authorization": "Bearer your-webhook-secret"}}]'5. Schema Management
Section titled “5. Schema Management”Generate a Schema from a Prompt
Section titled “Generate a Schema from a Prompt”Don’t want to write JSON schema by hand? Describe what you need in plain English and let the API generate a schema.
generated = client.extract.generate_schema( prompt="Extract the company name, CEO name, founding year, and headquarters city from this annual report",)
print("Generated schema:", generated.parameters.data_schema)print("Suggested config name:", generated.name)
# Use the generated schema directly in an extraction jobjob = client.extract.create( file_input=file_obj.id, configuration={ "data_schema": generated.parameters.data_schema, "extraction_target": "per_doc", "tier": "cost_effective", },)const generated = await client.extract.generateSchema({ prompt: "Extract the company name, CEO name, founding year, and headquarters city from this annual report",});
console.log("Generated schema:", generated.parameters.data_schema);
const job = await client.extract.create({ file_input: fileObj.id, configuration: { data_schema: generated.parameters.data_schema, extraction_target: "per_doc", tier: "cost_effective", },});import ( "encoding/json")
// fileObj is defined in the Quick Start above.generated, err := client.Extract.GenerateSchema(ctx, llamacloud.ExtractGenerateSchemaParams{ ExtractV2SchemaGenerateRequest: llamacloud.ExtractV2SchemaGenerateRequestParam{ Prompt: llamacloud.String("Extract the company name, CEO name, founding year, and headquarters city from this annual report"), },})if err != nil { log.Fatal(err)}fmt.Println("Generated schema:", generated.Parameters.JSON.DataSchema.Raw())
// Use the generated schema directly in an extraction jobvar generatedSchema map[string]*llamacloud.ExtractConfigurationDataSchemaUnionParamif err := json.Unmarshal([]byte(generated.Parameters.JSON.DataSchema.Raw()), &generatedSchema); err != nil { log.Fatal(err)}
job, err := client.Extract.New(ctx, llamacloud.ExtractNewParams{ ExtractV2JobCreate: llamacloud.ExtractV2JobCreateParam{ FileInput: fileObj.ID, Configuration: llamacloud.ExtractConfigurationParam{ DataSchema: generatedSchema, ExtractionTarget: llamacloud.ExtractConfigurationExtractionTargetPerDoc, Tier: llamacloud.ExtractConfigurationTierCostEffective, }, },})if err != nil { log.Fatal(err)}import ai.llamaindex.llamacloud.models.configurations.ConfigurationCreate;import ai.llamaindex.llamacloud.models.extract.ExtractGenerateSchemaParams;import ai.llamaindex.llamacloud.models.extract.ExtractV2SchemaGenerateRequest;
// fileObj is defined in the Quick Start above.ConfigurationCreate generated = client.extract().generateSchema( ExtractGenerateSchemaParams.builder() .extractV2SchemaGenerateRequest( ExtractV2SchemaGenerateRequest.builder() .prompt("Extract the company name, CEO name, founding year, and headquarters city from this annual report") .build()) .build());
// Use the generated schema directly in an extraction jobExtractConfiguration.DataSchema generatedSchema = ExtractConfiguration.DataSchema.builder() .putAllAdditionalProperties( generated.parameters().extractV2().get().dataSchema()._additionalProperties()) .build();
ExtractV2Job job = client.extract().create( ExtractCreateParams.builder() .extractV2JobCreate( ExtractV2JobCreate.builder() .fileInput(fileObj.id()) .configuration( ExtractConfiguration.builder() .dataSchema(generatedSchema) .extractionTarget(ExtractConfiguration.ExtractionTarget.PER_DOC) .tier(ExtractConfiguration.Tier.COST_EFFECTIVE) .build()) .build()) .build());# Generate schema from a promptSCHEMA=$(llp extract generate-schema \ --prompt "Extract the company name, CEO name, founding year, and headquarters city from this annual report" \ | jq -c '.parameters.data_schema')
echo "Generated schema: $SCHEMA"
# Use the generated schema directly in an extraction jobllp extract create \ --file-input "$FILE_ID" \ --configuration "{\"data_schema\": $SCHEMA, \"extraction_target\": \"per_doc\", \"tier\": \"cost_effective\"}"Generate a Schema from a Sample File
Section titled “Generate a Schema from a Sample File”Upload a representative file and let the API analyze it to suggest a schema.
sample_file = client.files.create( file="./invoices/sample_invoice.pdf", purpose="extract",)
generated = client.extract.generate_schema( file_id=sample_file.id, prompt="Extract all invoice fields including line items",)
print("Generated schema:", generated.parameters.data_schema)const sampleFile = await client.files.create({ file: fs.createReadStream("./invoices/sample_invoice.pdf"), purpose: "extract",});
const generated = await client.extract.generateSchema({ file_id: sampleFile.id, prompt: "Extract all invoice fields including line items",});
console.log("Generated schema:", generated.parameters.data_schema);f, err := os.Open("./invoices/sample_invoice.pdf")if err != nil { log.Fatal(err)}defer f.Close()
sampleFile, err := client.Files.New(ctx, llamacloud.FileNewParams{ File: f, Purpose: "extract",})if err != nil { log.Fatal(err)}
generated, err := client.Extract.GenerateSchema(ctx, llamacloud.ExtractGenerateSchemaParams{ ExtractV2SchemaGenerateRequest: llamacloud.ExtractV2SchemaGenerateRequestParam{ FileID: llamacloud.String(sampleFile.ID), Prompt: llamacloud.String("Extract all invoice fields including line items"), },})if err != nil { log.Fatal(err)}
fmt.Println("Generated schema:", generated.Parameters.JSON.DataSchema.Raw())FileCreateResponse sampleFile = client.files().create( FileCreateParams.builder() .file(Paths.get("./invoices/sample_invoice.pdf")) .purpose("extract") .build());
ConfigurationCreate generated = client.extract().generateSchema( ExtractGenerateSchemaParams.builder() .extractV2SchemaGenerateRequest( ExtractV2SchemaGenerateRequest.builder() .fileId(sampleFile.id()) .prompt("Extract all invoice fields including line items") .build()) .build());
System.out.println("Generated schema: " + generated.parameters().extractV2().get().dataSchema());FILE_ID=$(llp files create --file ./invoices/sample_invoice.pdf --purpose extract | jq -r '.id')
llp extract generate-schema \ --file-id "$FILE_ID" \ --prompt "Extract all invoice fields including line items" \ | jq '.parameters.data_schema'Validate a Schema Before Use
Section titled “Validate a Schema Before Use”Catch schema errors before running extraction jobs.
from pydantic import BaseModel, Field
class MySchema(BaseModel): name: str = Field(description="Person's full name") age: int = Field(description="Person's age in years")
validated = client.extract.validate_schema( data_schema=MySchema.model_json_schema(),)
# Returns the validated schema if valid, raises an error if invalidprint("Schema is valid:", validated.data_schema)// Returns the validated schema if valid, throws an error if invalidconst validated = await client.extract.validateSchema({ data_schema: { type: "object", properties: { name: { type: "string", description: "Person's full name" }, age: { type: "integer", description: "Person's age in years" }, }, required: ["name", "age"], },});
console.log("Schema is valid:", validated.data_schema);// Returns the validated schema if valid, returns an error if invalidvalidated, err := client.Extract.ValidateSchema(ctx, llamacloud.ExtractValidateSchemaParams{ ExtractV2SchemaValidateRequest: llamacloud.ExtractV2SchemaValidateRequestParam{ DataSchema: map[string]*llamacloud.ExtractV2SchemaValidateRequestDataSchemaUnionParam{ "type": {OfString: llamacloud.String("object")}, "properties": {OfAnyMap: map[string]any{ "name": map[string]any{"type": "string", "description": "Person's full name"}, "age": map[string]any{"type": "integer", "description": "Person's age in years"}, }}, "required": {OfAnyArray: []any{"name", "age"}}, }, },})if err != nil { log.Fatal(err)}fmt.Println("Schema is valid:", validated.RawJSON())import ai.llamaindex.llamacloud.models.extract.ExtractValidateSchemaParams;import ai.llamaindex.llamacloud.models.extract.ExtractV2SchemaValidateRequest;import ai.llamaindex.llamacloud.models.extract.ExtractV2SchemaValidateResponse;
ExtractV2SchemaValidateRequest.DataSchema schema = ExtractV2SchemaValidateRequest.DataSchema.builder() .putAdditionalProperty("type", JsonValue.from("object")) .putAdditionalProperty("properties", JsonValue.from(Map.of( "name", Map.of("type", "string", "description", "Person's full name"), "age", Map.of("type", "integer", "description", "Person's age in years")))) .putAdditionalProperty("required", JsonValue.from(Arrays.asList("name", "age"))) .build();
// Returns the validated schema if valid, throws an error if invalidExtractV2SchemaValidateResponse validated = client.extract().validateSchema( ExtractValidateSchemaParams.builder() .extractV2SchemaValidateRequest( ExtractV2SchemaValidateRequest.builder() .dataSchema(schema) .build()) .build());
System.out.println("Schema is valid: " + validated.dataSchema());# Returns the validated schema if valid, returns an error if invalidllp extract validate-schema \ --data-schema '{ "type": "object", "properties": { "name": {"type": "string", "description": "Full name of the person"}, "age": {"type": "integer", "description": "Age of the person in years"} }, "required": ["name", "age"] }'Putting It All Together
Section titled “Putting It All Together”Here’s a complete production script that combines batch upload, parse-then-extract, timeout handling, and result collection.
import asyncioimport timefrom pathlib import Pathfrom pydantic import BaseModel, Fieldfrom typing import Optionalfrom llama_cloud import AsyncLlamaCloud
async_client = AsyncLlamaCloud()
class ContractSummary(BaseModel): parties: list[str] = Field(description="Names of all contracting parties") effective_date: str = Field(description="Contract effective date") contract_type: str = Field(description="Type of contract (NDA, MSA, SOW, etc.)") total_value: Optional[float] = Field(None, description="Total contract value if specified")
EXTRACT_CONFIG = { "data_schema": ContractSummary.model_json_schema(), "extraction_target": "per_doc", "tier": "agentic", "cite_sources": True,}
async def process_contract(file_path: Path, timeout: float = 300) -> dict: """Parse, then extract from a single contract with timeout.""" # Upload file_obj = await async_client.files.create( file=str(file_path), purpose="extract" )
# Parse first for higher quality parse_job = await async_client.parsing.create( tier="agentic", version="latest", file_id=file_obj.id, ) parse_job = await async_client.parsing.wait_for_completion( parse_job.id, timeout=timeout )
# Extract from parse result job = await async_client.extract.create( file_input=parse_job.id, configuration=EXTRACT_CONFIG, )
start = time.monotonic() while job.status not in ("COMPLETED", "FAILED", "CANCELLED"): if time.monotonic() - start > timeout: return {"file": file_path.name, "error": f"Timeout after {timeout}s"} await asyncio.sleep(2) job = await async_client.extract.get(job.id)
if job.status == "COMPLETED": return { "file": file_path.name, "data": job.extract_result, # Note: extract_metadata requires ?expand=extract_metadata on GET } return {"file": file_path.name, "error": job.error_message}
async def main(): contracts = list(Path("./contracts").glob("*.pdf")) semaphore = asyncio.Semaphore(5)
async def bounded(path): async with semaphore: return await process_contract(path)
results = await asyncio.gather(*[bounded(p) for p in contracts])
for r in results: if "data" in r: summary = ContractSummary.model_validate(r["data"]) print(f"{r['file']}: {summary.contract_type} between {', '.join(summary.parties)}") else: print(f"{r['file']}: ERROR - {r['error']}")
asyncio.run(main())import * as fs from "fs";import * as path from "path";
const EXTRACT_CONFIG = { data_schema: { type: "object", properties: { parties: { type: "array", items: { type: "string" }, description: "Contracting parties" }, effective_date: { type: "string", description: "Contract effective date" }, contract_type: { type: "string", description: "Type of contract (NDA, MSA, SOW, etc.)" }, total_value: { type: "number", description: "Total contract value", nullable: true }, }, required: ["parties", "effective_date", "contract_type"], }, extraction_target: "per_doc" as const, tier: "agentic" as const, cite_sources: true,};
async function processContract(filePath: string, timeoutMs = 300_000) { const fileObj = await client.files.create({ file: fs.createReadStream(filePath), purpose: "extract", });
// Parse first let parseJob = await client.parsing.create({ tier: "agentic", version: "latest", file_id: fileObj.id, }); parseJob = await client.parsing.waitForCompletion(parseJob.id);
// Extract from parse result let job = await client.extract.create({ file_input: parseJob.id, configuration: EXTRACT_CONFIG, });
const start = Date.now(); while (!["COMPLETED", "FAILED", "CANCELLED"].includes(job.status)) { if (Date.now() - start > timeoutMs) { return { file: path.basename(filePath), error: "Timeout" }; } await new Promise((r) => setTimeout(r, 2000)); job = await client.extract.get(job.id); }
if (job.status === "COMPLETED") { return { file: path.basename(filePath), data: job.extract_result }; } return { file: path.basename(filePath), error: job.error_message };}package main
import ( "context" "fmt" "os" "path/filepath" "sync" "time"
llamacloud "github.com/run-llama/llama-parse-go")
type contractResult struct { File string Data llamacloud.ExtractV2JobExtractResultUnion Err error}
func main() { ctx := context.Background() client := llamacloud.NewClient()
contractSchema := map[string]*llamacloud.ExtractConfigurationDataSchemaUnionParam{ "type": {OfString: llamacloud.String("object")}, "properties": {OfAnyMap: map[string]any{ "parties": map[string]any{"type": "array", "items": map[string]any{"type": "string"}, "description": "Names of all contracting parties"}, "effective_date": map[string]any{"type": "string", "description": "Contract effective date"}, "contract_type": map[string]any{"type": "string", "description": "Type of contract (NDA, MSA, SOW, etc.)"}, "total_value": map[string]any{"type": "number", "description": "Total contract value if specified"}, }}, "required": {OfAnyArray: []any{"parties", "effective_date", "contract_type"}}, }
extractConfig := llamacloud.ExtractConfigurationParam{ DataSchema: contractSchema, ExtractionTarget: llamacloud.ExtractConfigurationExtractionTargetPerDoc, Tier: llamacloud.ExtractConfigurationTierAgentic, CiteSources: llamacloud.Bool(true), }
// Parse, then extract from a single contract with a timeout. processContract := func(filePath string, timeout time.Duration) contractResult { name := filepath.Base(filePath)
f, err := os.Open(filePath) if err != nil { return contractResult{File: name, Err: err} } defer f.Close()
fileObj, err := client.Files.New(ctx, llamacloud.FileNewParams{ File: f, Purpose: "extract", }) if err != nil { return contractResult{File: name, Err: err} }
// Parse first for higher quality parseJob, err := client.Parsing.New(ctx, llamacloud.ParsingNewParams{ Tier: llamacloud.ParsingNewParamsTierAgentic, Version: llamacloud.ParsingNewParamsVersionLatest, FileID: llamacloud.String(fileObj.ID), }) if err != nil { return contractResult{File: name, Err: err} }
deadline := time.Now().Add(timeout) parseResult, err := client.Parsing.Get(ctx, parseJob.ID, llamacloud.ParsingGetParams{}) if err != nil { return contractResult{File: name, Err: err} } for parseResult.Job.Status != "COMPLETED" && parseResult.Job.Status != "FAILED" && parseResult.Job.Status != "CANCELLED" { if time.Now().After(deadline) { return contractResult{File: name, Err: fmt.Errorf("parse timed out")} } time.Sleep(2 * time.Second) parseResult, err = client.Parsing.Get(ctx, parseJob.ID, llamacloud.ParsingGetParams{}) if err != nil { return contractResult{File: name, Err: err} } }
// Extract from the parse result job, err := client.Extract.New(ctx, llamacloud.ExtractNewParams{ ExtractV2JobCreate: llamacloud.ExtractV2JobCreateParam{ FileInput: parseJob.ID, Configuration: extractConfig, }, }) if err != nil { return contractResult{File: name, Err: err} }
for job.Status != "COMPLETED" && job.Status != "FAILED" && job.Status != "CANCELLED" { if time.Now().After(deadline) { return contractResult{File: name, Err: fmt.Errorf("extraction timed out")} } time.Sleep(2 * time.Second) job, err = client.Extract.Get(ctx, job.ID, llamacloud.ExtractGetParams{}) if err != nil { return contractResult{File: name, Err: err} } }
if job.Status != "COMPLETED" { return contractResult{File: name, Err: fmt.Errorf("%s", job.ErrorMessage)} } return contractResult{File: name, Data: job.ExtractResult} }
contracts := []string{"./contracts/agreement_a.pdf", "./contracts/agreement_b.pdf"} sem := make(chan struct{}, 5) // Limit concurrency to 5 results := make([]contractResult, len(contracts))
var wg sync.WaitGroup for i, path := range contracts { wg.Add(1) go func(i int, path string) { defer wg.Done() sem <- struct{}{} defer func() { <-sem }() results[i] = processContract(path, 300*time.Second) }(i, path) } wg.Wait()
for _, r := range results { if r.Err != nil { fmt.Printf("%s: ERROR - %v\n", r.File, r.Err) } else { fmt.Printf("%s: %s\n", r.File, r.Data.RawJSON()) } }}import ai.llamaindex.llamacloud.client.LlamaCloudClient;import ai.llamaindex.llamacloud.client.okhttp.LlamaCloudOkHttpClient;import ai.llamaindex.llamacloud.core.JsonValue;import ai.llamaindex.llamacloud.models.extract.ExtractConfiguration;import ai.llamaindex.llamacloud.models.extract.ExtractCreateParams;import ai.llamaindex.llamacloud.models.extract.ExtractGetParams;import ai.llamaindex.llamacloud.models.extract.ExtractV2Job;import ai.llamaindex.llamacloud.models.extract.ExtractV2JobCreate;import ai.llamaindex.llamacloud.models.files.FileCreateParams;import ai.llamaindex.llamacloud.models.files.FileCreateResponse;import ai.llamaindex.llamacloud.models.parsing.ParsingCreateParams;import ai.llamaindex.llamacloud.models.parsing.ParsingCreateResponse;import ai.llamaindex.llamacloud.models.parsing.ParsingGetParams;import ai.llamaindex.llamacloud.models.parsing.ParsingGetResponse;import java.nio.file.Paths;import java.util.ArrayList;import java.util.Arrays;import java.util.List;import java.util.Map;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;
public class BatchExtraction { public static void main(String[] args) throws Exception { // reads LLAMA_CLOUD_API_KEY from the environment LlamaCloudClient client = LlamaCloudOkHttpClient.fromEnv();
ExtractConfiguration.DataSchema contractSchema = ExtractConfiguration.DataSchema.builder() .putAdditionalProperty("type", JsonValue.from("object")) .putAdditionalProperty("properties", JsonValue.from(Map.of( "parties", Map.of("type", "array", "items", Map.of("type", "string"), "description", "Names of all contracting parties"), "effective_date", Map.of("type", "string", "description", "Contract effective date"), "contract_type", Map.of("type", "string", "description", "Type of contract (NDA, MSA, SOW, etc.)"), "total_value", Map.of("type", "number", "description", "Total contract value if specified")))) .putAdditionalProperty("required", JsonValue.from(Arrays.asList("parties", "effective_date", "contract_type"))) .build();
ExtractConfiguration extractConfig = ExtractConfiguration.builder() .dataSchema(contractSchema) .extractionTarget(ExtractConfiguration.ExtractionTarget.PER_DOC) .tier(ExtractConfiguration.Tier.AGENTIC) .citeSources(true) .build();
ExecutorService pool = Executors.newFixedThreadPool(5); // Limit concurrency to 5 List<String> contracts = Arrays.asList("./contracts/agreement_a.pdf", "./contracts/agreement_b.pdf"); List<Future<ExtractV2Job>> futures = new ArrayList<>();
for (String filePath : contracts) { futures.add(pool.submit(() -> { FileCreateResponse fileObj = client.files().create( FileCreateParams.builder() .file(Paths.get(filePath)) .purpose("extract") .build());
// Parse first for higher quality ParsingCreateResponse parseJob = client.parsing().create( ParsingCreateParams.builder() .tier(ParsingCreateParams.Tier.AGENTIC) .version(ParsingCreateParams.Version.LATEST) .fileId(fileObj.id()) .build());
ParsingGetResponse parseResult = client.parsing().get( ParsingGetParams.builder().jobId(parseJob.id()).build()); while (!parseResult.job().status().equals(ParsingGetResponse.Job.Status.COMPLETED) && !parseResult.job().status().equals(ParsingGetResponse.Job.Status.FAILED) && !parseResult.job().status().equals(ParsingGetResponse.Job.Status.CANCELLED)) { Thread.sleep(2000); parseResult = client.parsing().get( ParsingGetParams.builder().jobId(parseJob.id()).build()); }
// Extract from the parse result ExtractV2Job job = client.extract().create( ExtractCreateParams.builder() .extractV2JobCreate( ExtractV2JobCreate.builder() .fileInput(parseJob.id()) .configuration(extractConfig) .build()) .build());
while (!job.status().equals("COMPLETED") && !job.status().equals("FAILED") && !job.status().equals("CANCELLED")) { Thread.sleep(2000); job = client.extract().get(ExtractGetParams.builder().jobId(job.id()).build()); } return job; })); }
for (Future<ExtractV2Job> future : futures) { System.out.println(future.get().extractResult()); } pool.shutdown(); }}CONTRACT_SCHEMA='{ "type": "object", "properties": { "parties": {"type": "array", "items": {"type": "string"}, "description": "Names of all contracting parties"}, "effective_date": {"type": "string", "description": "Contract effective date"}, "contract_type": {"type": "string", "description": "Type of contract (NDA, MSA, SOW, etc.)"}, "total_value": {"type": "number", "description": "Total contract value if specified"} }, "required": ["parties", "effective_date", "contract_type"]}'EXTRACT_CONFIG="{\"data_schema\": $CONTRACT_SCHEMA, \"extraction_target\": \"per_doc\", \"tier\": \"agentic\", \"cite_sources\": true}"
CONCURRENCY=5TIMEOUT=300 # secondsWORK_DIR=$(mktemp -d)
# Block until fewer than $CONCURRENCY background jobs are running.throttle() { while (( $(jobs -rp | wc -l) >= CONCURRENCY )); do sleep 1; done}
# Parse, then extract from a single contract with a timeout.process_contract() { local path="$1" name deadline file_id parse_job_id job job_id status name=$(basename "$path") deadline=$(( $(date +%s) + TIMEOUT ))
file_id=$(llp files create --file "$path" --purpose extract | jq -r '.id')
# Parse first for higher quality parse_job_id=$(llp parsing create \ --tier agentic \ --version latest \ --file-id "$file_id" \ | jq -r '.id')
while true; do status=$(llp parsing get --job-id "$parse_job_id" | jq -r '.job.status') case "$status" in COMPLETED|FAILED|CANCELLED) break ;; esac if [ "$(date +%s)" -gt "$deadline" ]; then jq -nc --arg file "$name" --arg t "$TIMEOUT" \ '{file: $file, error: "Parse timed out after \($t)s"}' > "$WORK_DIR/$name.result" return fi sleep 2 done
# Extract from the parse result job_id=$(llp extract create \ --file-input "$parse_job_id" \ --configuration "$EXTRACT_CONFIG" \ | jq -r '.id')
while true; do job=$(llp extract get --job-id "$job_id") status=$(echo "$job" | jq -r '.status') case "$status" in COMPLETED|FAILED|CANCELLED) break ;; esac if [ "$(date +%s)" -gt "$deadline" ]; then jq -nc --arg file "$name" --arg t "$TIMEOUT" \ '{file: $file, error: "Timeout after \($t)s"}' > "$WORK_DIR/$name.result" return fi sleep 2 done
# Note: extract_metadata requires --expand extract_metadata on `llp extract get` if [ "$status" = "COMPLETED" ]; then echo "$job" | jq -c --arg file "$name" '{file: $file, data: .extract_result}' else echo "$job" | jq -c --arg file "$name" '{file: $file, error: .error_message}' fi > "$WORK_DIR/$name.result"}
for path in ./contracts/*.pdf; do throttle process_contract "$path" &donewait
jq -r 'if .data then "\(.file): \(.data.contract_type) between \(.data.parties | join(", "))" else "\(.file): ERROR - \(.error)" end' "$WORK_DIR"/*.resultWhat’s Next?
Section titled “What’s Next?”- Extract Data with Citations for inspecting extraction provenance
- Auto-Generate Schema for Extraction for more schema generation patterns
- Extract Repeating Entities for table row extraction
- LlamaExtract Documentation for the full API reference