> ## Documentation Index
> Fetch the complete documentation index at: https://upstash.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Upstash Blob Quickstart

> Create a bucket, upload a file straight from the browser, and read it back.

Building with an AI coding agent?

Connect the Upstash MCP server and the Blob skill to Claude Code, Cursor, Codex, and other agents.

[Open](https://upstash.com/docs/agent-resources/overview.md)

Upstash Blob is S3-compatible object storage. Each server SDK has a bucket client and an upload handler, and `@upstash/blob` has React hooks that upload from the browser straight to storage. This page builds a working file picker in React. The server code is Next.js App Router in TypeScript, FastAPI in Python, or `net/http` in Go. Other frameworks work the same way; see [Other frameworks](https://upstash.com/docs/blob/sdks/ts/upload-handler#other-frameworks) in TypeScript, [Mounting it](https://upstash.com/docs/blob/sdks/py/upload-handler#mounting-it) in Python and [Mounting it](https://upstash.com/docs/blob/sdks/go/upload-handler#mounting-it) in Go.

***

## Setup

```bash npm theme={"system"}
npm install @upstash/blob
```

```bash pnpm theme={"system"}
pnpm add @upstash/blob
```

```bash yarn theme={"system"}
yarn add @upstash/blob
```

```bash bun theme={"system"}
bun add @upstash/blob
```

```bash pip theme={"system"}
pip install upstash-blob
```

```bash uv theme={"system"}
uv add upstash-blob
```

```bash go theme={"system"}
go get github.com/upstash/blob-go
```

`upstash-blob` is the Python server SDK and `github.com/upstash/blob-go` the Go one. The browser side is `@upstash/blob` whichever language runs the server, so a Python or Go app installs both.

Create a bucket in the [Upstash Console](https://console.upstash.com) and put its token in your environment.

```bash .env theme={"system"}
UPSTASH_BLOB_TOKEN=...
```

The console asks whether the bucket is public or private:

* **Public**: every object has a public URL. For avatars, product images, anything a page links to directly.
* **Private**: no public URL. Every read goes through a time-limited [signed URL](https://upstash.com/docs/blob/bucket/reading#signed-read-urls). For user documents, invoices, anything that must not be guessable.

***

## Upload from your server

```ts TypeScript theme={"system"}
// lib/reports.ts
import { Bucket } from "@upstash/blob"

const bucket = Bucket.fromEnv()

export async function saveReport(file: Blob) {
  const blob = await bucket.put("reports/2026-01.pdf", file, {
    contentType: "application/pdf",
  })
  return blob.url
}
```

```python Python theme={"system"}
# app/reports.py
from pathlib import Path

from upstash_blob import Bucket

bucket = Bucket.from_env()


def save_report(pdf: Path) -> str | None:
    blob = bucket.put("reports/2026-01.pdf", pdf, content_type="application/pdf")
    return blob.url
```

```go Go theme={"system"}
// reports.go
package reports

import (
	"context"
	"os"

	blob "github.com/upstash/blob-go"
)

func SaveReport(
	ctx context.Context, bucket *blob.Bucket, pdf *os.File,
) (string, error) {
	b, err := bucket.Put(ctx, "reports/2026-01.pdf", pdf, &blob.PutOptions{
		ContentType: "application/pdf",
	})
	if err != nil {
		return "", err
	}
	return b.URL, nil
}
```

Bytes already on your server go to the bucket with `put`. The returned record's URL is the public object URL, not set on a private bucket. See [Writing](https://upstash.com/docs/blob/bucket/writing), and the [TypeScript SDK](https://upstash.com/docs/blob/sdks/ts/overview), [Python SDK](https://upstash.com/docs/blob/sdks/py/overview) or [Go SDK](https://upstash.com/docs/blob/sdks/go/overview) for setup details.

***

## Upload from the browser

Files a user picks go straight from the browser to storage. Your server only authorizes the upload and records the result, so the bytes never pass through it.

**1. Write the upload handler**

```ts TypeScript theme={"system"}
// lib/uploads.ts
import "server-only"
import { uploadHandler } from "@upstash/blob"

export const uploads = uploadHandler({
  constraints: {
    maxSize: "20mb",
    contentTypes: ["image/*", "application/pdf"],
  },

  onBeforeUpload: ({ file }) => ({ path: `images/${file.name}` }),
})
```

```python Python theme={"system"}
# app/uploads.py
from upstash_blob import AsyncUploadHandler, BeforeUploadResult

uploads = AsyncUploadHandler(
    max_size="20mb",
    content_types=["image/*", "application/pdf"],
    on_before_upload=lambda args: BeforeUploadResult(path=f"images/{args.file.name}"),
)
```

```go Go theme={"system"}
// uploads.go
uploads, err := blob.NewUploadHandler(blob.UploadHandlerOptions[struct{}]{
	MaxSize:      20 * blob.MB,
	ContentTypes: []string{"image/*", "application/pdf"},

	OnBeforeUpload: func(
		a blob.BeforeUploadArgs[struct{}],
	) (blob.BeforeUploadResult, error) {
		return blob.BeforeUploadResult{Path: "images/" + a.File.Name}, nil
	},
})
```

The handler runs on your server. It decides who may upload, where the object goes, and what happens once it lands. It never sees the bytes.

This one accepts anyone. [Upload handler](https://upstash.com/docs/blob/uploads/upload-handler) adds the auth check and the completion callback.

**2. Mount it as a route**

```ts TypeScript theme={"system"}
// app/api/upload/route.ts
import { uploads } from "@/lib/uploads"

export const { GET, POST } = uploads
```

```python Python theme={"system"}
# app/main.py (FastAPI)
from fastapi import FastAPI
from upstash_blob.fastapi import upload_view

from app.uploads import uploads

app = FastAPI()
app.add_api_route("/api/upload", upload_view(uploads), methods=["GET", "POST"])
```

```go Go theme={"system"}
// main.go
http.Handle("/api/upload", uploads)
log.Fatal(http.ListenAndServe(":8080", nil))
```

`POST` runs the upload and `GET` serves the route's constraints. The hooks look at `/api/upload` by default, so nothing else has to name a URL.

**3. Bind the hooks to the handler**

```ts lib/upload-hooks.ts theme={"system"}
"use client"

import { uploadHooks } from "@upstash/blob/react"
import type { uploads } from "./uploads"

export const { useUpload } = uploadHooks<typeof uploads>()
```

`uploadHooks<typeof uploads>()` reads the handler's type, so route names and completion data are checked at compile time. The `import type` is erased at build time and never pulls server code into the browser bundle.

A Python or Go handler has no type to bind to. Call `uploadHooks()` with no type parameter and pass the URL, `useUpload("/api/upload")`, in the next step. [FastAPI + React](https://upstash.com/docs/blob/recipes/fastapi-react) has the whole app, including the dev proxy that puts `/api` on the page's origin.

**4. Upload a file**

```tsx app/page.tsx theme={"system"}
"use client"

import { useUpload } from "@/lib/upload-hooks"

export default function Page() {
  const { start, upload, accept } = useUpload()

  return (
    <div>
      <input
        type="file"
        accept={accept}
        onChange={(e) => start({ file: e.target.files?.[0] })}
      />

      {upload && <p>{upload.status}</p>}
      {upload?.pending && <progress value={upload.percent} max={100} />}
      {upload?.status === "done" && (
        <a href={upload.blob.url}>{upload.blob.path}</a>
      )}
      {upload?.status === "error" && <p>{upload.error.message}</p>}
    </div>
  )
}
```

You get these without more code:

* **Multipart for large files.** Past 16 MB the SDK switches to parts, with pause, resume and per-part retry. See [Large files](https://upstash.com/docs/blob/uploads/large-files).
* **Retries.** Failed parts back off and retry, and an expired signature is refreshed mid-upload.
* **A picker that matches the server.** `accept` comes from the route's own `GET`, so an oversized file is refused before any request goes out. See [Constraints](https://upstash.com/docs/blob/uploads/constraints).
* **Progress.** `percent`, `status` and `pending` read the same for one PUT or 200 parts.
* **Types end to end.** With a TypeScript handler, whatever `onUploadComplete` returns is `upload.blob.data` on the client, typed.

***

## Next steps

> [card] [Upload handler](https://upstash.com/docs/blob/uploads/upload-handler)
>
> Auth, routes, and the completion callback in full.

> [card] [Upload client](https://upstash.com/docs/blob/uploads/upload-client)
>
> `useUpload`, the record it renders, and the non-React client.

> [card] [Writing](https://upstash.com/docs/blob/bucket/writing)
>
> `put`, metadata, conditional writes and multipart from the server.

> [card] [Recipes](https://upstash.com/docs/blob/recipes/avatars)
>
> Avatars, attachments and private documents, wired end to end.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.