---
title: S3 API
description: Use S3 tools and SDKs with an API key: the endpoint, signing, client setup, every S3 operation that storage supports, and the common errors.
doc_version: 0.1.0-preview
last_updated: 2026-09-14
---

# S3 API

Use S3 tools and SDKs with an API key: the endpoint, signing, client setup, every S3 operation that storage supports, and the common errors.

## Connection

- S3 endpoint: https://wspq7ws2np5l8z4.storage.beta.graphon.sh
- JSON origin: https://storage.beta.graphon.sh
- Replace wspq7ws2np5l8z4 in every example with your workspace ID.
- Region: us-east-1 (a signing region only; data stays in the bucket's location)
- Addressing: path style, /{bucket}/{key}
- Signature: AWS Signature Version 4, in a header or a presigned URL
- Access key ID: the part of the API key between the `_` and the `.`
- Secret access key: the whole API key
- `graphon s3-credentials` prints these values for the AWS tools.
- The key must belong to the workspace in the S3 hostname. The bare storage host serves the JSON API only.

## Set up a client

```ini
# Replace wspq7ws2np5l8z4 with your workspace ID.
# ~/.aws/config
[profile graphon]
credential_process = graphon s3-credentials --format process
endpoint_url = https://wspq7ws2np5l8z4.storage.beta.graphon.sh
region = us-east-1
s3 =
  addressing_style = path
```

```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.beta.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"}),
)
```

```javascript
// Replace wspq7ws2np5l8z4 with your workspace ID.
import { S3Client } from '@aws-sdk/client-s3';

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.beta.graphon.sh',
  region: 'us-east-1',
  forcePathStyle: true,
  credentials: { accessKeyId, secretAccessKey: apiKey },
});
```

## Bucket names and rename

Names are case-sensitive and unique within a workspace. Use 1–255 ASCII letters, digits, periods, underscores, or hyphens. The exact names `.` and `..` are invalid.
An Editor can rename a bucket through JSON or `graphon buckets rename <bucket> <new-name>`. S3 has no RenameBucket operation.
JSON uses `PATCH /v1/workspaces/{workspaceId}/buckets/{bucketId}` on the bare storage origin. Include the current quoted revision in `If-Match`. Missing revisions return 428, stale revisions return 412, and name conflicts return 409.
Rename keeps the bucket ID and data. Update S3 settings and name-based URLs. The old name becomes available without an alias. An old URL can address a new bucket if the name is reused, subject to current access checks.

## Operations

Role is the lowest role whose key can send the request. Each operation has a
full page with its parameters, responses, errors, and samples.

### Buckets

- [ListBuckets](/api/s3/list-buckets.md): `GET /`. Lists the buckets in the workspace of the key. Role: Viewer.
- [CreateBucket](/api/s3/create-bucket.md): `PUT /{bucket}`. Creates a bucket in the workspace of the key. Role: Editor.
- [HeadBucket](/api/s3/head-bucket.md): `HEAD /{bucket}`. Checks that a bucket exists and that the key can read it. Role: Viewer.
- [DeleteBucket](/api/s3/delete-bucket.md): `DELETE /{bucket}`. Deletes an empty bucket. Role: Editor.
- [GetBucketLocation](/api/s3/get-bucket-location.md): `GET /{bucket}?location`. Returns the signing region of a bucket. Role: Viewer.

### Objects

- [PutObject](/api/s3/put-object.md): `PUT /{bucket}/{key}`. Writes an object. Role: Editor.
- [GetObject](/api/s3/get-object.md): `GET /{bucket}/{key}`. Reads an object. Role: Viewer. Partial support.
- [HeadObject](/api/s3/head-object.md): `HEAD /{bucket}/{key}`. Reads the headers of an object, with no body. Role: Viewer.
- [CopyObject](/api/s3/copy-object.md): `PUT /{bucket}/{key}`. Copies an object to another key. Role: Editor. Partial support.
- [DeleteObject](/api/s3/delete-object.md): `DELETE /{bucket}/{key}`. Deletes an object, or one version of an object. Role: Editor.
- [DeleteObjects](/api/s3/delete-objects.md): `POST /{bucket}?delete`. Deletes up to 1,000 objects in one request. Role: Editor.
- [ListObjectsV2](/api/s3/list-objects-v2.md): `GET /{bucket}?list-type=2`. Lists the objects in a bucket, one page at a time. Role: Viewer.
- [ListObjects](/api/s3/list-objects.md): `GET /{bucket}`. Lists the objects in a bucket with marker pagination. Role: Viewer.

### Multipart uploads

- [CreateMultipartUpload](/api/s3/create-multipart-upload.md): `POST /{bucket}/{key}?uploads`. Starts a multipart upload and returns its upload ID. Role: Editor.
- [UploadPart](/api/s3/upload-part.md): `PUT /{bucket}/{key}?partNumber&uploadId`. Uploads one part of a multipart upload. Role: Editor.
- [UploadPartCopy](/api/s3/upload-part-copy.md): `PUT /{bucket}/{key}?partNumber&uploadId`. Copies a byte range of an object into one part of a multipart upload. Role: Editor. Partial support.
- [CompleteMultipartUpload](/api/s3/complete-multipart-upload.md): `POST /{bucket}/{key}?uploadId`. Joins the uploaded parts into one object. Role: Editor.
- [AbortMultipartUpload](/api/s3/abort-multipart-upload.md): `DELETE /{bucket}/{key}?uploadId`. Stops a multipart upload and deletes its parts. Role: Editor.
- [ListParts](/api/s3/list-parts.md): `GET /{bucket}/{key}?uploadId`. Lists the parts of a multipart upload. Role: Viewer.
- [ListMultipartUploads](/api/s3/list-multipart-uploads.md): `GET /{bucket}?uploads`. Lists the multipart uploads in a bucket that are not complete. Role: Viewer.

### Versioning & retention

- [GetBucketVersioning](/api/s3/get-bucket-versioning.md): `GET /{bucket}?versioning`. Returns the versioning state of a bucket. Role: Viewer.
- [PutBucketVersioning](/api/s3/put-bucket-versioning.md): `PUT /{bucket}?versioning`. Turns versioning on or off for a bucket. Role: Editor. Partial support.
- [ListObjectVersions](/api/s3/list-object-versions.md): `GET /{bucket}?versions`. Lists the versions and delete markers in a bucket. Role: Viewer.
- [GetObjectLockConfiguration](/api/s3/get-object-lock-configuration.md): `GET /{bucket}?object-lock`. Returns the object lock settings of a bucket. Role: Viewer.
- [PutObjectLockConfiguration](/api/s3/put-object-lock-configuration.md): `PUT /{bucket}?object-lock`. Turns on object lock, and sets the default retention for new versions. Role: Editor. Partial support.
- [GetObjectRetention](/api/s3/get-object-retention.md): `GET /{bucket}/{key}?retention`. Returns the retention of one object version. Role: Viewer.
- [PutObjectRetention](/api/s3/put-object-retention.md): `PUT /{bucket}/{key}?retention`. Sets or extends the retention of one object version. Role: Editor. Partial support.

### Object tags

- [GetObjectTagging](/api/s3/get-object-tagging.md): `GET /{bucket}/{key}?tagging`. Returns the tags of an object. Role: Viewer.
- [PutObjectTagging](/api/s3/put-object-tagging.md): `PUT /{bucket}/{key}?tagging`. Replaces the tags of an object. Role: Editor.
- [DeleteObjectTagging](/api/s3/delete-object-tagging.md): `DELETE /{bucket}/{key}?tagging`. Removes every tag from an object. Role: Editor.

## Common errors

An error has an XML body with a Code and a Message. Every response has an
x-amz-request-id header.

- 403 `AccessDenied`: The request has no signature, the presigned URL expired, or the role of the key is too low. Sign the request, presign a new URL, or use a key with a higher role.
- 403 `AccessDenied`: The key belongs to a workspace other than the one in the hostname, or that workspace is unknown or suspended. Use the S3 endpoint of the workspace that owns the key.
- 400 `InvalidBucketName`: The bucket name in the path breaks the naming rules. Use 1 to 255 ASCII letters, digits, periods, underscores, or hyphens. A name cannot be `.` or `..`.
- 403 `InvalidAccessKeyId`: No key has this access key ID, or the key was revoked. Check the access key ID. It is the part of the API key between `_` and `.`.
- 403 `ExpiredToken`: The key has expired. Create a new key.
- 403 `SignatureDoesNotMatch`: The signature does not match the request. Use the whole API key as the secret access key, sign for `us-east-1`, and use path-style addressing.
- 403 `RequestTimeTooSkewed`: The request time is too far from the time of the server. Set the clock of the client with NTP.
- 403 `AuthorizationQueryParametersError`: A presigned URL has an X-Amz-Date or X-Amz-Expires value that is not valid. Presign the URL again.
- 400 `InvalidRequest`: A signed request has no `x-amz-content-sha256` header. Send the header. SDKs and the aws CLI send it by themselves.
- 400 `InvalidURI`: The path or the query has a `%` escape that does not decode. Percent-encode the key and the query values correctly.
- 405 `MethodNotAllowed`: The path does not accept this HTTP method. Check the method and the path of the operation.
- 413 `EntityTooLarge`: The body is larger than 200 MiB. Use a multipart upload.
- 501 `NotImplemented`: The request uses an S3 feature that storage does not support. See Not supported below.
- 503 `SlowDown`: The bucket has too many writes in progress. Retry after the Retry-After time. SDKs retry by themselves.
- 500 `InternalError`: Storage could not handle the request. Retry with backoff. Send the x-amz-request-id to support if it continues.

## Browser requests

Storage answers a CORS preflight (OPTIONS) on any S3 path with 204. It allows every
origin and header, and the GET, PUT, POST, DELETE, and HEAD methods.

## Not supported

These requests return 501 NotImplemented. An S3 request to the bare storage host returns 404 with a JSON error.

- `GET /{bucket}?website`: Website, CORS, and lifecycle configuration return 501.
- `GET /{bucket}/{key}?acl`: Object ACLs return 501. Use the bucket permission in the JSON API.
- `GET /{bucket}/{key}?legal-hold`: Legal hold returns 501.
- `POST /{bucket}/{key}?select`: S3 Select returns 501.
- `PUT /{bucket}/{key} with x-amz-server-side-encryption-customer-algorithm`: SSE-C returns 501. Providers encrypt every object at rest.

## Sitemap

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