> ## 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.

# Upload one part of a chunked upload

> Send one part of a chunked upload with PUT /v1/objects/chunked: raw bytes, 5 to 32 MB each except the last, in any order and safe to retry on failure.

<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 Part sends one part of an upload opened with [Chunked Init](/en/blob-reference/endpoint/chunked-init). The body is the **raw bytes** of the part, not multipart/form-data. Part numbers run from 1 to 2048 and may be sent in **any order**. Re-sending a part with the same number replaces it, so a failed part can simply be retried.

Every part except the last must have from **5 MB to 32 MB**; the last one may be smaller. Send at most **6 parts in parallel** per account: a seventh answers `TOO_MANY_CONCURRENT_CHUNKS`, and you retry that part once another finishes. With 6 in flight, 16 MB parts are a good default for a browser.

This route has its own limit of 60 parts per 10 seconds and doesn't consume the account-wide API budget, so a long upload can't exhaust your API quota for the rest of the platform. Requires the `blob:write` scope, or the upload token that opened the upload, and an active plan.

## Parameters

<ParamField query="upload" type="string" required>
  The `upload` token returned by Chunked Init.
</ParamField>

<ParamField query="part" type="number" placeholder="1" required>
  The part number, from 1 to 2048.
</ParamField>

<ParamField body="body" type="binary" required>
  The raw part bytes (`Content-Type: application/octet-stream`).
</ParamField>

## Response

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

<ResponseField name="response" type="object">
  <Expandable title="Toggle object">
    <ResponseField name="part" type="number">
      The part number stored.
    </ResponseField>

    <ResponseField name="size" type="number">
      The size of the part, in bytes.
    </ResponseField>

    <ResponseField name="etag" type="string">
      The ETag of the part. Informational: Chunked Complete reads the part list on the server, so you never send it back.
    </ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
  ```bash cURL theme={"system"}
  curl --request PUT \
    --url 'https://blob.squarecloud.app/v1/objects/chunked?upload=UPLOAD_TOKEN&part=1' \
    --header 'Authorization: YOUR_API_KEY' \
    --header 'Content-Type: application/octet-stream' \
    --data-binary '@./chunk-1.bin'
  ```

  ```javascript JavaScript theme={"system"}
  const PART_SIZE = 16 * 1024 * 1024;
  const total = Math.ceil(file.size / PART_SIZE);

  async function sendPart(n) {
    const body = file.slice((n - 1) * PART_SIZE, n * PART_SIZE);
    const params = new URLSearchParams({ upload: uploadToken, part: String(n) });
    const res = await fetch(`https://blob.squarecloud.app/v1/objects/chunked?${params}`, {
      method: 'PUT',
      headers: { Authorization: 'YOUR_API_KEY', 'Content-Type': 'application/octet-stream' },
      body,
    });
    if (res.status === 429 || res.status >= 500) return sendPart(n); // add a backoff in production
    if (!res.ok) throw new Error((await res.json()).code);
  }

  // 6 workers, each taking the next part number
  let next = 1;
  await Promise.all(Array.from({ length: 6 }, async () => {
    while (next <= total) await sendPart(next++);
  }));
  ```

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

  with open('./bigfile.bin', 'rb') as f:
      chunk = f.read(16 * 1024 * 1024)

  response = requests.put(
      'https://blob.squarecloud.app/v1/objects/chunked',
      headers={'Authorization': 'YOUR_API_KEY', 'Content-Type': 'application/octet-stream'},
      params={'upload': upload_token, 'part': 1},
      data=chunk,
  )
  ```
</RequestExample>

<ResponseExample>
  ```json theme={"system"}
  {
    "status": "success",
    "response": {
      "part": 1,
      "size": 16777216,
      "etag": "\"9bb58f26192e4ba00f01e2e7b136bbd8\""
    }
  }
  ```
</ResponseExample>

## Errors

| Code | HTTP | When |
| - | - | - |
| `INVALID_UPLOAD_TOKEN` | 400 | The `upload` token is missing, malformed or not yours. |
| `INVALID_CHUNK_PART` | 400 | `part` is not an integer from 1 to 2048. |
| `EMPTY_CHUNK` | 400 | The body is empty. |
| `PERMISSION_DENIED` | 401 | The account has no active plan. |
| `UPLOAD_NOT_FOUND` | 404 | The upload was completed, aborted or expired. |
| `CHUNK_TOO_LARGE` | 413 | The part has more than 32 MB. |
| `FILE_TOO_LARGE` | 413 | With this part, the upload passes its size limit. |
| `TOO_MANY_CONCURRENT_CHUNKS` | 429 | 6 parts are already in flight. Retry this part when one finishes. |
| `RATE_LIMITED` | 429 | More than 60 parts in 10 seconds. |
| `UPLOAD_FAILED` | 500 | The part failed. Retry it. |

## 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.