Invoice Data Extraction Uploads API

Register the files of one upload session, upload each file in parts to signed URLs, and complete each file. Every upload endpoint shares one rate limit of 600 requests per minute per API key.

GitHub OpenAPI

Operations 4

POST /uploads/sessions Create an upload session #
POST /uploads/sessions/{upload_session_id}/parts Get signed upload URLs for a file's parts #
POST /uploads/sessions/{upload_session_id}/complete Complete a file's upload #
POST /uploads/import Import files attached in an assistant's conversation #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/invoicedataextraction-com:invoicedataextraction-com-uploads-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

invoicedataextraction-com-uploads-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Invoice Data Extraction Uploads API
  version: 1.0.0
  summary: Turn invoices and other financial documents into structured rows and spreadsheets.
  description: 'Upload files, submit an extraction that says in plain words what to extract, wait for it

    to finish, and read the rows as JSON or download an XLSX, CSV or JSON file. An extraction

    submitted with `options.ask_questions` can stop and ask when the documents leave something

    unsettled; the caller answers and the extraction continues.


    This document is the contract as the API enforces it. The prose reference, with the same

    facts told in order, is https://invoicedataextraction.com/docs/api (Markdown:

    https://invoicedataextraction.com/docs/api.md). What changed and when, and how the API is

    versioned, is https://invoicedataextraction.com/docs/changelog. A guide for AI agents and the

    people directing them is https://invoicedataextraction.com/docs/agents.


    Every request may carry `X-SDK-Name` (up to 32 characters) naming the client it comes from,

    and `X-SDK-Version` with its version; the official SDKs send `node` and `python`.'
  termsOfService: https://invoicedataextraction.com/terms-of-service
  contact:
    name: Invoice Data Extraction support
    email: support@invoicedataextraction.com
    url: https://invoicedataextraction.com/contact
servers:
- url: https://api.invoicedataextraction.com/v1
security:
- bearerAuth: []
tags:
- name: Uploads
  description: Register the files of one upload session, upload each file in parts to signed URLs, and complete each file. Every upload endpoint shares one rate limit of 600 requests per minute per API key.
paths:
  /uploads/sessions:
    post:
      tags:
      - Uploads
      operationId: createUploadSession
      summary: Create an upload session
      description: 'Registers one or more files (1 to 6,000) and returns the part size to use when

        uploading each of them. Every file needs at least one available credit at this point,

        so an account with fewer available credits than files is refused with

        `INSUFFICIENT_CREDITS`.


        Idempotent: a retry with the same `upload_session_id` and the same files returns the

        existing session. The same `upload_session_id` with different files is

        `SESSION_ALREADY_INITIALIZED`.


        Rate limit: 600 requests per minute per API key, shared by the upload endpoints.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUploadSessionRequest'
            example:
              upload_session_id: sess_001
              files:
              - file_id: file_001
                file_name: invoice-1.pdf
                file_size_bytes: 120450
              - file_id: file_002
                file_name: receipt.jpg
                file_size_bytes: 84200
      responses:
        '200':
          description: The session, with the part size to upload each file in.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateUploadSessionResponse'
              example:
                success: true
                upload_session_id: sess_001
                files:
                - file_id: file_001
                  file_name: invoice-1.pdf
                  part_size: 8388608
                - file_id: file_002
                  file_name: receipt.jpg
                  part_size: 8388608
        '400':
          description: '`INVALID_INPUT` (the body does not match the schema; `details.issues` names each problem), `DUPLICATE_FILE_NAME`, `DUPLICATE_FILE_ID`, `FILE_TOO_LARGE` (a PDF over 150 MB or an image over 5 MB; `details` names the file and the limit) or `TOTAL_UPLOAD_SIZE_LIMIT_EXCEEDED` (the files together exceed 2 GB).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '402':
          description: '`INSUFFICIENT_CREDITS`: fewer available credits than files. `details` carries `credits_balance`, `credits_reserved` and `file_count`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '409':
          description: '`SESSION_ALREADY_INITIALIZED`: this `upload_session_id` is in use with different files.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /uploads/sessions/{upload_session_id}/parts:
    post:
      tags:
      - Uploads
      operationId: getUploadPartUrls
      summary: Get signed upload URLs for a file's parts
      description: 'Returns one signed URL per requested part number. `PUT` the raw bytes of each part to

        its URL with no extra headers, and keep the `ETag` response header of each `PUT`

        (quotes included) for completing the file.


        `total_parts = ceil(file_size_bytes / part_size)`; a file smaller than `part_size` is

        one part. Up to 1,000 part numbers per request, each from 1 to 10,000; a URL is valid

        for 15 minutes from issue, so for a very large file request URLs in batches just before

        uploading each batch.


        Rate limit: 600 requests per minute per API key, shared by the upload endpoints.'
      parameters:
      - $ref: '#/components/parameters/UploadSessionId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GetUploadPartUrlsRequest'
            examples:
              singlePart:
                summary: A file smaller than the part size
                value:
                  file_id: file_001
                  part_numbers:
                  - 1
              multiPart:
                summary: A 20 MB file at an 8 MB part size
                value:
                  file_id: file_002
                  part_numbers:
                  - 1
                  - 2
                  - 3
      responses:
        '200':
          description: The signed URLs, one per part number requested.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetUploadPartUrlsResponse'
              example:
                success: true
                upload_session_id: sess_001
                file_id: file_001
                file_name: invoice-1.pdf
                part_size: 8388608
                part_urls:
                - part_number: 1
                  url: https://storage.example.com/...?X-Amz-Signature=...
        '400':
          $ref: '#/components/responses/InvalidInput'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '404':
          description: '`FILE_NOT_FOUND`: this `file_id` was not registered when the session was created.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '409':
          description: '`FILE_NOT_UPLOADABLE`: the file has already been completed or aborted.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /uploads/sessions/{upload_session_id}/complete:
    post:
      tags:
      - Uploads
      operationId: completeFileUpload
      summary: Complete a file's upload
      description: 'Finalises one file from the ETags of its uploaded parts. Call it once per file. A file

        that is already complete returns success again, so a retry after a dropped connection

        is safe.


        Rate limit: 600 requests per minute per API key, shared by the upload endpoints.'
      parameters:
      - $ref: '#/components/parameters/UploadSessionId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CompleteFileUploadRequest'
            example:
              file_id: file_001
              parts:
              - part_number: 1
                e_tag: '"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4"'
      responses:
        '200':
          description: The file is complete and can be named in an extraction.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CompleteFileUploadResponse'
              example:
                success: true
                upload_session_id: sess_001
                file_id: file_001
                file_name: invoice-1.pdf
        '400':
          description: '`INVALID_INPUT`, or `INVALID_COMPLETION_PARTS` (the parts do not cover 1 to the part count with their ETags; `details.reason` says which check failed).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '404':
          description: '`FILE_NOT_FOUND`: this `file_id` was not registered when the session was created.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '409':
          description: '`FILE_ABORTED` (the file was aborted; upload it in a new session) or `UPLOAD_ID_NOT_FOUND` (the session is no longer available).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '422':
          description: '`OBJECT_SIZE_MISMATCH`: the uploaded bytes differ from the declared `file_size_bytes`; `details` carries `declared_size_bytes` and `actual_size_bytes`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          description: '`UPLOAD_COMPLETE_FAILED`: completing the file failed on our side; retryable.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
  /uploads/import:
    post:
      tags:
      - Uploads
      operationId: importAttachedFiles
      summary: Import files attached in an assistant's conversation
      description: 'In place of creating a session, uploading parts and completing each file, when the

        files are attached in a conversation with an assistant that gives your app a download

        link for each one, as ChatGPT does: creates the session, uploads every file (1 to 100)

        from its link and completes it, in one call. The session''s limits apply, and every

        file needs one available credit. The links expire a few minutes after the assistant

        gives them. When any link cannot be read, nothing is created and nothing is charged.


        Files are independent: one that cannot be uploaded carries its `error` and is listed

        in `failed_file_ids`, and the completed files can be submitted. A retry with the same

        `upload_session_id` and files continues the session, and a file already completed is

        not uploaded again.


        Rate limit: 20 requests per minute per API key.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required:
              - upload_session_id
              - files
              properties:
                upload_session_id:
                  type: string
                  pattern: ^[A-Za-z0-9._:-]{1,200}$
                  description: Your identifier for the session; a retry with the same one and the same files continues it.
                files:
                  type: array
                  minItems: 1
                  maxItems: 100
                  items:
                    type: object
                    additionalProperties: false
                    required:
                    - file_id
                    - file_name
                    - url
                    properties:
                      file_id:
                        type: string
                        pattern: ^[A-Za-z0-9._:-]{1,200}$
                      file_name:
                        type: string
                        description: The file name, ending in .pdf, .jpg, .jpeg or .png.
                      url:
                        type: string
                        format: uri
                        description: The download link the assistant gave for the attached file. Links from ChatGPT are accepted.
            example:
              upload_session_id: sess_002
              files:
              - file_id: file_001
                file_name: invoice-1.pdf
                url: https://files.oaiusercontent.com/file-abc123
      responses:
        '200':
          description: The session, with each file's outcome.
          content:
            application/json:
              schema:
                type: object
                required:
                - success
                - upload_session_id
                - files
                - completed_file_ids
                - failed_file_ids
                properties:
                  success:
                    type: boolean
                  upload_session_id:
                    type: string
                  files:
                    type: array
                    items:
                      type: object
                      required:
                      - file_id
                      - file_name
                      - success
                      properties:
                        file_id:
                          type: string
                        file_name:
                          type: string
                        success:
                          type: boolean
                        error:
                          type: object
                          description: Present when the file could not be uploaded.
                          properties:
                            code:
                              type: string
                            message:
                              type: string
                            retryable:
                              type: boolean
                            details:
                              type: object
                              nullable: true
                  completed_file_ids:
                    type: array
                    items:
                      type: string
                  failed_file_ids:
                    type: array
                    items:
                      type: string
              example:
                success: true
                upload_session_id: sess_002
                files:
                - file_id: file_001
                  file_name: invoice-1.pdf
                  success: true
                completed_file_ids:
                - file_001
                failed_file_ids: []
        '400':
          description: '`INVALID_INPUT` (a link that is not an assistant''s download link for an attached file among them), or the session''s own refusals (`DUPLICATE_FILE_NAME`, `DUPLICATE_FILE_ID`, `FILE_TOO_LARGE`, `TOTAL_UPLOAD_SIZE_LIMIT_EXCEEDED`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '402':
          description: '`INSUFFICIENT_CREDITS`: fewer available credits than files.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '409':
          description: '`SESSION_ALREADY_INITIALIZED`: this `upload_session_id` is in use.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '422':
          description: '`FILE_LINK_UNREADABLE`: a link could not be read, most often because it expired; `details.file_names` names the files. Attach them again and import with the new links.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  responses:
    InternalError:
      description: '`INTERNAL_ERROR`: retry after a short delay.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    Unauthenticated:
      description: '`UNAUTHENTICATED`, `API_KEY_EXPIRED` or `API_KEY_REVOKED`.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    RateLimited:
      description: '`RATE_LIMITED`: wait `details.retry_after_seconds` (also the `Retry-After` header), then retry.'
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    InvalidInput:
      description: '`INVALID_INPUT`: the request does not match the contract; `details.issues` lists each problem with its `path`.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            success: false
            error:
              code: INVALID_INPUT
              message: Request validation failed. Check details for specific issues.
              retryable: false
              details:
                issues:
                - message: 'file_name must end with a supported extension: .pdf, .jpg, .jpeg, or .png.'
                  path:
                  - files
                  - 0
                  - file_name
  schemas:
    CreateUploadSessionResponse:
      type: object
      required:
      - success
      - upload_session_id
      - files
      properties:
        success:
          type: boolean
          const: true
        upload_session_id:
          $ref: '#/components/schemas/ClientId'
        files:
          type: array
          items:
            type: object
            required:
            - file_id
            - file_name
            - part_size
            properties:
              file_id:
                $ref: '#/components/schemas/ClientId'
              file_name:
                type: string
              part_size:
                type: integer
                description: The part size in bytes to split the file into; the same for every file in the session (8,388,608 today). A file smaller than this is one part.
    UploadSessionFileRequest:
      type: object
      additionalProperties: false
      required:
      - file_id
      - file_name
      - file_size_bytes
      properties:
        file_id:
          $ref: '#/components/schemas/ClientId'
        file_name:
          type: string
          minLength: 1
          maxLength: 200
          description: The file name with its extension, one of `.pdf`, `.jpg`, `.jpeg`, `.png`; no control characters, slashes or surrounding whitespace. Unique within the session.
        file_size_bytes:
          type: integer
          minimum: 1
          description: The exact size in bytes. PDFs up to 150 MB (157,286,400 bytes) and up to 5,000 pages; images up to 5 MB (5,242,880 bytes); all files of a session together up to 2 GB.
    ErrorEnvelope:
      type: object
      required:
      - success
      - error
      properties:
        success:
          type: boolean
          const: false
        error:
          $ref: '#/components/schemas/ErrorObject'
    ErrorCode:
      type: string
      description: The codes an error envelope can carry, from any endpoint.
      enum:
      - UNAUTHENTICATED
      - API_KEY_EXPIRED
      - API_KEY_REVOKED
      - INVALID_INPUT
      - EXTRACTION_NOT_FOUND
      - INTERNAL_ERROR
      - SUBMISSIONS_PAUSED
      - SUBMISSION_ID_ALREADY_USED
      - DUPLICATE_FILE_NAME
      - DUPLICATE_FILE_ID
      - FILE_TOO_LARGE
      - TOTAL_UPLOAD_SIZE_LIMIT_EXCEEDED
      - SESSION_ALREADY_INITIALIZED
      - FILE_NOT_FOUND
      - FILE_NOT_UPLOADABLE
      - FILE_ABORTED
      - INVALID_COMPLETION_PARTS
      - OBJECT_SIZE_MISMATCH
      - UPLOAD_ID_NOT_FOUND
      - INSUFFICIENT_CREDITS
      - UPLOAD_COMPLETE_FAILED
      - OUTPUT_NOT_AVAILABLE
      - OUTPUT_EXPIRED
      - EXTRACTION_IN_PROGRESS
      - EXTRACTION_NOT_CANCELLABLE
      - NOT_FOUND
      - FORBIDDEN
      - RATE_LIMITED
    CreateUploadSessionRequest:
      type: object
      additionalProperties: false
      required:
      - upload_session_id
      - files
      properties:
        upload_session_id:
          $ref: '#/components/schemas/ClientId'
        files:
          type: array
          minItems: 1
          maxItems: 6000
          items:
            $ref: '#/components/schemas/UploadSessionFileRequest'
    GetUploadPartUrlsResponse:
      type: object
      required:
      - success
      - upload_session_id
      - file_id
      - file_name
      - part_size
      - part_urls
      properties:
        success:
          type: boolean
          const: true
        upload_session_id:
          $ref: '#/components/schemas/ClientId'
        file_id:
          $ref: '#/components/schemas/ClientId'
        file_name:
          type: string
        part_size:
          type: integer
        part_urls:
          type: array
          items:
            type: object
            required:
            - part_number
            - url
            properties:
              part_number:
                type: integer
              url:
                type: string
                format: uri
                description: '`PUT` the part''s raw bytes here within 15 minutes; keep the `ETag` response header.'
    CompleteFileUploadRequest:
      type: object
      additionalProperties: false
      required:
      - file_id
      - parts
      properties:
        file_id:
          $ref: '#/components/schemas/ClientId'
        parts:
          type: array
          minItems: 1
          maxItems: 10000
          items:
            type: object
            additionalProperties: false
            required:
            - part_number
            - e_tag
            properties:
              part_number:
                type: integer
                minimum: 1
                maximum: 10000
              e_tag:
                type: string
                minLength: 1
                description: The `ETag` header of the part's `PUT` response, quotes included.
    CompleteFileUploadResponse:
      type: object
      required:
      - success
      - upload_session_id
      - file_id
      - file_name
      properties:
        success:
          type: boolean
          const: true
        upload_session_id:
          $ref: '#/components/schemas/ClientId'
        file_id:
          $ref: '#/components/schemas/ClientId'
        file_name:
          type: string
        status:
          type: string
          const: completed
          description: Present when this call completed the file; absent when the file was already complete.
    ErrorObject:
      type: object
      required:
      - code
      - message
      - retryable
      - details
      properties:
        code:
          $ref: '#/components/schemas/ErrorCode'
        message:
          type: string
          description: What happened and what to do next, in plain words.
        retryable:
          type: boolean
          description: '`true` when the same request may succeed after a short delay; `false` when the request itself must change.'
        details:
          description: 'Always present: `null`, or an object with error-specific context. `INVALID_INPUT` carries `issues`, each with a `message` and a `path`; `RATE_LIMITED` carries `retry_after_seconds`.'
          oneOf:
          - type: object
            additionalProperties: true
          - type: 'null'
    GetUploadPartUrlsRequest:
      type: object
      additionalProperties: false
      required:
      - file_id
      - part_numbers
      properties:
        file_id:
          $ref: '#/components/schemas/ClientId'
        part_numbers:
          type: array
          minItems: 1
          maxItems: 1000
          items:
            type: integer
            minimum: 1
            maximum: 10000
          description: 1-indexed part numbers; duplicates are ignored.
    ClientId:
      type: string
      pattern: ^[A-Za-z0-9._:-]{1,200}$
      description: 'An identifier you choose: 1 to 200 characters from letters, digits, dots, underscores, colons and hyphens. Used for `upload_session_id`, `file_id` and `submission_id`.'
  parameters:
    UploadSessionId:
      name: upload_session_id
      in: path
      required: true
      description: The `upload_session_id` you chose when creating the session.
      schema:
        $ref: '#/components/schemas/ClientId'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: 'An API key created at https://invoicedataextraction.com/dashboard?view=API, sent as `Authorization: Bearer YOUR_API_KEY`. Every account includes 50 free pages a month. A team admin''s key sees the team''s extractions by default (the `scope` parameter).'
externalDocs:
  description: REST API reference
  url: https://invoicedataextraction.com/docs/api