---
name: azion-upload-and-download-objects
description: >-
  Upload objects to an Object Storage bucket from Azion Console, the API, or the Azion CLI, then list, download, replace, and delete them.
---

# Upload and download objects

You upload, list, download, replace, and delete the objects of an [Object Storage](/en/documentation/platform/object-storage/) bucket from Azion Console, the Azion API, the Azion CLI, or a function. An object exists as soon as its upload succeeds, stored under the key you gave it. The bucket then lists that key with its size and the time it was last modified.

To create the bucket itself, or to change the access level it carries, refer to [Create a bucket](/en/documentation/guides/application-development/data/create-and-modify-bucket/).

---

## Prerequisites

- A bucket. To create one, refer to [Create a bucket](/en/documentation/guides/application-development/data/create-and-modify-bucket/).
- A file on your machine to upload.
- Access to Azion Console, for the Console procedures. Refer to [Access Azion Console](/en/documentation/guides/platform/account-and-billing/how-to-access-azion-console/).
- A [personal token](/en/documentation/guides/platform/account-and-billing/personal-tokens/), for the API procedures.
- The [Azion CLI](/en/documentation/devtools/cli/) installed and authorized, for the CLI procedures.

---

## Upload an object using Azion Console

Azion Console is the shortest path for a handful of files. The API and the Azion CLI put the same upload inside a script or a pipeline. The files land in the files area of the bucket, described as "Browse, upload, and manage objects stored in this bucket." To upload them:

1. **Open the bucket**

   Access [Azion Console](https://console.azion.com/) > **Object Storage** > **Buckets**, then select the bucket.

2. **Add the files**

   Select **Upload files** for one or more files, or **Upload folder** for a directory. Dragging the files onto the drop target, which reads "Drag files here to add them to your bucket", adds them as well.

Each file is stored as an object of the bucket and appears in the files area under its key.

> **Caution**
>
> Azion Console refuses a file larger than 300 MB: "Files larger than 300 MB cannot be uploaded." The bound applies to the Console. For more information, refer to [Object Storage limits](/en/documentation/platform/object-storage/limits/).

---

## Upload an object using the API

The bucket name and the object key travel in the path, and the file travels in the request body. To upload the object:

1. **Send the upload request**

   Replace `[TOKEN VALUE]` with your personal token, `my-bucket` with your bucket, and `folder/file.csv` with the key you want:

   ```bash
   curl --location --request POST 'https://api.azion.com/v4/workspace/storage/buckets/my-bucket/objects/folder/file.csv' \
   --header 'Accept: application/json' \
   --header 'Authorization: Token [TOKEN VALUE]' \
   --header 'Content-Type: text/csv' \
   --data-binary '@./path/file.csv'
   ```

2. **Read the response**

   The API answers with HTTP `201`, or `202` when it processes the request asynchronously, and names the key it stored the object under:

   ```json
   {
     "state": "executed",
     "data": {
       "object_key": "folder/file.csv"
     }
   }
   ```

The object is stored under `folder/file.csv`. The `folder/` segment is part of the key: Object Storage creates the prefix with the upload, and no request creates it beforehand.

> **Note**
>
> The stored content type comes from the `Content-Type` header of the upload. A request that carries no `Content-Type` leaves Azion to detect the type. An object key holds 1 to 1,024 characters. For more information, refer to [Buckets and objects](/en/documentation/platform/object-storage/buckets-and-objects/).

---

## Upload an object using the Azion CLI

The command reads the file named in `--source` and stores it under `--object-key`. To upload the object:

```bash
azion create storage object --bucket-name my-bucket --object-key folder/file.csv --source ./path/file.csv
```

The command prints one line:

```text
Object created successfully
```

The object is stored in the bucket, under the key you passed in `--object-key`.

> **Caution**
>
> `--source` takes a path relative to the directory you run the command in. An absolute path fails, because the Azion CLI prepends the working directory to it.

---

## List the objects in a bucket

Every interface returns the keys the bucket holds, and the API narrows the list to one prefix.

### Azion Console

Access [Azion Console](https://console.azion.com/) > **Object Storage** > **Buckets**, then select the bucket. Its files area lists the objects it holds.

### The API

Send a `GET` request to the objects endpoint:

```bash
curl --location 'https://api.azion.com/v4/workspace/storage/buckets/my-bucket/objects' \
--header 'Accept: application/json' \
--header 'Authorization: Token [TOKEN VALUE]'
```

The response carries one entry per object: its key, the time it was last modified, its size in bytes, and whether the entry is a prefix.

```json
{
  "continuation_token": null,
  "results": [
    {
      "key": "folder/file.csv",
      "last_modified": "2026-01-01T12:00:00.000000Z",
      "size": 12,
      "is_folder": false
    }
  ]
}
```

Four query parameters shape the listing:

| Parameter            | What it does                                                                              |
| -------------------- | ----------------------------------------------------------------------------------------- |
| `prefix`             | Returns the keys that begin with the value. The default is empty, which returns every key |
| `all_levels`         | Defaults to `true` and returns the keys at every level under the prefix                   |
| `max_object_count`   | Sets how many entries one response carries, up to 1,000                                   |
| `continuation_token` | Returns the entries that follow the token the previous response carried                   |

With `all_levels=false`, a prefix is returned as one entry instead of the keys under it:

```json
{"key": "folder/", "last_modified": null, "size": 0, "is_folder": true}
```

### The Azion CLI

Name the bucket with `--bucket-name`:

```bash
azion list storage object --bucket-name my-bucket --page-size 5
```

The command prints a table with a `KEY` column and a `LAST MODIFIED` column. `--details` adds a `SIZE` column, and `--next-page` moves to the next page.

The listing names every key the bucket holds, which is how you confirm an upload arrived.

---

## Download an object

A download returns the bytes of one object, addressed by its key.

### The API

Send a `GET` request to the key:

```bash
curl --location 'https://api.azion.com/v4/workspace/storage/buckets/my-bucket/objects/folder/file.csv' \
--header 'Authorization: Token [TOKEN VALUE]'
```

The API answers with HTTP `200` and the object as `application/octet-stream`, so the terminal prints the contents of the object. A key that does not exist answers with HTTP `404` and error `17013`, `Object Does Not Exist`.

To write the object to a local file instead, add the `curl` options `-O` and `-J`. They take the object name from the response headers and write the output into a file:

```bash
curl --location 'https://api.azion.com/v4/workspace/storage/buckets/my-bucket/objects/folder/file.csv' \
--header 'Authorization: Token [TOKEN VALUE]' \
-O -J
```

> **Note**
>
> A key that carries a prefix, such as `folder/file.csv`, creates no directory. The file is written to the directory you ran the command in.

### The Azion CLI

Name the bucket and the key:

```bash
azion describe storage object --bucket-name my-bucket --object-key folder/file.csv
```

The command prints the content of the object.

You now hold the object's bytes, in the terminal or in a local file.

---

## Replace an object

Two methods write over the object stored under a key, and they differ in what they do with a key that does not exist yet. `POST` stores the object either way and answers with HTTP `201`. `PUT` replaces only: a `PUT` to a key that does not exist answers with HTTP `404` and error `17013`, `Object Does Not Exist`.

### The API

Send a `PUT` request to the key, with the new file as the body:

```bash
curl --location --request PUT 'https://api.azion.com/v4/workspace/storage/buckets/my-bucket/objects/folder/file.csv' \
--header 'Accept: application/json' \
--header 'Authorization: Token [TOKEN VALUE]' \
--header 'Content-Type: text/csv' \
--data-binary '@./path/file.csv'
```

The API answers with HTTP `200`, or `202` when it processes the request asynchronously, and names the key it replaced:

```json
{
  "state": "executed",
  "data": {
    "object_key": "folder/file.csv"
  }
}
```

### The Azion CLI

Name the new file in `--source`:

```bash
azion update storage object --bucket-name my-bucket --object-key folder/file.csv --source ./path/file.csv
```

The command prints one line:

```text
Object updated successfully
```

`azion update storage object -h` lists every flag it accepts.

The key now addresses the new content, and the content it held is gone.

> **Note**
>
> A key cannot be renamed. Azion Console carries a **Move** dialog, which asks for the destination folder path and takes the bucket root when the path is empty. The move copies the object to the destination and deletes the original, so the key itself is still immutable.

---

## Delete an object

A delete is asynchronous. Azion accepts the request, the key leaves the listing at once, and the object is permanently removed after a 24-hour grace period.

### Azion Console

To delete the objects:

1. **Open the bucket**

   Access [Azion Console](https://console.azion.com/) > **Object Storage** > **Buckets**, then select the bucket.

2. **Select the files to delete**

3. **Confirm the deletion**

   Azion Console asks "Are you sure you want to delete the selected files?" before it removes them.

The objects leave the files area of the bucket.

### The API

Send a `DELETE` request to the key:

```bash
curl --location --request DELETE 'https://api.azion.com/v4/workspace/storage/buckets/my-bucket/objects/folder/file.csv' \
--header 'Accept: application/json' \
--header 'Authorization: Token [TOKEN VALUE]'
```

The API answers with HTTP `202` and reports the delete as pending:

```json
{"state": "pending"}
```

The key is gone from the listing, and a request for it answers with HTTP `404` right away.

### The Azion CLI

Name the bucket and the key:

```bash
azion delete storage object --bucket-name my-bucket --object-key folder/file.csv
```

The command names the key it removed:

```text
Object folder/file.csv was deleted successfully
```

The object answers no request, and a second delete of the same key returns error `17013`, `Object Does Not Exist`.

> **Caution**
>
> Azion removes a deleted object permanently after a 24-hour grace period, and refuses to delete the bucket during that period. For more information, refer to [Create a bucket](/en/documentation/guides/application-development/data/create-and-modify-bucket/).

---

## Read and write objects from a function

A function reaches the same bucket through the `azion:storage` module, so an application stores what a request carries and returns what the bucket holds. To put the bucket behind a function:

1. **Create the function**

   Create a function in [Functions](/en/documentation/platform/functions/) with this code. It routes by method. A `POST` writes the request body under the key taken from the request path. A `GET` reads the object back and answers with the content type it was stored with.

   ```js
   import Storage from "azion:storage";

   async function doGet(path, bucket_name) {
       const storage = new Storage(bucket_name);
       const asset = await storage.get(path);
       return new Response(await asset.arrayBuffer(), {
           headers: {
               "Content-Type": asset.contentType
           },
       });
   }

   async function doPost(path, content_type, value, bucket_name) {
       let options = {
           "content-type": content_type
       }
       const storage = new Storage(bucket_name);
       await storage.put(path, value, options);
       return new Response("Object added.");
   }

   async function router(event) {
       const request = event.request;
       const method = request.method;
       const path = decodeURI(new URL(request.url).pathname);
       const bucket_name = event.args.bucket;
       if (method === "POST") {
           let content_type = request.headers.get("Content-Type");
           let content = await request.arrayBuffer();
           return doPost(path, content_type, content, bucket_name);
       } else if (method === "GET") {
           return doGet(path, bucket_name);
       } else {
           throw new Error(`Invalid method: ${method}. Expected POST or GET.`);
       }
   }

   addEventListener("fetch", (event) => {
       event.respondWith(
           router(event)
       );
   });
   ```

   The values it carries:

   | Variable       | Description                                        |
   | -------------- | -------------------------------------------------- |
   | `path`         | The path to the object. Example: `./path/file.csv` |
   | `bucket_name`  | The name of the bucket. Example: `my-bucket`       |
   | `content_type` | The MIME type of the object. Example: `text/csv`   |
   | `value`        | The contents of the object, as binary data         |

2. **Set the bucket argument**

   The function reads the bucket name from its own arguments, so add the `bucket` property with the name of your bucket as a string:

   ```json
   {
     "bucket": "my-bucket"
   }
   ```

3. **Instantiate the function in an application**

   A function answers a request only once an [application](/en/documentation/platform/applications/) runs it, so instantiate the function in the application that receives the uploads.

A `POST` to the application stores the request body in the bucket. A `GET` to the same path returns the object with the content type it was stored with.

> **Note**
>
> `storage.put` and `storage.get` are two of the methods the module carries, and `storage.list` and `storage.delete` cover the other two operations on this page. For every method, its parameters, and what it returns, refer to [Storage runtime API](/en/documentation/devtools/runtime/api-reference/storage/).

---

## Next steps

- [Use a bucket as an application origin](/en/documentation/guides/application-development/data/use-bucket-as-origin.md): Point an application at the bucket so these objects answer requests from the internet.
- [Buckets and objects](/en/documentation/platform/object-storage/buckets-and-objects.md): Every field, parameter, and error of the bucket and object endpoints.
- [Use S3-compatible tools with Object Storage](/en/documentation/guides/application-development/data/use-s3-compatible-tools-with-object-storage.md): Manage the same objects with an S3 client and a credential.
- [Troubleshooting](/en/documentation/platform/object-storage/troubleshooting.md): What each rejection means when an upload, a download, or a delete fails.
