---
title: UploadPart · S3 API
description: Uploads one part of a multipart upload.
doc_version: 0.1.0-preview
last_updated: 2026-09-14
---

# UploadPart

`PUT /{bucket}/{key}?partNumber&uploadId`

Uploads one part of a multipart upload.

Send the parts in any order, or at the same time. A part with a part number that exists replaces it. Keep the ETag of each part for CompleteMultipartUpload.

- Role: Editor or higher.
- Support: supported.
- Auth: AWS Signature Version 4 with your API key. See [S3 API](/api/s3.md).
- Returns the part ETag. Every part except the last must be the same size.

## Example request

```http
PUT /support-tickets/big.bin?partNumber=1&uploadId=VXBsb2FkIGZvciBiaWcuYmlu HTTP/1.1
Host: wspq7ws2np5l8z4.storage.graphon.sh
x-amz-date: 20261007T093000Z
x-amz-content-sha256: UNSIGNED-PAYLOAD
Authorization: AWS4-HMAC-SHA256 Credential=<ACCESS_KEY_ID>/20261007/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=<SIGNATURE>
Content-Length: 104857600

<104857600 bytes>
```

## Path parameters

- `bucket` (string, required): The bucket name.
- `key` (string, required): The object key. Keep each `/` as it is. Percent-encode the other reserved characters.

## Query parameters

- `partNumber` (integer, required): The part number, from 1 through 10,000.
- `uploadId` (string, required): The upload ID from CreateMultipartUpload.

## Headers

- `Content-Length` (integer, required): The size of the body in bytes. One request can carry at most 200 MiB.
- `Content-MD5` (string, optional): The base64 MD5 digest of the body. A different digest returns `400 BadDigest`.
- `x-amz-checksum-*` (string, optional): A base64 checksum of the part: `crc32`, `crc32c`, `crc64nvme`, `sha1`, or `sha256`. Storage checks it. A different checksum returns `400 BadDigest`.
- `x-amz-sdk-checksum-algorithm` (string, optional): The checksum to compute: `CRC32`, `CRC32C`, `CRC64NVME`, `SHA1`, or `SHA256`. `x-amz-checksum-algorithm` does the same.
- `x-amz-expected-bucket-owner` (string, optional): The workspace ID that must own the bucket. Another owner returns `403 AccessDenied`.

## Request body

- `Body` (binary, required): The bytes of the part.

## Responses

### 200 · OK

Storage stored the part.

Response headers:

- `ETag` (string, optional): The entity tag of the part. Send it to CompleteMultipartUpload.

```http
HTTP/1.1 200 OK
ETag: "7ac66c0f148de9519b8bd264312c4d64"
x-amz-request-id: 4F8A2C7D1E6B4093A5C2D8E1F7B3A960
```

## Errors

Any request can also get the common errors on [S3 API](/api/s3.md).

### 404 · `NoSuchUpload`

The upload ID is not known for this key. The upload was completed or aborted.

Start a new upload with CreateMultipartUpload.

### 400 · `InvalidArgument`

The partNumber is not an integer from 1 through 10,000.

Number the parts from 1 through 10,000.

### 400 · `BadDigest`

A checksum header does not match the body. Storage did not keep the bytes.

Compute the checksum from the bytes that you send, then send the request again.

### 400 · `XAmzContentSHA256Mismatch`

The body does not match the SHA-256 that the request signed.

Hash the bytes that you send, then sign and send the request again.

### 400 · `IncompleteBody`

The body ended before the length that the request declared.

Send the whole body again.

### 411 · `MissingContentLength`

An `aws-chunked` body came without `x-amz-decoded-content-length`.

Send the object size in `x-amz-decoded-content-length`.

### 413 · `EntityTooLarge`

The body is larger than 200 MiB.

Use a multipart upload. The aws CLI and the SDKs do this for a large file by themselves.

### 503 · `SlowDown`

The bucket has too many writes in progress. The response has `Retry-After: 1`.

Wait for the Retry-After time, then send the request again. SDKs retry by themselves.

### 403 · `QuotaExceeded`

The workspace stores twice its plan's storage limit, so storage refuses writes. Reads, lists, and deletes still work.

Delete objects, or ask an Admin to upgrade the plan. Then send the write again.

### 404 · `NoSuchBucket`

No bucket has this name, or the bucket was deleted.

Check the bucket name. ListBuckets shows the buckets of the workspace.

### 403 · `AccessDenied`

The key does not have the role for this operation, or the bucket is in another workspace.

Use a key of the workspace that owns the bucket, with the role above.

## Other request languages

### aws CLI

```bash
# The graphon profile from the S3 API overview.
export AWS_PROFILE=graphon

UPLOAD_ID="VXBsb2FkIGZvciBiaWcuYmlu"  # The UploadId from create-multipart-upload
aws s3api upload-part \
  --bucket support-tickets \
  --key big.bin \
  --part-number 1 \
  --upload-id "$UPLOAD_ID" \
  --body part-1.bin
```

### Python (boto3)

```python
# Replace wspq7ws2np5l8z4 with your workspace ID.
import os

import boto3
from botocore.config import Config

api_key = os.environ["GRAPHON_API_KEY"]
access_key_id = api_key.split("_", 1)[1].split(".")[0]

s3 = boto3.client(
    "s3",
    endpoint_url="https://wspq7ws2np5l8z4.storage.graphon.sh",
    region_name="us-east-1",
    aws_access_key_id=access_key_id,
    aws_secret_access_key=api_key,
    config=Config(signature_version="s3v4", s3={"addressing_style": "path"}),
)

upload_id = "VXBsb2FkIGZvciBiaWcuYmlu"  # The UploadId from create_multipart_upload
response = s3.upload_part(
    Bucket="support-tickets",
    Key="big.bin",
    PartNumber=1,
    UploadId=upload_id,
    Body=open("part-1.bin", "rb"),
)
print(response["ETag"])
```

### JavaScript (AWS SDK)

```javascript
// Replace wspq7ws2np5l8z4 with your workspace ID.
import { S3Client, UploadPartCommand } from '@aws-sdk/client-s3';
import { createReadStream } from 'node:fs';

const apiKey = process.env.GRAPHON_API_KEY;
if (!apiKey) throw new Error('Set GRAPHON_API_KEY.');
const accessKeyId = apiKey.split('_')[1].split('.')[0];

const s3 = new S3Client({
  endpoint: 'https://wspq7ws2np5l8z4.storage.graphon.sh',
  region: 'us-east-1',
  forcePathStyle: true,
  credentials: { accessKeyId, secretAccessKey: apiKey },
});

const uploadId = 'VXBsb2FkIGZvciBiaWcuYmlu'; // The UploadId from CreateMultipartUpload
const response = await s3.send(new UploadPartCommand({
  Bucket: 'support-tickets',
  Key: 'big.bin',
  PartNumber: 1,
  UploadId: uploadId,
  Body: createReadStream('part-1.bin'),
}));
console.log(response.ETag);
```

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
