---
updatedAt: 2026-07-31T18:03:06.000Z
agentTools:
  projectIndex: https://docs.sensible.so/llms.txt
---

# Extract portfolio at your URL


Use this endpoint with multiple documents that are packaged into one file (a "portfolio"). For a list of supported file types, see [Supported file types](doc:file-types).
Segments a portfolio file at the specified `document_url` into the specified document types (for example, 1099, w2, and bank_statement)
and then runs extractions asynchronously for each document Sensible finds in the portfolio. Take the following steps.
1. Run this endpoint.
3. To retrieve the extraction, use a webhook, or use the extraction `id` returned in the  response to poll the GET documents/{id} endpoint.
For more about extracting from portfolios, see [Multi-document extractions](doc:portfolio).


# OpenAPI definition

```json
{
  "openapi": "3.0.3",
  "servers": [
    {
      "url": "https://api.sensible.so/v0",
      "description": "Production server (uses live data)"
    }
  ],
  "info": {
    "title": "Extraction",
    "version": "0.0.0",
    "license": {
      "name": "Sensible API",
      "url": "https://www.TBD.org/licenses/LICENSE-2.0.html"
    },
    "description": "Extract structured data from documents with the Sensible API."
  },
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Portfolio",
      "description": "Extract data from multiple documents bundled into single PDF files"
    }
  ],
  "paths": {
    "/extract_from_url": {
      "post": {
        "operationId": "provide-a-download-url-for-a-pdf-portfolio",
        "summary": "Extract portfolio at your URL",
        "description": "\nUse this endpoint with multiple documents that are packaged into one file (a \"portfolio\"). For a list of supported file types, see [Supported file types](https://docs.sensible.so/docs/file-types).\nSegments a portfolio file at the specified `document_url` into the specified document types (for example, 1099, w2, and bank_statement)\nand then runs extractions asynchronously for each document Sensible finds in the portfolio. Take the following steps.\n1. Run this endpoint.\n3. To retrieve the extraction, use a webhook, or use the extraction `id` returned in the  response to poll the GET documents/{id} endpoint.\nFor more about extracting from portfolios, see [Multi-document extractions](https://docs.sensible.so/docs/portfolio).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/environment"
          },
          {
            "$ref": "#/components/parameters/document_name"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExtractFromUrlPortfolioRequest"
              }
            }
          }
        },
        "tags": [
          "Portfolio"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExtractFromUrlPortfolioResponse"
                }
              }
            },
            "description": "Returns the ID to use to retrieve the extraction."
          },
          "400": {
            "$ref": "#/components/responses/400"
          },
          "401": {
            "$ref": "#/components/responses/401"
          },
          "415": {
            "$ref": "#/components/responses/415"
          },
          "429": {
            "$ref": "#/components/responses/429"
          },
          "500": {
            "$ref": "#/components/responses/500"
          }
        }
      }
    }
  },
  "components": {
    "responses": {
      "400": {
        "description": "Bad Request. Sensible returns either a plain-text error message or a structured JSON error, depending on what caused the failure. For more information, see the two content types below.",
        "content": {
          "text/plain": {
            "schema": {
              "title": "Bad Request",
              "type": "string",
              "example": "Either a specific set of messages about fields in the request, or error messages like the following examples - To use the asynchronous flow you must have persistence enabled Specified document type does not exist Specified document type ${named type} does not exist No published configurations found for environment ${environment} Specified golden does not exist Must provide the Content-Type header Missing request body or body.document Could not determine the content type of the document Could not determine the content type of the document. Please check that the document was correctly encoded as Base64 This PDF is invalid. If you submitted this PDF using Base64 encoding, please check that the encoding is correct This PDF is password protected. Please resubmit with password protection disabled This PDF is empty This PDF exceeds the maximum dimensions for OCR of 17 x 17 inches This PDF exceeds the maximum size for OCR of 50MB No fingerprints match for this PDF and fingerprint_mode is set to strict Content type of ${found} does not match declared type of ${expected} The start date must be before the end date"
            }
          },
          "application/json": {
            "schema": {
              "title": "Bad Request (structured)",
              "type": "object",
              "description": "Returned instead of plain text when the request fails path, query, or body validation (for example, a malformed id, an out-of-range limit, or an invalid field in a JSON body), and for the extract_from_url document-fetch failure. The shape of `context` varies by cause: for validation failures, it echoes the offending path and/or query values; for the extract_from_url failure, it's the extraction's current state.",
              "properties": {
                "message": {
                  "type": "string",
                  "description": "Human-readable summary of the error."
                },
                "errors": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Present when the request failed path, query, or body validation -- one message per failing field, in the form '<location>/<field> <reason>'."
                },
                "context": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "Additional context about the failure. See the parent description for how this varies by cause."
                }
              },
              "required": [
                "message"
              ]
            },
            "examples": {
              "validationFailure": {
                "summary": "A path parameter failed validation",
                "value": {
                  "message": "This request contains errors",
                  "context": {
                    "path": "/documents/{id}"
                  },
                  "errors": [
                    "path/id should be valid version 4 UUID"
                  ]
                }
              },
              "documentFetchFailure": {
                "summary": "extract_from_url could not download the document",
                "value": {
                  "message": "Transferring the file failed with 'Response code 404 (Not Found)'",
                  "context": {
                    "id": "e75dd6ee-ef8f-4960-8be2-ba22f8c398e3",
                    "created": "2026-07-17T14:39:49.084Z",
                    "completed": "2026-07-17T14:39:49.330Z",
                    "status": "FAILED",
                    "type": "1040s",
                    "environment": "production",
                    "errors": [],
                    "error": "Response code 404:\n404: Not Found",
                    "actor": "legacy: Sensible"
                  }
                }
              }
            }
          }
        }
      },
      "401": {
        "description": "Not authorized",
        "content": {
          "text/plain": {
            "schema": {
              "title": "Unauthorized",
              "type": "string",
              "example": "Unauthorized"
            }
          }
        }
      },
      "415": {
        "description": "Unsupported Media Type",
        "content": {
          "text/plain": {
            "schema": {
              "title": "Unsupported Media Type",
              "type": "string",
              "description": "Unsupported file type. See https://docs.sensible.so/docs/file-types."
            }
          }
        }
      },
      "429": {
        "description": "Too Many Requests",
        "content": {
          "text/plain": {
            "schema": {
              "title": "Too Many Requests",
              "type": "string",
              "example": "One of the following error messages - Attempt limit exceeded, please retry after some time. Free accounts are limited to 150 API calls per month. Please upgrade your account to make additional calls. Pro accounts are limited to 5,000 API calls per month. Please upgrade your account to make additional calls."
            }
          }
        }
      },
      "500": {
        "description": "Internal Server Error",
        "content": {
          "text/plain": {
            "schema": {
              "title": "Sensible encountered an unknown error",
              "type": "string",
              "example": "Sensible encountered an unknown error"
            }
          }
        }
      }
    },
    "parameters": {
      "document_name": {
        "name": "document_name",
        "in": "query",
        "description": "If you specify the filename of the document using this parameter, then Sensible returns the filename in the extraction response.",
        "schema": {
          "type": "string"
        },
        "example": "test.pdf"
      },
      "environment": {
        "name": "environment",
        "in": "query",
        "description": "If you specify `development`, extracts preferentially using config versions published to the development environment in the Sensible app. The extraction runs all configs in the doc type before picking the best fit. For each config, falls back to production version if no development version of the config exists.",
        "schema": {
          "type": "string",
          "enum": [
            "production",
            "development"
          ],
          "default": "production"
        }
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Sensible uses API keys to authenticate requests. Keep your API keys secure and do not share them publicly accessible areas such as GitHub, client-side code, etc. Authentication to the API is performed via Bearer Authentication. Provide your API key as the bearer auth value."
      }
    },
    "schemas": {
      "ExtraDataRecord": {
        "type": "object",
        "description": "Extra data in the form of flat key/value pairs you attach to an asynchronous extraction endpoint request, for example, `{\"applicant_id\": \"A-123\", \"expected_premium\": 1250.00}`. Use this parameter to bring request-time context into a config's output so validations, postprocessors, and computed field methods can read it. For example, use it to validate extraction data against a dynamic external record. Has the following constraints: \n- Doesn't support synchronous extractions \n- Doesn't support nested objects and arrays \n- Values must be strings, numbers, booleans, or null \n- Extra data has a maximum size of 16 kB \n- Extra data isn't subject to custom data retention policies. Don't include sensitive information in it. \n\n Sensible persists the extra data and echoes it in responses and webhook deliveries. When you submit a [portfolio](https://docs.sensible.so/docs/portfolio) extraction with extra data, Sensible passes the same object to every document extracted from the portfolio. For more information, see the [Extra Data](https://docs.sensible.so/docs/extra-data) method.",
        "additionalProperties": {
          "oneOf": [
            {
              "type": "string",
              "nullable": true
            },
            {
              "type": "number"
            },
            {
              "type": "boolean"
            }
          ]
        },
        "example": {
          "applicant_id": "A-123",
          "tenant": "acme",
          "premium_member": true,
          "year": 2025,
          "prior_decision": null
        }
      },
      "Actor": {
        "type": "string",
        "description": "Best-effort label identifying the user or credential that initiated the extraction. For extractions you initiate in the Sensible app, the value is your account's email address. For extractions you initiate by using your API key, the value is `'api_key: <key name>'` if the key has a name, or `'api_key'` if unnamed. For extractions you initiate in the Sensible app while logged in with legacy credentials, the value is `'legacy: <account>'`. Sensible omits this field for extractions you initiate with email processors.",
        "example": "api_key: my production key",
        "x-internal-note": "auth_token-initiated extractions are not a real code path; omitting the 'auth_token: <creator>' case from the public description deliberately."
      },
      "ContentTypeResponse": {
        "type": "string",
        "description": "The content type of the document.\n",
        "example": "image/png"
      },
      "EnvironmentResponse": {
        "description": "Name of the environment to which the configuration used by this extraction was published.",
        "example": "production",
        "type": "string",
        "enum": [
          "production",
          "development"
        ]
      },
      "ExtractFromUrlPortfolioResponse": {
        "type": "object",
        "properties": {
          "id": {
            "$ref": "#/components/schemas/ExtractionId"
          },
          "created": {
            "$ref": "#/components/schemas/ExtractionCreated"
          },
          "status": {
            "$ref": "#/components/schemas/ExtractionStatus"
          },
          "types": {
            "$ref": "#/components/schemas/DocumentTypeNames"
          },
          "segment_documents_with": {
            "$ref": "#/components/schemas/SegmentDocumentsWith"
          },
          "environment": {
            "$ref": "#/components/schemas/EnvironmentResponse"
          },
          "content_type": {
            "$ref": "#/components/schemas/ContentTypeResponse"
          },
          "actor": {
            "$ref": "#/components/schemas/Actor"
          }
        }
      },
      "ExtractFromUrlPortfolioRequest": {
        "type": "object",
        "x-internal-note": "ocr_engine and ocr_every_page are accepted by the backend (src/api/extract-from-url/handler.ts:92-95,100-102) but deliberately not documented publicly.",
        "properties": {
          "document_url": {
            "$ref": "#/components/schemas/DocumentUrl"
          },
          "types": {
            "$ref": "#/components/schemas/DocumentTypeNames"
          },
          "content_type": {
            "$ref": "#/components/schemas/ContentTypeParameter"
          },
          "segment_documents_with": {
            "$ref": "#/components/schemas/SegmentDocumentsWith"
          },
          "webhook": {
            "$ref": "#/components/schemas/Webhook"
          },
          "extra_data": {
            "$ref": "#/components/schemas/ExtraDataRecord"
          }
        },
        "required": [
          "types",
          "document_url"
        ]
      },
      "ExtractionId": {
        "type": "string",
        "format": "uuid",
        "description": "Unique ID for the extraction, used to retrieve the extraction",
        "example": "246a6f60-0e5b-11eb-b720-295a6fba723e"
      },
      "ExtractionCreated": {
        "type": "string",
        "format": "date-time",
        "example": "2022-10-31T16:27:53.433",
        "description": "Date and time Sensible created the initial empty extraction and set its status to WAITING."
      },
      "ExtractionStatus": {
        "type": "string",
        "description": "Status of the extraction:\n- WAITING: Sensible created an initial empty extraction and is waiting for the document.\n- PROCESSING: Sensible received the document and is extracting data.\n- FAILED: The extraction failed.\n- COMPLETE: The extraction is complete.\n",
        "enum": [
          "WAITING",
          "PROCESSING",
          "COMPLETE",
          "FAILED"
        ],
        "example": "COMPLETE"
      },
      "DocumentTypeNames": {
        "type": "array",
        "description": "Specifies the document types contained in the PDF portfolio.",
        "example": [
          "tax_returns",
          "bank_statements",
          "credit_reports"
        ],
        "items": {
          "type": "string"
        }
      },
      "SegmentDocumentsWith": {
        "type": "string",
        "enum": [
          "llm",
          "fingerprints"
        ],
        "default": "fingerprints",
        "example": "llm",
        "description": "Specifies how to segment the page ranges of the documents in the portfolio. For more information, see [Multi-document extraction](https://docs.sensible.so/docs/portfolio)."
      },
      "Webhook": {
        "type": "object",
        "description": "Pushes extraction results to the specified webhook under the following circumstances, so you don't have to poll for results status:\n  - When Sensible sets `\"status\": \"COMPLETE\"` or `\"status\": \"FAILED\"` \n  - When the value for `reviewStatus` (for portfolio extractions) or `reviewStatus` (for single-document extractions) changes.",
        "properties": {
          "url": {
            "type": "string",
            "format": "url",
            "description": "Webhook destination. Sensible will POST to this URL when the extraction is complete.",
            "example": "https://example.com/example_webhook_url"
          },
          "payload": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              },
              {
                "type": "boolean"
              },
              {
                "type": "array",
                "items": {}
              },
              {
                "type": "object",
                "additionalProperties": true
              }
            ],
            "description": "Information additional to the API response, for example a UUID for verification. Can be any of the following types - [string, number, boolean, array, object].",
            "example": "info extra to the default extraction payload"
          }
        }
      },
      "DocumentUrl": {
        "type": "string",
        "format": "url",
        "description": "URL that responds to a GET request with the bytes of the document you want to extract data from. This URL must be either publicly accessible, or presigned with a security token as part of the URL path. To check if the URL meets these criteria, open the URL with a web browser. The browser must either render the document as a full-page view with no other data, or download the document, without prompting for authentication.",
        "example": "https://raw.githubusercontent.com/sensible-hq/sensible-docs/v0/assets/pdfs/auto_insurance_anyco.pdf"
      },
      "ContentTypeParameter": {
        "type": "string",
        "enum": [
          "application/pdf",
          "image/jpeg",
          "image/png",
          "image/tiff",
          "application/msword",
          "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
          "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
          "application/vnd.ms-excel",
          "application/vnd.ms-excel.sheet.macroenabled.12",
          "text/csv",
          "application/json"
        ],
        "description": "Content type of the document being presented for extraction. Required for the CSV file type."
      }
    }
  }
}
```