> ## Documentation Index > Fetch the complete documentation index at: https://docs.extend.ai/llms.txt > Use this file to discover all available pages before exploring further. > > ## API version > The current API version is `2026-02-09`, served at the site root (no version prefix in URLs). > If this page URL contains `/2025-04-21/` or `/2024-12-23/`, you are reading an older API version. > Prefer the current docs at https://docs.extend.ai/llms.txt unless the user explicitly needs that older version. > Do not treat older-version pages as the source of truth for new integrations. # Multifile Extraction > Extract structured data from multiple documents in a single run using the package field. > **Note** > > This page covers standalone `/extract_runs` calls. To run the same extraction inside a workflow, with validation, review, and routing applied to the combined result, see [Multifile Extraction in Workflows](/workflows/multifile-extraction). Multifile extraction lets you run a single extraction over a collection of files with a shared context. Useful when a field's value must be chosen from the best among a variety of sources, or when values are derived from multiple files. **Example 1:** A contract contains a number of amendments and those amendments supersede the original content. **Example 2:** A user submits multiple pictures of a long receipt that should be extracted together. ## How it works Pass a `package` instead of a `file` on your request. The `package.files` array accepts up to 50 entries, each either a URL or an existing Extend file ID. The API ingests all files concurrently, runs extraction across the full corpus, and returns a single `ExtractRun` with a `files` array in the response (and `file: null`). ## Quick start #### Python ```python from extend_ai import Extend client = Extend() result = client.extract_runs.create_and_poll( extractor={"id": "ex_abc123"}, package={ "files": [ {"url": "https://example.com/invoice1.pdf"}, {"url": "https://example.com/invoice2.pdf"}, {"url": "https://example.com/invoice3.pdf"}, ] }, ) print(result.output.value) print("Files processed:", [f.name for f in result.files]) ``` #### TypeScript ```typescript import { ExtendClient } from "extend-ai"; const client = new ExtendClient(); const result = await client.extractRuns.createAndPoll({ extractor: { id: "ex_abc123" }, package: { files: [ { url: "https://example.com/invoice1.pdf" }, { url: "https://example.com/invoice2.pdf" }, { url: "https://example.com/invoice3.pdf" }, ], }, }); console.log(result.output?.value); console.log("Files processed:", result.files?.map((f) => f.name)); ``` #### Java ```java import ai.extend.ExtendClient; import ai.extend.resources.extractruns.requests.ExtractRunsCreateRequest; import ai.extend.resources.extractruns.types.ExtractRunsCreateRequestExtractor; import ai.extend.types.ExtractRun; import ai.extend.types.FileFromUrl; import ai.extend.types.MultiFileRunPackage; import ai.extend.types.MultiFileRunPackageFilesItem; import java.util.List; ExtendClient client = ExtendClient.builder().build(); ExtractRun result = client.extractRuns().createAndPoll( ExtractRunsCreateRequest.builder() .extractor(ExtractRunsCreateRequestExtractor.builder() .id("ex_abc123") .build()) .package_(MultiFileRunPackage.builder() .files(List.of( MultiFileRunPackageFilesItem.of(FileFromUrl.builder().url("https://example.com/invoice1.pdf").build()), MultiFileRunPackageFilesItem.of(FileFromUrl.builder().url("https://example.com/invoice2.pdf").build()), MultiFileRunPackageFilesItem.of(FileFromUrl.builder().url("https://example.com/invoice3.pdf").build()) )) .build()) .build()); System.out.println(result.getOutput()); System.out.println("Files processed: " + result.getFiles()); ``` #### Go ```go package main import ( "context" "fmt" "log" "time" extend "github.com/extend-hq/extend-go-sdk" "github.com/extend-hq/extend-go-sdk/client" ) func main() { c := client.NewClient() ctx := context.Background() run, err := c.ExtractRuns.Create(ctx, &extend.ExtractRunsCreateRequest{ Extractor: &extend.ExtractRunsCreateRequestExtractor{ ID: "ex_abc123", }, Package: &extend.MultiFileRunPackage{ Files: []*extend.MultiFileRunPackageFilesItem{ {FileFromURL: &extend.FileFromURL{URL: "https://example.com/invoice1.pdf"}}, {FileFromURL: &extend.FileFromURL{URL: "https://example.com/invoice2.pdf"}}, {FileFromURL: &extend.FileFromURL{URL: "https://example.com/invoice3.pdf"}}, }, }, }) if err != nil { log.Fatal(err) } // The Go SDK does not yet include a CreateAndPoll helper, so poll for completion. for run.Status == extend.ProcessorRunStatusPending || run.Status == extend.ProcessorRunStatusProcessing { time.Sleep(2 * time.Second) run, err = c.ExtractRuns.Retrieve(ctx, run.ID, &extend.ExtractRunsRetrieveRequest{}) if err != nil { log.Fatal(err) } } // Output is only present when the run reaches PROCESSED. if run.Status != extend.ProcessorRunStatusProcessed { log.Fatalf("extract run ended with status %s", run.Status) } fmt.Println(run.Output.GetExtractOutputJSON().Value) for _, f := range run.Files { fmt.Println("Processed:", f.Name) } } ``` #### cURL ```bash curl -X POST https://api.extend.ai/extract_runs \ -H "x-extend-api-version: 2026-02-09" \ -H "Authorization: Bearer $EXTEND_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "extractor": { "id": "ex_abc123" }, "package": { "files": [ { "url": "https://example.com/invoice1.pdf" }, { "url": "https://example.com/invoice2.pdf" }, { "url": "https://example.com/invoice3.pdf" } ] } }' ``` ## File inputs Each entry in `package.files` can be either: | Input | Shape | Notes | | ------- | -------------------------- | ----------------------------------------- | | URL | `{ "url": "https://..." }` | Presigned URLs recommended for production | | File ID | `{ "id": "file_..." }` | Reuse a previously uploaded Extend file | Raw text (`text`) and base64 inputs are not supported in multifile packages — use `url` or `id`. You can mix URLs and file IDs in the same package: ```json { "package": { "files": [ { "url": "https://example.com/cover-page.pdf" }, { "id": "file_xK9mLPqRtN3vS8wF5hB2cQ" }, { "url": "https://example.com/appendix.pdf" } ] } } ``` ## Response A multifile run returns the same `ExtractRun` shape as a single-file run, with two differences: * **`file`** is `null` * **`files`** is an ordered array of `FileSummary` objects, one per input file in submission order ```json { "object": "extract_run", "id": "exr_3f1j6I1gsw5k96xFiCnkM", "status": "PROCESSED", "file": null, "files": [ { "object": "file", "id": "file_aaa", "name": "invoice1.pdf", ... }, { "object": "file", "id": "file_bbb", "name": "invoice2.pdf", ... }, { "object": "file", "id": "file_ccc", "name": "invoice3.pdf", ... } ], "output": { "value": { ... }, "metadata": { ... } } } ``` > **Note** > > `output.value` is a single object covering the whole corpus — not one object per file. Design your extractor schema to describe what you want extracted across all files together. ## Citations and file provenance > **Note** > > Looking for citations on an ordinary single-file extraction (what they contain, how to enable them, bounding-box coordinates)? See [Citations](/extraction/response-format#citations) on the Response Format page. This section covers only what changes when the run spans a package of files. Every citation carries a `fileId` telling you which input file the cited content came from. Join it against the run's `files` array to attribute each extracted value to its source document — a citation's `page.number` is always relative to that file, not to the corpus as a whole. ```json { "files": [ { "object": "file", "id": "file_aaa", "name": "invoice1.pdf" }, { "object": "file", "id": "file_bbb", "name": "invoice2.pdf" } ], "output": { "value": { "total_amount": 15735.1 }, "metadata": { "total_amount": { "logprobsConfidence": 0.98, "citations": [ { "fileId": "file_bbb", "page": { "number": 2, "width": 612, "height": 792 }, "referenceText": "TOTAL $15,735.1" } ] } } } } ``` `fileId` is present whether or not bounding-box citations are enabled, so per-field provenance is available on every multifile run. See [Citations](/extraction/response-format#citations) for the full citation shape. ## Multifile vs batch Multifile extraction and [batch processing](/general/batch-processing) are complementary but different: | | Multifile (`package`) | Batch (`/extract_runs/batch`) | | --------------------- | ---------------------------------------- | ------------------------------------------------- | | **What it is** | One run across N files — a single corpus | N independent single-file runs submitted together | | **Output** | One `output.value` combining all files | One `output.value` per file | | **Use when** | Fields span multiple documents | Each file is extracted independently | | **Files per request** | 1–50 | Up to 1,000 | Use multifile when your extractor schema is designed to aggregate across a set of documents. Use batch when you just want to submit many independent files efficiently. ## Evaluating multifile extraction To measure accuracy over packages, add multifile items to an [evaluation set](/evaluation/overview) by passing `fileIds` (an ordered list of 2–50 file IDs) instead of `fileId`. Each item is evaluated as a single multifile run against one `expectedOutput`, and is returned with the same `file: null` / `files: [...]` shape as a multifile `ExtractRun`. See [Add a multifile item](/evaluation/creating-evaluation-sets#add-a-multifile-item). > Ship your document processing with Extend. Get started with our APIs, SDKs, and guides.