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

# S3 compatibility

> Use aws-cli, boto3, rclone and the AWS SDKs with Blob Storage through the S3-compatible gateway: endpoint, buckets, supported operations and limits.

Blob Storage speaks the S3 API at `https://s3-blob.squarecloud.app`. Any tool that lets you set a custom endpoint works: aws-cli, boto3, the AWS SDK for JavaScript, rclone, Cyberduck and most backup tools.

## Credentials

S3 tools sign requests with an access key pair. Get it from [S3 Credentials](/en/blob-reference/endpoint/s3-credentials):

```bash theme={null}
curl https://blob.squarecloud.app/v1/s3/credentials \
  --header 'Authorization: YOUR_API_KEY'
```

The pair is **derived from your API key**. It needs no separate management: revoking or regenerating the key does the same to the pair, and the pair gets the same scopes. A key with only `blob:read` gives a read-only pair.

The pair doesn't change while the API key doesn't change, so fetch it once and keep it in your secret manager or in an environment variable. Don't call the route on every boot: it accepts 10 requests per hour.

| Setting    | Value                                                         |
| ---------- | ------------------------------------------------------------- |
| Endpoint   | `https://s3-blob.squarecloud.app`                             |
| Region     | `auto`                                                        |
| Addressing | Path-style (`https://s3-blob.squarecloud.app/<bucket>/<key>`) |
| Signature  | AWS Signature Version 4                                       |

## Buckets

Your account sees three fixed buckets. You can't create or delete buckets.

| Bucket    | Contents                                                                   | Access                |
| --------- | -------------------------------------------------------------------------- | --------------------- |
| `public`  | Public files, served on `https://blob.squarecloud.dev/pub/<user_id>/<key>` | Read and write        |
| `private` | Private files, with no public URL                                          | Read and write        |
| `legacy`  | Legacy files, uploaded before the September 2026 update                    | Read, list and delete |

A key in the `public` or `private` bucket is the object path **without** your user id: `images/logo.png` in the `public` bucket is the object `pub/<user_id>/images/logo.png` on the REST API. Files written over S3 appear on the REST API and in the dashboard, and the other way around.

## Configuration

<CodeGroup>
  ```bash aws-cli theme={null}
  aws configure set aws_access_key_id SQ2_...
  aws configure set aws_secret_access_key ...
  aws configure set region auto

  aws s3 cp ./logo.png s3://public/images/logo.png \
    --endpoint-url https://s3-blob.squarecloud.app
  aws s3 ls s3://public/images/ --endpoint-url https://s3-blob.squarecloud.app
  ```

  ```python boto3 theme={null}
  import boto3
  from botocore.config import Config

  s3 = boto3.client(
      "s3",
      endpoint_url="https://s3-blob.squarecloud.app",
      aws_access_key_id="SQ2_...",
      aws_secret_access_key="...",
      region_name="auto",
      config=Config(signature_version="s3v4", s3={"addressing_style": "path"}),
  )

  s3.upload_file("report.pdf", "private", "reports/2026/report.pdf")
  url = s3.generate_presigned_url(
      "get_object",
      Params={"Bucket": "private", "Key": "reports/2026/report.pdf"},
      ExpiresIn=3600,
  )
  ```

  ```javascript AWS SDK v3 theme={null}
  import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

  const s3 = new S3Client({
    endpoint: "https://s3-blob.squarecloud.app",
    region: "auto",
    forcePathStyle: true,
    credentials: { accessKeyId: "SQ2_...", secretAccessKey: "..." },
  });

  await s3.send(new PutObjectCommand({
    Bucket: "public",
    Key: "images/logo.png",
    Body: buffer,
    ContentType: "image/png",
  }));
  ```

  ```ini rclone theme={null}
  [squarecloud]
  type = s3
  provider = Other
  access_key_id = SQ2_...
  secret_access_key = ...
  endpoint = https://s3-blob.squarecloud.app
  region = auto
  force_path_style = true
  ```
</CodeGroup>

<Note>boto3 needs `signature_version="s3v4"` for presigned URLs: without it, `generate_presigned_url` signs with the older SigV2, which the gateway doesn't accept. Presigned URLs last up to 7 days (604800 seconds).</Note>

Gateway responses are never cached at the edge. A presigned URL stops working exactly when it expires, when the API key is revoked or when the object is deleted.

## Supported operations

| Area          | Operations                                                                                                                                                                                                                                                                             |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Buckets       | `ListBuckets`, `HeadBucket`, `GetBucketLocation`. `CreateBucket` on an existing bucket succeeds; `DeleteBucket` answers `BucketNotEmpty` while the bucket has files.                                                                                                                   |
| Listing       | `ListObjects` and `ListObjectsV2`, with `delimiter`, `prefix` and `encoding-type=url`.                                                                                                                                                                                                 |
| Objects       | `HeadObject`, `GetObject` (with `Range`, `If-Match`, `If-None-Match`, `If-Modified-Since`), `PutObject` (up to 100 MB in one request), `CopyObject` (`COPY` and `REPLACE` metadata directives), `DeleteObject`, `DeleteObjects` (up to 1000 keys).                                     |
| Multipart     | `CreateMultipartUpload`, `UploadPart` (5 MB to 80 MB per part, except the last one), `UploadPartCopy`, `CompleteMultipartUpload`, `AbortMultipartUpload`, `ListParts`, `ListMultipartUploads`. Objects up to 10 GiB.                                                                   |
| Integrity     | `Content-MD5` is always verified, even when an `x-amz-checksum-*` header is also sent. `x-amz-checksum-crc32`, `-sha1` and `-sha256` are verified too; a mismatch answers `BadDigest`. `crc32c` and `crc64nvme` are accepted without verification. `aws-chunked` bodies are supported. |
| Compatibility | `GetBucketVersioning`, `GetBucketAcl`, `GetObjectAcl` and the tagging reads answer fixed values so tools that probe them keep working.                                                                                                                                                 |

Bucket policies, CORS, lifecycle, website, encryption, object lock, versions, logging, notifications, replication, ACL and tag writes, `GET` by `partNumber` and browser POST form uploads answer `501 NotImplemented`. Use [account settings](/en/blob-reference/endpoint/settings-put) for lifecycle rules.

## Keys

* Keys are literal paths. A key can have up to about 1000 bytes: the 1024-byte limit counts the account prefix too. A longer key answers `KeyTooLongError`, and its message says the exact number of bytes you have. Segments can't be empty, `.` or `..`.
* A 0-byte key ending in `/` is a folder marker, the way the AWS console creates folders.
* The served `Content-Type` is derived from the extension, as on the REST API. Executables are refused with `InvalidArgument` and `.html`, `.svg` and `.xml` are served as downloads.

## Metadata, cache and expiry

* `x-amz-meta-*` headers are kept on **Pro and Enterprise** (up to 5 keys and 512 bytes, otherwise `MetadataTooLarge`). On other plans they are dropped.
* `Cache-Control` and `Content-Disposition` are kept. An uncached `Cache-Control` (`no-cache`, `no-store` or `max-age=0`) outside Enterprise is refused with `AccessDenied`.
* [Rules per prefix](/en/blob-reference/endpoint/settings-put) apply to objects written over S3, including automatic deletion. A rule's `max_size` and `extensions` apply to REST uploads only.

## Limits

| Limit                                               | Value                                                               |
| --------------------------------------------------- | ------------------------------------------------------------------- |
| Requests per account, every 10 seconds              | **Hobby** 100 · **Standard** 200 · **Pro** 400 · **Enterprise** 600 |
| Server-side copies (`CopyObject`, `UploadPartCopy`) | 20 per 10 seconds                                                   |
| Keys deleted by `DeleteObjects`                     | 2000 per 10 seconds                                                 |
| Open multipart uploads                              | 32 per account, shared with the REST chunked uploads                |
| Single `PutObject`                                  | 100 MB                                                              |
| Part (`UploadPart`)                                 | 5 MB to 80 MB, except the last part                                 |
| Object size                                         | 10 GiB                                                              |
| Invalid credentials                                 | Too many failures block the IP for a few minutes                    |

Past a limit the gateway answers `SlowDown` (HTTP 503), and the AWS SDKs back off and retry on their own. S3 requests **don't count** against your plan's API request limit.

Check the key pair before retrying in a loop: an IP that sends too many invalid credentials is blocked for a few minutes.

### Part size

Multipart tools split big files on their own; keep each part at **80 MB or less**. The defaults of the AWS CLI (8 MB) and rclone (5 MB) already fit.

<CodeGroup>
  ```bash aws-cli theme={null}
  aws configure set default.s3.multipart_chunksize 64MB
  ```

  ```python boto3 theme={null}
  from boto3.s3.transfer import TransferConfig

  config = TransferConfig(multipart_chunksize=64 * 1024 * 1024)
  s3.upload_file("backup.tar.gz", "private", "backups/backup.tar.gz", Config=config)
  ```

  ```javascript AWS SDK v3 theme={null}
  import { Upload } from "@aws-sdk/lib-storage";

  await new Upload({
    client: s3,
    params: { Bucket: "private", Key: "backups/backup.tar.gz", Body: stream },
    partSize: 64 * 1024 * 1024, // up to 80 MB
  }).done();
  ```
</CodeGroup>

Writing needs a paid plan. Without one, and on the read-only `legacy` bucket, writes answer `AccessDenied`. Storage quota applies as on the REST API.

## Errors

The gateway answers standard S3 XML errors, so SDKs handle them natively:

| Error                                               | When                                                                                               |
| --------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `AccessDenied`                                      | No paid plan, read-only key, write to `legacy`, or an option outside your plan.                    |
| `NoSuchBucket`                                      | The bucket is not `public`, `private` or `legacy`.                                                 |
| `NoSuchKey`                                         | The object does not exist.                                                                         |
| `InvalidArgument`                                   | A blocked file type (executables and installers) or a malformed header.                            |
| `QuotaExceeded`                                     | The account reached its included storage.                                                          |
| `EntityTooLarge` / `EntityTooSmall`                 | A `PutObject` over 100 MB, a part over 80 MB, or a part under 5 MB that isn't the last one.        |
| `InvalidPart` / `InvalidPartOrder` / `NoSuchUpload` | Multipart completion with bad parts, or an upload that no longer exists.                           |
| `BadDigest`                                         | The body doesn't match `Content-MD5` or the `x-amz-checksum-*` header.                             |
| `PreconditionFailed`                                | An `If-Match` or `If-None-Match` condition failed.                                                 |
| `MetadataTooLarge`                                  | More than 5 metadata keys or 512 bytes.                                                            |
| `KeyTooLongError`                                   | The key is too long (about 1000 bytes). The message says the exact limit for your account.         |
| `SlowDown`                                          | HTTP 503: a rate limit or the open upload limit was reached. SDKs retry with backoff on their own. |
| `NotImplemented`                                    | The operation is not supported (see above).                                                        |
