> ## Documentation Index
> Fetch the complete documentation index at: https://docs.squarecloud.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Start a chunked upload

> Open a chunked upload for files of up to 10 GiB with POST /v1/objects/chunked and get the upload token for the next calls.

<ParamField header="Authorization" type="string" placeholder="API Key" required>
  The API key for your account. You can find this in your [account settings](https://squarecloud.app/en/account/security).
</ParamField>

Chunked Init opens a chunked upload, the flow for files above the 100 MB single-request limit, up to **10 GiB**. It returns an opaque `upload` token that you carry through the next calls:

<Steps>
  <Step title="Init">This route. Keep the `upload` token.</Step>
  <Step title="Send the parts">[Chunked Part](/en/blob-reference/endpoint/chunked-part), in any order, up to 6 at a time.</Step>
  <Step title="Complete">[Chunked Complete](/en/blob-reference/endpoint/chunked-complete) seals the file. Or [Chunked Abort](/en/blob-reference/endpoint/chunked-abort) to cancel.</Step>
</Steps>

There is no server-side session: the token is the whole upload state, so persist it if the upload may span a page reload. Lost it? [Open Uploads](/en/blob-reference/endpoint/chunked-open) lists the open uploads with their tokens, and [Chunked Status](/en/blob-reference/endpoint/chunked-status) tells which parts already arrived. Uploads idle for 24 hours are aborted automatically.

## Parameters

The query parameters are the same as in [Object Post](/en/blob-reference/endpoint/post), plus `filename` and `mime_type`: there is no multipart envelope here, so the extension has to come from the query. Requires the `blob:write` scope, or an [upload token](/en/blob-reference/endpoint/upload-tokens), and an active plan.

<ParamField query="name" type="string" placeholder="dataset" required>
  The file name, without extension. Same pattern as in Object Post.
</ParamField>

<ParamField query="filename" type="string" placeholder="reads.fastq.gz">
  The original file name, extension included. The stored extension comes from it, then from `mime_type`, then falls back to `bin`.
</ParamField>

<ParamField query="mime_type" type="string" placeholder="application/gzip">
  Used for the extension only when `filename` has none.
</ParamField>

<ParamField query="prefix" type="string">
  Same as in Object Post.
</ParamField>

<ParamField query="private" type="boolean">
  Same as in Object Post.
</ParamField>

<ParamField query="expire" type="string">
  Same as in Object Post.
</ParamField>

<ParamField query="security_hash" type="boolean">
  Same as in Object Post.
</ParamField>

<ParamField query="overwrite" type="boolean">
  Same as in Object Post.
</ParamField>

<ParamField query="disposition" type="string">
  Same as in Object Post.
</ParamField>

<ParamField query="auto_download" type="boolean">
  Same as in Object Post.
</ParamField>

<ParamField query="cache_control" type="string">
  Same as in Object Post.
</ParamField>

<ParamField query="metadata" type="string">
  Same as in Object Post.
</ParamField>

## Limits

<Note>
  * File size: up to **10 GiB** (10,737,418,240 bytes), or less when a rule or upload token sets a smaller limit.
  * Part size: **5 MB minimum** (5,242,880 bytes), **32 MB maximum** (33,554,432 bytes). The last part may be smaller.
  * Parts per file: up to **2048**.
  * Open uploads per account: **32**, counting uploads opened through the [S3 gateway](/en/blob-reference/s3-compatibility) (`TOO_MANY_OPEN_UPLOADS`, 429).
  * Rate limit: 5 opens per 10 seconds.
  * An account already over its included storage can't open uploads (`STORAGE_QUOTA_EXCEEDED`).

  Read the limits from the `chunk` object of the response rather than hardcoding them.
</Note>

## Response

<ResponseField name="status" type="string">
  "success" if successful, "error" if not.
</ResponseField>

<ResponseField name="response" type="object">
  <Expandable title="Toggle object">
    <ResponseField name="upload" type="string">
      The opaque upload token. Send it on every later call of the flow.
    </ResponseField>

    <ResponseField name="id" type="string">
      The id the file will have once completed.
    </ResponseField>

    <ResponseField name="private" type="boolean">
      Whether the file will be private.
    </ResponseField>

    <ResponseField name="url" type="string | null">
      The public URL the file will have, or `null` when private.
    </ResponseField>

    <ResponseField name="expires_at" type="ISO 8601">
      When the file will be deleted. Present only when it expires.
    </ResponseField>

    <ResponseField name="name" type="string">
      The name sent in the request.
    </ResponseField>

    <ResponseField name="prefix" type="string">
      The prefix, when one was sent.
    </ResponseField>

    <ResponseField name="chunk" type="object">
      The limits of this upload: `min_size` and `max_size` (part bytes), `max_parts`, and `max_object_size` (file bytes).
    </ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
  ```bash cURL theme={"system"}
  curl --request POST \
    --url 'https://blob.squarecloud.app/v1/objects/chunked?name=dataset&filename=reads.fastq.gz&prefix=genomics&private=true' \
    --header 'Authorization: YOUR_API_KEY'
  ```

  ```javascript JavaScript theme={"system"}
  const params = new URLSearchParams({
    name: 'dataset',
    filename: file.name,
    prefix: 'genomics',
    private: 'true',
  });

  const res = await fetch(`https://blob.squarecloud.app/v1/objects/chunked?${params}`, {
    method: 'POST',
    headers: { Authorization: 'YOUR_API_KEY' },
  });
  const { response: upload } = await res.json();
  // persist upload.upload: it is the handle to this upload
  ```

  ```python Python theme={"system"}
  import requests

  response = requests.post(
      'https://blob.squarecloud.app/v1/objects/chunked',
      headers={'Authorization': 'YOUR_API_KEY'},
      params={'name': 'dataset', 'filename': 'reads.fastq.gz', 'prefix': 'genomics', 'private': 'true'},
  )
  upload = response.json()['response']
  ```
</RequestExample>

<ResponseExample>
  ```json theme={"system"}
  {
    "status": "success",
    "response": {
      "upload": "Zk3pQ8vR2mWx7LcT0aYbNe5uHs1JqGd9KoVfBr4XtIy6EwUz.q2Ws8nLc0VbR5TfYp3Hk",
      "id": "prv/3155597145698959364/genomics/dataset_mugws5c0-3c59dc048e8850243be8079a5c74d079.fastq.gz",
      "private": true,
      "url": null,
      "name": "dataset",
      "prefix": "genomics",
      "chunk": {
        "min_size": 5242880,
        "max_size": 33554432,
        "max_parts": 2048,
        "max_object_size": 10737418240
      }
    }
  }
  ```
</ResponseExample>

## Errors

| Code | HTTP | When |
| - | - | - |
| `INVALID_OBJECT_*` / `INVALID_STORAGE_AUTO_DOWNLOAD` | 400 | A parameter is invalid, as in [Object Post](/en/blob-reference/endpoint/post#errors). |
| `INVALID_FILE_TYPE` / `BLOCKED_FILE_TYPE` / `FILE_TYPE_NOT_ALLOWED` | 400 | The extension is malformed, blocked, or not allowed by the rule or token. |
| `PERMISSION_DENIED` | 401 | The account has no active plan. |
| `UPLOAD_TOKEN_USED` | 401 | The upload token has no uses left. An expired token answers `ACCESS_DENIED`. |
| `STORAGE_QUOTA_EXCEEDED` | 403 | The account is already over its included storage. |
| `PREFIX_NOT_ALLOWED` | 403 | The upload token is bound to another prefix. |
| `UPGRADE_REQUIRED` | 403 | An option needs a higher plan. |
| `TOO_MANY_OPEN_UPLOADS` | 429 | The account has 32 open uploads. Complete or abort one. |
| `RATE_LIMITED` | 429 | More than 5 opens in 10 seconds. |
| `UPLOAD_FAILED` | 500 | The upload could not be opened. Retry. |
| `PRIVATE_STORAGE_UNAVAILABLE` / `PUBLIC_STORAGE_UNAVAILABLE` | 503 | Storage is temporarily unavailable. Retry. |

## Related

* Blob SDK: [`blob.put()`](/en/sdks/blob/uploads) runs the whole chunked flow for files above 90 MiB


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.