# Upload files

> Source: https://docs.strapi.io/cms/api/rest/upload

The `/api/upload` REST API endpoints enable you to upload files to the Media Library, retrieve paginated file lists, update file metadata, and delete files from your Strapi application.

The [Media Library feature](/cms/features/media-library) is powered in the back-end server of Strapi by the `upload` package. To upload files to Strapi, you can either use the Media Library directly from the admin panel, or use the [REST API](/cms/api/rest), with the following available endpoints :

| Method | Path                       | Description                  |
| :----- | :------------------------- | :--------------------------- |
| GET    | `/api/upload/files`        | Get a list of files          |
| GET    | `/api/upload/files/page`   | Get a paginated list of files |
| GET    | `/api/upload/files/:id`    | Get a specific file          |
| POST   | `/api/upload`           | Upload files        |
| POST   | `/api/upload?id=x`      | Update fileInfo     |
| DELETE | `/api/upload/files/:id` | Delete a file       |

:::note Notes
- [Folders](/cms/features/media-library#organizing-assets-with-folders) are an admin panel-only feature and are not part of the Content API (REST or GraphQL). Files uploaded through REST are located in the automatically created "API Uploads" folder.
- The GraphQL API does not support uploading media files. To upload files, use the REST API or directly add files from the [Media Library](/cms/features/media-library) in the admin panel. Some GraphQL mutations to update or delete uploaded media files are still possible (see [GraphQL API documentation](/cms/api/graphql#mutations-on-media-files) for details).
:::

## Get a list of files

2 endpoints return files from the Media Library: `/api/upload/files` returns every file as a flat array, while `/api/upload/files/page` returns files using the standard paginated response. Use `/api/upload/files/page` for any non-trivial Media Library.

### Get all files

`GET /api/upload/files` returns a flat array of all files in the Media Library:

```json
[
  {
    "id": 1,
    "documentId": "a1b2c3...",
    "name": "photo.jpg",
    "url": "/uploads/photo.jpg",
    "mime": "image/jpeg",
    "size": 12.34
    // ...other file fields
  }
  // ...
]
```

This endpoint ignores pagination parameters and always returns every file. For large media libraries the response can be very large, so prefer `/api/upload/files/page` instead.

### Get a paginated list of files

`GET /api/upload/files/page` returns files using the standard [paginated response](/cms/api/rest/sort-pagination), wrapped in a `data` array and a `meta.pagination` object.

It accepts the same query parameters as other REST collection endpoints:

| Parameter | Description |
| --------- | ----------- |
| `pagination[page]` | Page number (1-based). Default `1`. |
| `pagination[pageSize]` | Number of files per page. Defaults to the `api.rest.defaultLimit` [configuration](/cms/configurations/api) (`25`), capped by `api.rest.maxLimit` if set. |
| `pagination[start]` | Pagination by offset: number of files to skip. |
| `pagination[limit]` | Pagination by offset: maximum number of files to return. |
| `pagination[withCount]` | Whether to run the count query and include `total` and `pageCount` in the response. Defaults to `true`. |
| `filters` | [Filter](/cms/api/rest/filters) the results. |
| `sort` | [Sort](/cms/api/rest/sort-pagination#sorting) the results. |
| `fields` | [Select](/cms/api/rest/populate-select#field-selection) which fields to return. |
| `populate` | [Populate](/cms/api/rest/populate-select#population) relations. |

:::note
Pagination parameters use the nested format (`pagination[page]=2&pagination[pageSize]=10`), not the flat format (`page=2&pageSize=10`). Pagination by page (`page`/`pageSize`) and pagination by offset (`start`/`limit`) are mutually exclusive: combining them returns a `400` error. For details on both pagination methods, see [Sort & Pagination](/cms/api/rest/sort-pagination#pagination).
:::

**Example request: Get the second page of 10 files:**
`GET /api/upload/files/page?pagination[page]=2&pagination[pageSize]=10`

**Example response:**
```json
{
  "data": [
    {
      "id": 1,
      "documentId": "a1b2c3...",
      "name": "photo.jpg",
      "url": "/uploads/photo.jpg",
      "mime": "image/jpeg",
      "size": 12.34
      // ...other file fields
    }
  ],
  "meta": {
    "pagination": {
      "page": 2,
      "pageSize": 10,
      "pageCount": 4,
      "total": 100
    }
  }
}
```

When using pagination by offset, the `meta.pagination` object returns `start` and `limit` instead of `page` and `pageSize`:

**Example request: Get 5 image files, skipping the first 20, without a total count:**
`GET /api/upload/files/page?pagination[start]=20&pagination[limit]=5&pagination[withCount]=false&filters[mime][$startsWith]=image/`

**Example response:**
```json
{
  "data": [
    // ...
  ],
  "meta": {
    "pagination": {
      "start": 20,
      "limit": 5
    }
  }
}
```

When `pagination[withCount]` is `false`, the count query is skipped and `total` and `pageCount` are omitted from the response.

## Upload files

Upload one or more files to your application.

`files` is the only accepted parameter, and describes the file(s) to upload. The value(s) can be a Buffer or Stream.

:::info Signed URLs with private S3 buckets
When using AWS S3 with the `ACL` parameter set to `"private"`, file URLs returned by the upload endpoints are automatically signed. Signed URLs include `X-Amz-Signature` query parameters and an `isUrlSigned: true` flag in the response, making them accessible despite the private bucket ACL. Signed URLs expire based on your `signedUrlExpires` configuration (default: 15 minutes).
:::

:::tip
When uploading an image, include a `fileInfo` object to set the file name, alt text, and caption.
:::

<Tabs>
<TabItem value="browser" label="Browser">

```html
<form>
  <!-- Can be multiple files -->
  <input type="file" name="files" />
  <input
    type="hidden"
    name="fileInfo"
    value='{"name":"homepage-hero","alternativeText":"Person smiling while
      holding laptop","caption":"Hero image used on the homepage"}'
  />
  <input type="submit" value="Submit" />
</form>

<script type="text/javascript">
  const form = document.querySelector('form');

  form.addEventListener('submit', async (e) => {
    e.preventDefault();

    await fetch('/api/upload', {
      method: 'post',
      body: new FormData(e.target)
    });
  });
</script>
```

```js

const file = await blobFrom('./1.png', 'image/png');
const form = new FormData();

form.append('files', file, "1.png");
form.append(
  'fileInfo',
  JSON.stringify({
    name: 'Homepage hero',
    alternativeText: 'Person smiling while holding laptop',
    caption: 'Hero image used on the homepage',
  })
);

const response = await fetch('http://localhost:1337/api/upload', {
  method: 'post',
  body: form,
});

```

:::caution
You have to send FormData in your request body.
:::

## Upload entry files

Upload one or more files that will be linked to a specific entry.

The following parameters are accepted:

| Parameter | Description |
| --------- | ----------- |
|`files`    | The file(s) to upload. The value(s) can be a Buffer or Stream. |
|`path` (optional) | The folder where the file(s) will be uploaded to (only supported on strapi-provider-upload-aws-s3). |
| `refId` | The ID of the entry which the file(s) will be linked to. |
| `ref` | The unique ID (uid) of the model which the file(s) will be linked to (see more below). |
| `source` (optional) | The name of the plugin where the model is located. |
| `field` | The field of the entry which the file(s) will be precisely linked to. |

For example, given the `Restaurant` model attributes:

```json title="/src/api/restaurant/content-types/restaurant/schema.json"
{
  // ...
  "attributes": {
    "name": {
      "type": "string"
    },
    "cover": {
      "type": "media",
      "multiple": false,
    }
  }
// ...
}
```

The following is an example of a corresponding front-end code:

```html
<form>
  <!-- Can be multiple files if you setup "collection" instead of "model" -->
  <input type="file" name="files" />
  <input type="text" name="ref" value="api::restaurant.restaurant" />
  <input type="text" name="refId" value="5c126648c7415f0c0ef1bccd" />
  <input type="text" name="field" value="cover" />
  <input type="submit" value="Submit" />
</form>

<script type="text/javascript">
  const form = document.querySelector('form');

  form.addEventListener('submit', async (e) => {
    e.preventDefault();

    await fetch('/api/upload', {
      method: 'post',
      body: new FormData(e.target)
    });
  });
</script>
```

:::caution
You have to send FormData in your request body.
:::

## Update fileInfo

Update a file in your application.

`fileInfo` is the only accepted parameter, and describes the fileInfo to update:

```js

const fileId = 50;
const newFileData = {
  alternativeText: 'My new alternative text for this image!',
};

const form = new FormData();

form.append('fileInfo', JSON.stringify(newFileData));

const response = await fetch(`http://localhost:1337/api/upload?id=${fileId}`, {
  method: 'post',
  body: form,
});

```

## Models definition

Adding a file attribute to a [model](/cms/backend-customization/models) (or the model of another plugin) is like adding a new association.

The following example lets you upload and attach one file to the `avatar` attribute:

```json title="/src/api/restaurant/content-types/restaurant/schema.json"

{
  // ...
  {
    "attributes": {
      "pseudo": {
        "type": "string",
        "required": true
      },
      "email": {
        "type": "email",
        "required": true,
        "unique": true
      },
      "avatar": {
        "type": "media",
        "multiple": false,
      }
    }
  }
  // ...
}

```

The following example lets you upload and attach multiple pictures to the `restaurant` content-type:

```json title="/src/api/restaurant/content-types/restaurant/schema.json"
{
  // ...
  {
    "attributes": {
      "name": {
        "type": "string",
        "required": true
      },
      "covers": {
        "type": "media",
        "multiple": true,
      }
    }
  }
  // ...
}
```
