API ReferenceUploads
API Reference

Uploads

Upload files, then reference them by ID when loading a managed table. You create an upload session, then PUT the bytes straight to the URL it returns (so the file never passes through this API), and finalize.

Upload flow

  1. POST /v1/uploads — returns a one-time finalize_token plus one of three shapes: • a single url (mode: single) — PUT the whole file to it; • a list of part_urls (mode: multipart) when you declared the size — PUT each part to its entry; • when you omit declared_size_bytes (streaming), mode: multipart with a part_size but no part_urls — call POST /v1/uploads/{upload_id}/parts with a batch of part_numbers to get a PUT URL for each part you're about to upload.
  2. PUT the bytes: the whole file to url, or each part to its URL (keeping each part's ETag). For a streaming upload, slice the file into part_size chunks and mint part URLs as you go.
  3. POST /v1/uploads/{upload_id}/finalize with the token (and, for multipart, the part ETags) to confirm the upload and make it usable.

Which approach to use

  • One file, or a handful — create a session, PUT, then finalize.
  • Many small filesPOST /v1/uploads/batch creates up to 100 sessions in one call; upload and finalize each one independently, at your own pace.
  • A large file — small files are returned as a single PUT; larger files are returned as a multi-part upload automatically, which you can upload in parallel and resume part by part.

Create upload session

POST/v1/uploads

Create an upload session for a file you will upload directly to the URL the response carries, without sending it through this API. The response is one of three shapes. For a small file (mode: single) it contains a short-lived url to PUT the whole file to. For a large file with a known size (mode: multipart) it contains part_urls and part_size: split the file into part_size-byte chunks (the last is the remainder) and PUT chunk i (1-based) to part_urls[i - 1], keeping each response's ETag. Slice by part_size, not by an even division across the number of part URLs (which can make a non-final part too small). For a file whose size you do not know up front, omit declared_size_bytes: the response is mode: multipart with a part_size but NO part_urls. As you stream, call POST /v1/uploads/{upload_id}/parts with a batch of part_numbers to mint per-part PUT URLs, PUT each part and keep its ETag, then finalize as for any multipart upload. In all cases the response also includes a one-time finalize_token. After uploading, call the finalize endpoint with the token (and, for multipart, the {part_number, e_tag} list) to make the upload usable as managed-table contents. The returned upload ID can then be passed to the managed-table load endpoint.

You may hint a preferred part size with part_size; the service clamps it to the allowed range and ignores it for single-PUT uploads.

A 501 with error code PRESIGN_UNSUPPORTED means this deployment cannot issue upload URLs; send the data inline on the load endpoint instead.

Request body

  • checksum_algo string,null — Integrity checksum algorithm you are volunteering for this file. Currently only sha256 is accepted. Optional; pair with checksum_value.
  • checksum_value string,null — Integrity checksum value, paired with checksum_algo. Optional.
  • content_encoding string,null — Content encoding to record for the uploaded file (for example gzip). Optional.
  • content_type string,null — Content type to record for the uploaded file (for example the Parquet, CSV, or JSON MIME type). Optional.
  • declared_size_bytes integer,null — The exact size, in bytes, of the file you will upload. Optional. When provided, it is validated at create time against the maximum allowed size, and again at finalize against the bytes actually uploaded — a mismatch fails the finalize. Omit it to create a streaming (unknown-size) upload: the session is always multi-part and returns no part URLs up front; instead you mint part URLs on demand from POST /v1/uploads/{upload_id}/parts as you upload, and finalize validates only that the file is non-empty. Min: 0
  • filename string,null — Original file name, recorded with the upload for your own bookkeeping. Optional and advisory — it does not affect how the file is uploaded or loaded.
  • part_size integer,null — Preferred size, in bytes, of each part for a large (multi-part) upload. Optional hint — the service clamps it to the allowed part-size range and to the maximum number of parts, and ignores it for small files uploaded with a single PUT. Omit to let the service choose. Min: 0
{
  "checksum_algo": "sha256",
  "checksum_value": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
  "content_type": "application/vnd.apache.parquet",
  "declared_size_bytes": 10485760,
  "filename": "orders.parquet",
  "part_size": 8388608
}

Response 201 — Upload session created

  • finalize_token stringrequired. One-time token that authorizes finalizing this upload. Returned exactly once at create time — store it; it cannot be retrieved again.
  • headers objectrequired. Headers you must send verbatim with each PUT. Currently always empty; present so a future mode can require signed headers without changing the response shape.
  • mode stringrequired. Upload mode: single (upload the whole file with one PUT to url) or multipart (upload each part with one PUT to the matching entry in part_urls). Modeled as a string so additional modes can be added later without breaking clients.
  • part_size integer,null — For a multipart upload (both known-size and streaming), the size in bytes to split the file into: send bytes [(i-1) * part_size, i * part_size) as part i, with the last part carrying the remainder. Slice by this value — do not divide the file evenly by the number of parts, which can make a non-final part smaller than the 5 MiB minimum for a non-final part (the upload then fails at finalize). Absent for single uploads. Min: 0
  • part_urls string[] | null — For a known-size multipart upload, the per-part URLs in ascending part order: PUT your file's part i (1-based) to part_urls[i - 1] and keep each response's ETag, then pass the {part_number, e_tag} list to finalize. Absent for single uploads, and also absent for a streaming (unknown-size) multipart upload — there, mint part URLs on demand via POST /v1/uploads/{upload_id}/parts.
  • upload_id stringrequired. Identifier for this upload. Pass it to the finalize endpoint and to the managed-table load endpoint once finalized.
  • url string,null — The URL to PUT the raw file bytes to, for a single upload. Short-lived — upload promptly and finalize. Absent for multipart uploads (use part_urls).
{
  "finalize_token": "string",
  "headers": {},
  "mode": "string",
  "part_size": 0,
  "part_urls": [
    "string"
  ],
  "upload_id": "string",
  "url": "string"
}

Errors

StatusDescription
400Invalid request (e.g. file too large, unsupported checksum algorithm)
501This deployment cannot issue upload URLs

Create upload sessions in bulk

POST/v1/uploads/batch

Create upload sessions for several files in one request. Each file is planned independently and the response returns one session per requested file, in the same order. Each session is finalized separately via the finalize endpoint, so you can upload and finalize files at your own pace.

A 501 with error code PRESIGN_UNSUPPORTED means this deployment cannot issue upload URLs; send the data inline on the load endpoint instead.

Request body

  • uploads CreateUploadRequest[] — required
    • checksum_algo string,null — Integrity checksum algorithm you are volunteering for this file. Currently only sha256 is accepted. Optional; pair with checksum_value.
    • checksum_value string,null — Integrity checksum value, paired with checksum_algo. Optional.
    • content_encoding string,null — Content encoding to record for the uploaded file (for example gzip). Optional.
    • content_type string,null — Content type to record for the uploaded file (for example the Parquet, CSV, or JSON MIME type). Optional.
    • declared_size_bytes integer,null — The exact size, in bytes, of the file you will upload. Optional. When provided, it is validated at create time against the maximum allowed size, and again at finalize against the bytes actually uploaded — a mismatch fails the finalize. Omit it to create a streaming (unknown-size) upload: the session is always multi-part and returns no part URLs up front; instead you mint part URLs on demand from POST /v1/uploads/{upload_id}/parts as you upload, and finalize validates only that the file is non-empty. Min: 0
    • filename string,null — Original file name, recorded with the upload for your own bookkeeping. Optional and advisory — it does not affect how the file is uploaded or loaded.
    • part_size integer,null — Preferred size, in bytes, of each part for a large (multi-part) upload. Optional hint — the service clamps it to the allowed part-size range and to the maximum number of parts, and ignores it for small files uploaded with a single PUT. Omit to let the service choose. Min: 0
{
  "uploads": [
    {
      "content_type": "text/csv",
      "declared_size_bytes": 4096,
      "filename": "orders.csv"
    },
    {
      "content_type": "text/csv",
      "declared_size_bytes": 2048,
      "filename": "customers.csv"
    }
  ]
}

Response 201 — Upload sessions created

  • uploads UploadSessionResponse[] — required
    • finalize_token stringrequired. One-time token that authorizes finalizing this upload. Returned exactly once at create time — store it; it cannot be retrieved again.
    • headers objectrequired. Headers you must send verbatim with each PUT. Currently always empty; present so a future mode can require signed headers without changing the response shape.
    • mode stringrequired. Upload mode: single (upload the whole file with one PUT to url) or multipart (upload each part with one PUT to the matching entry in part_urls). Modeled as a string so additional modes can be added later without breaking clients.
    • part_size integer,null — For a multipart upload (both known-size and streaming), the size in bytes to split the file into: send bytes [(i-1) * part_size, i * part_size) as part i, with the last part carrying the remainder. Slice by this value — do not divide the file evenly by the number of parts, which can make a non-final part smaller than the 5 MiB minimum for a non-final part (the upload then fails at finalize). Absent for single uploads. Min: 0
    • part_urls string[] | null — For a known-size multipart upload, the per-part URLs in ascending part order: PUT your file's part i (1-based) to part_urls[i - 1] and keep each response's ETag, then pass the {part_number, e_tag} list to finalize. Absent for single uploads, and also absent for a streaming (unknown-size) multipart upload — there, mint part URLs on demand via POST /v1/uploads/{upload_id}/parts.
    • upload_id stringrequired. Identifier for this upload. Pass it to the finalize endpoint and to the managed-table load endpoint once finalized.
    • url string,null — The URL to PUT the raw file bytes to, for a single upload. Short-lived — upload promptly and finalize. Absent for multipart uploads (use part_urls).
{
  "uploads": [
    {
      "finalize_token": "string",
      "headers": {},
      "mode": "string",
      "part_size": 0,
      "part_urls": [
        "string"
      ],
      "upload_id": "string",
      "url": "string"
    }
  ]
}

Errors

StatusDescription
400Invalid request (e.g. a file too large, unsupported checksum algorithm)
501This deployment cannot issue upload URLs

Finalize upload

POST/v1/uploads/{upload_id}/finalize

Confirm that a file has been uploaded and make it usable as managed-table contents. Supply the finalize_token returned when the session was created, in the X-Upload-Finalize-Token header. When you declared a size at create time, the uploaded file's size is validated against it and a mismatch is rejected. An upload created without a declared size is finalized from its uploaded parts; it must be non-empty and is rejected if it exceeds the server's maximum upload size. Finalize is exactly-once: a second finalize of the same upload is rejected.

Path parameters

  • upload_id stringrequired. Upload session ID returned at create time

Headers

  • X-Upload-Finalize-Token stringrequired. One-time finalize token returned when the session was created

Request body

  • parts FinalizeUploadPart[] | null — Parts to assemble, for a multi-part upload. Omit for single-PUT uploads (the common case).
    • e_tag stringrequired. The ETag response header returned by that part's PUT.
    • part_number integerrequired. The 1-based part number you uploaded this part as.
{
  "parts": [
    {
      "e_tag": "\"9f8c1e5b7a2d4f60b3c8e1a9d7f4b206\"",
      "part_number": 1
    }
  ]
}

Response 200 — Upload finalized

  • content_type string,null
  • created_at stringrequired
  • size_bytes integerrequired. The validated size of the uploaded file in bytes.
  • status stringrequired
  • upload_id stringrequired
{
  "content_type": "string",
  "created_at": "2026-01-01T00:00:00Z",
  "size_bytes": 0,
  "status": "string",
  "upload_id": "string"
}

Errors

StatusDescription
400Invalid finalize token, uploaded size mismatch, missing file, or upload not finalizable
404Upload session not found

Mint upload part URLs

POST/v1/uploads/{upload_id}/parts

Get short-lived upload URLs for specific parts of a multi-part upload. This is required for a streaming (unknown-size) upload — created by omitting the declared size — which mints no part URLs up front. It also works for a known-size multi-part upload: use it to re-mint a part whose URL expired before you uploaded that part. Supply the finalize_token returned when the session was created, in the X-Upload-Finalize-Token header, and the 1-based part_numbers you want URLs for. PUT each part's bytes to its URL, keep each response's ETag, then pass the {part_number, e_tag} list to finalize. You may mint parts in batches as you upload, and re-mint a part number whose URL expired before you finished uploading it.

Path parameters

  • upload_id stringrequired. Upload session ID returned at create time

Headers

  • X-Upload-Finalize-Token stringrequired. One-time finalize token returned when the session was created

Request body

  • part_numbers integer[] — required. The 1-based part numbers to mint URLs for. Must be non-empty; each number must be between 1 and the maximum number of parts allowed.
{
  "part_numbers": [
    1,
    2,
    3
  ]
}

Response 200 — Minted part URLs

  • parts MintedUploadPartResponse[] — required. The minted part URLs, in ascending part-number order. PUT each part's bytes to its URL and keep the response's ETag to pass to finalize.
    • part_number integerrequired. The 1-based part number this URL is for.
    • url stringrequired. Short-lived URL to PUT this part's bytes to. Keep the response's ETag and pass the {part_number, e_tag} pair to finalize.
{
  "parts": [
    {
      "part_number": 0,
      "url": "string"
    }
  ]
}

Errors

StatusDescription
400Invalid finalize token, invalid part numbers, batch too large, or the upload is not a multi-part upload
404Upload session not found
501This deployment cannot issue upload URLs