> ## Documentation index
> Fetch the complete documentation index at: https://upstash.com/docs/llms.txt
> Ask it with GET https://upstash.com/docs/ask?q=<keywords or question>. Short keywords rank best.
> Use these to discover all available pages before exploring further.
> Feature requests: https://upstash.com/docs/common/help/feature-requests.md lists what users asked for and how to vote for a request or add one.
> Page rating: `GET https://upstash.com/docs/feedback?path=/docs/blob/reference/types&helpful=true` (or `false`) records whether this page helped. An optional `comment` (URL-encoded, at most 500 characters) says what was missing or wrong. It is anonymous and needs no key. Comments hold no code, secrets or personal data.

# Types

> Size, Duration, CacheOption, the record types, and the phases an upload moves through.

The option and record types the rest of these docs refer to, and the upload phases named in error messages.

---

## Size

<CodeGroup>
```ts TypeScript
maxSize: 4096       // 4,096 bytes
maxSize: "20MB"     // 20,000,000 bytes
maxSize: "32MiB"    // 33,554,432 bytes
multipart: "100MB"
```

```python Python
max_size=4096       # 4,096 bytes
max_size="20MB"     # 20,000,000 bytes
max_size="32MiB"    # 33,554,432 bytes
multipart="100MB"
```

```go Go
MaxSize: 4096          // 4,096 bytes
MaxSize: 20 * blob.MB  // 20,000,000 bytes
MaxSize: 32 * blob.MiB // 33,554,432 bytes
Multipart: blob.MultipartOver(100 * blob.MB)
```
</CodeGroup>

A byte count, never a bit count. A bare number is bytes. String units ignore case, so `MB` and `mb` both mean megabytes.

| Units | Base | Example |
| --- | --- | --- |
| `B`, `KB`, `MB`, `GB`, `TB` | Decimal, powers of 1,000 | `"32MB"` = 32,000,000 bytes |
| `KiB`, `MiB`, `GiB`, `TiB` | Binary, powers of 1,024 | `"32MiB"` = 33,554,432 bytes |

For a 32 MiB file limit, use `"32MiB"` or `33554432`. A `"32MB"` limit is smaller and rejects that file. Existing decimal units keep their values. Bit units such as `mbit` and `Mbps` are not supported.

An unparseable size throws where the option is written, not per request. In Go a size is an `int64` of bytes written with the unit constants, and `blob.ParseSize` reads the string form from configuration.

A limit you write by hand takes a size: the max size, the multipart threshold. A measurement you forward is a plain number of bytes: the size option on `put`, and every size the SDK returns.

`formatBytes`, exported from every TypeScript entrypoint, always displays decimal byte units, including values supplied as MiB: `formatBytes(2_400_000)` is `2.4 MB`.

---

## Duration

<CodeGroup>
```ts TypeScript
expiresIn: 300       // 5 minutes
expiresIn: "5m"
olderThan: "7d"
cache: "1h"
```

```python Python
expires_in=300                   # 5 minutes
expires_in="5m"
expires_in=timedelta(minutes=5)
older_than="7d"
cache="1h"
```

```go Go
ExpiresIn: 5 * time.Minute
olderThan := 7 * 24 * time.Hour
Cache: "1h"
```
</CodeGroup>

A span of time. A bare number is **seconds**; a string takes a unit: `ms`, `s`, `m`, `h`, `d`, or a long form (`sec`, `seconds`, `min`, `minutes`, `hr`, `hours`, `day`, `days`). A string with no unit is seconds. An unparseable duration throws an error naming the option. In Go a lifetime or window is a `time.Duration`; only `Cache` takes the string form.

---

## CacheOption

<CodeGroup>
```ts TypeScript
cache: "immutable"
cache: "revalidate"
cache: "no-store"
cache: "15m"
cache: "public, max-age=60, s-maxage=31536000"
```

```python Python
cache="immutable"
cache="revalidate"
cache="no-store"
cache="15m"
cache="public, max-age=60, s-maxage=31536000"
```

```go Go
Cache: blob.CacheImmutable  // "immutable"
Cache: blob.CacheRevalidate // "revalidate"
Cache: blob.CacheNoStore    // "no-store"
Cache: "15m"
Cache: "public, max-age=60, s-maxage=31536000"
```
</CodeGroup>

One of three words, a duration, or a raw `Cache-Control` header. The header each one stores is on [Caching](/blob/bucket/caching#the-cache-option).

How each SDK types these three: [TypeScript](/blob/sdks/ts/overview#option-types), [Python](/blob/sdks/py/overview#option-types), [Go](/blob/sdks/go/overview#option-types).

---

## Records

Four record shapes come back from the SDK. They nest.

| Record | Fields | Returned by |
| --- | --- | --- |
| Object | path, URL, versioned URL, size, etag, uploaded at | `list`, `copy`, `move`, the JSON update |
| Completed object | an object plus content type | `put`, the completion callback, a finished browser upload |
| Info | an object plus content type and metadata | `info` |
| Download | info plus the body | `get` |

Object fields:

| Field | Description |
| --- | --- |
| Path | The object's key. |
| URL | The public object URL. Not set on a private bucket. |
| Versioned URL | The URL with `?v=<etag>`, etag percent-encoded. Not set when the URL is not. |
| Size | Bytes. |
| Etag | Storage's etag, quoted: `"9f3c..."`. |
| Uploaded at | Last modified. |

Across the SDK, `blob` names a record, never the bytes. Bytes go in and come out as `body` (`content` on Python's `get`).

The type names and field types in each SDK: [TypeScript](/blob/sdks/ts/bucket#records), [Python](/blob/sdks/py/bucket#records), [Go](/blob/sdks/go/bucket#records).

---

## Upload phases

A direct browser upload makes up to four kinds of request to your upload route. The names appear in error messages and across these docs.

| Phase | When | What your route does |
| --- | --- | --- |
| `begin` | First request, once | Checks constraints, runs the before-upload callback, signs the first URL(s). Never retried. |
| `parts` | Multipart only, as needed | Signs the next batch of part URLs. |
| `end` | Once the bytes landed | Completes the upload, runs the completion callback. Retried by the browser on failure. |
| `cancel` | On `cancel()` | Aborts the multipart upload, or deletes the single-PUT object. |

Every request and every successful answer carries `protocol`, the version of this exchange, in the JSON body. It is separate from the package version, so a browser bundle and a server on different SDK releases, or a server written with another server SDK, still understand each other. A missing value means `1`, changes are only ever additive, and neither side refuses the other's version.

The PUTs that carry the bytes go to storage, never to your route. [How signing works](/blob/reference/signing#the-direct-upload-handshake) has the full handshake.
