Skip to main content
The Storage API is Supabase Storage v1.33.0 mounted at /storage/v1/* on your project URL. It manages files in buckets backed by S3, with object metadata mirrored into the storage.buckets and storage.objects tables in Postgres so RLS can gate access. For the conceptual model, see Storage model. For uploading, see Storage uploads. For RLS, see Storage policies.

Common headers

The Anon Key combined with a user’s access token in Authorization is the pattern for browser-direct uploads. Service Role in both is for server-side admin operations. The Storage API uses bearer auth for most endpoints. Public-URL reads (/object/public/*) work without auth; signed-URL reads pass auth via a ?token= query parameter.

Buckets

A bucket is a top-level container with a name, public flag, optional MIME allowlist, and optional file-size override.

POST /storage/v1/bucket

Create a new bucket.
string
required
Bucket id (used in URLs). Must be unique. Lowercase alphanumeric, dashes, and dots.
string
Human-readable name. Defaults to id.
boolean
When true, the bucket’s public URL (/object/public/{bucket}/{path}) returns files without auth. Default false.
integer
Per-bucket override for the project’s default file size limit. In bytes.
string[]
Restrict uploads to specific Content-Types. Default null = anything.
Response: { "name": "avatars" } on 200.

GET /storage/v1/bucket

List all buckets. Returns the bucket metadata rows from storage.buckets, filtered by SELECT policies.
Response: array of { id, name, public, owner, created_at, updated_at, file_size_limit, allowed_mime_types }.

GET /storage/v1/bucket/

Get a specific bucket’s metadata.

PUT /storage/v1/bucket/

Update bucket metadata. Body fields are the same as create (public, file_size_limit, allowed_mime_types).

DELETE /storage/v1/bucket/

Delete an empty bucket. Returns 409 not_empty if the bucket contains any objects; empty it first via POST /bucket/{id}/empty or DELETE the objects individually.

POST /storage/v1/bucket//empty

Delete all objects in a bucket without removing the bucket itself. Returns 200 OK.

Objects

The core file operations. Object endpoints are routed by bucket + path.

POST /storage/v1/object//

Upload a new file. The path can include slashes; they’re stored verbatim, not interpreted as folders by Storage. Returns 409 Duplicate if a file already exists at that path; use PUT or set the x-upsert: true header to overwrite. Headers:
  • Content-Type: the MIME type of the file. Checked against the bucket’s allowed_mime_types if set.
  • x-upsert: true (optional): overwrite if exists.
Body: raw file bytes.
Response: { "Id": "...", "Key": "avatars/user-123/avatar.png" }.

PUT /storage/v1/object//

Overwrite an existing file at the same path. Same body shape as POST. Returns 200 OK.

GET /storage/v1/object/public//

Fetch a file from a public bucket. No auth required. Returns the file bytes with the stored Content-Type. Returns 400 if the bucket is not public.

GET /storage/v1/object/authenticated//

Fetch a file from any bucket. Requires Authorization: Bearer <access token>. RLS on storage.objects decides whether the request succeeds.

DELETE /storage/v1/object//

Delete a single file. Requires DELETE policy match on storage.objects for the calling role.

POST /storage/v1/object/list/

List files in a bucket. Filtered by SELECT policies on storage.objects.
string
Only return files whose name starts with this prefix.
integer
Max files to return. Default 100.
integer
Pagination offset.
object
{ column: "name" | "created_at" | "updated_at", order: "asc" | "desc" }.
Substring search on name. Case-insensitive.
Response: array of { id, name, bucket_id, owner, created_at, updated_at, last_accessed_at, metadata }.

POST /storage/v1/object/copy

Copy a file to a new location (potentially in a different bucket).
string
required
Source bucket.
string
required
Source path.
string
Destination bucket. Defaults to source bucket.
string
required
Destination path.

POST /storage/v1/object/move

Move a file to a new location. Same parameters as copy; the source is deleted on success.

Signed URLs

For sharing files via TTL-bounded URLs that work without auth.

POST /storage/v1/object/sign//

Mint a download signed URL.
integer
URL TTL in seconds. Default 60. Max 604800 (7 days).
Response: { "signedURL": "/object/sign/documents/report.pdf?token=..." }. The URL is path-relative; prepend your BASE_URL to get the full URL.

GET /storage/v1/object/sign//?token=…

Fetch a file via a signed URL. The token is the signature; no other auth required.

POST /storage/v1/object/upload/sign//

Mint an upload signed URL: your server creates the URL, the client PUTs the file directly to it. Useful for high-bandwidth flows or offline clients.
integer
URL TTL in seconds. Default 7200 (2 hours).
Response: { "url": "/object/upload/sign/documents/report.pdf?token=..." }. The client then PUTs to <BASE_URL>{url} with the file bytes and Content-Type header.

POST /storage/v1/object/sign

Sign multiple URLs in one request. Body: { "paths": ["bucket1/path1", "bucket2/path2"], "expiresIn": 3600 }. Returns an array of signed URLs.

Image transformations

The render/image endpoints route through imgproxy. The path shape mirrors the object endpoints (public, authenticated, sign) with the same auth requirements.

GET /storage/v1/render/image/public//

Fetch a transformed image from a public bucket. Pass transformations as query parameters.
integer
Output width in pixels.
integer
Output height in pixels.
string
cover, contain, or fill. Default cover.
integer
JPEG/WebP quality, 0-100. Default 80.
string
webp, png, jpeg, or origin. Default origin (preserves the original format).

GET /storage/v1/render/image/authenticated//

Same as above but auth-gated.

GET /storage/v1/render/image/sign//?token=…

Same as above via signed URL. To mint a signed transformation URL, hit POST /storage/v1/object/sign/{bucket}/{path} with a transform parameter in the body:
Returns a signed URL that already includes the transformation parameters.

TUS resumable uploads

For files larger than 50MB (the per-request limit). Implements the TUS 1.0 protocol. Use a TUS client library rather than hand-rolling.

POST /storage/v1/upload/resumable

Initiate a resumable upload. Subsequent PATCH requests with Upload-Offset headers stream the chunks. HEAD requests query progress. Required headers:
  • apikey
  • Authorization: Bearer <token>
  • Upload-Length: <total bytes>
  • Upload-Metadata: bucketName <base64>,objectName <base64>,contentType <base64>
  • Tus-Resumable: 1.0.0
The TUS protocol is multi-step; see Storage uploads for a worked example with tus-js-client.

Error responses

Storage returns errors as {"statusCode": "<code>", "error": "<machine-readable>", "message": "<human-readable>"}. Common cases:

Next steps

Storage uploads

The four upload flows with worked examples.

Storage policies

RLS patterns on storage.objects.

Storage model

Buckets, public vs private, the underlying tables.

Auth Reference

The auth surface that mints the tokens you’ll use here.