# List uploads

> Page through the files you have uploaded and refresh their download URLs.

Canonical page: https://www.rundiffusion.com/docs/api/uploads/list
Endpoint: GET /api/v2/uploads
Authorization: OAuth device flow or Personal API Access Token or Company API Access Token

---

Lists the caller's uploads, newest first. Use it to find a file you sent
earlier, and to refresh signed URLs that have expired.

`GET /api/v2/uploads`

Authorization: OAuth device flow or Personal API Access Token or Company API Access Token

## Request

**Headers**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Authorization` | string | Yes | Bearer token. This endpoint accepts an OAuth device flow token, a Personal API Access Token, or a Company API Access Token. See [Authentication](/docs/api/authentication). |

Both parameters are optional. With neither you get the most recent 24 uploads.

**Query**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | string | No | Opaque pagination token. When a response has has_more true, pass its next_cursor here to get the following page. Do not construct or parse one. An unparseable value is rejected with 400 INVALID_CURSOR. |
| `limit` | integer | No | Page size, from 1 to 24. Default: `24` |

cURL:

```bash
curl "https://api2.rundiffusion.com/api/v2/uploads?limit=24" \
  -H "Authorization: Bearer $RUNDIFFUSION_TOKEN"
```

JavaScript:

```javascript
const page = await fetch(
  'https://api2.rundiffusion.com/api/v2/uploads?limit=24',
  { headers: { Authorization: `Bearer ${process.env.RUNDIFFUSION_TOKEN}` } },
).then(r => r.json());
```

Python:

```python
page = requests.get(
    "https://api2.rundiffusion.com/api/v2/uploads",
    headers={"Authorization": f"Bearer {os.environ['RUNDIFFUSION_TOKEN']}"},
    params={"limit": 24},
).json()
```

## Response

`200 OK` with one cursor page of upload records. Each entry has the same shape
as the [create response](/docs/api/uploads/create#response).

```json
{
  "data": [
    {
      "id": "Yb3kQ9…",
      "name": "fa9c1e22-….png",
      "mime_type": "image/png",
      "size_bytes": 1863245,
      "width": 1024,
      "height": 1024,
      "url": "https://rundiffusion.com/...",
      "thumb_url": "https://rundiffusion.com/...",
      "created": "2026-07-23T09:31:02Z"
    }
  ],
  "has_more": true,
  "next_cursor": "eyJhIjoiMjAyNi0…",
  "last_cursor": null
}
```

### Top level

| Name | Type | Description |
| --- | --- | --- |
| `data` | array | The uploads on this page, newest first. Empty when the caller has none. |
| `has_more` | boolean | Whether more pages exist beyond this one. Branch on this when paging. |
| `next_cursor` | string \| null | Pass this back as cursor to fetch the next page. Null on the last page. |
| `last_cursor` | string \| null | Echoes the cursor you sent on this request. Null on the first page. It points backwards, so paging with it loops. |

### Upload object

| Name | Type | Description |
| --- | --- | --- |
| `id` | string | The handle for this upload, a 20-character opaque string. |
| `name` | string \| null | The stored filename, generated as {uuid}.{ext}. Not the filename you sent. |
| `mime_type` | string \| null | MIME type as stored. |
| `size_bytes` | integer \| null | Size of the stored file in bytes. |
| `width` | integer \| null | Pixel width of the stored image. |
| `height` | integer \| null | Pixel height of the stored image. |
| `url` | string \| null | Signed download URL, valid for roughly seven days. Re-list to refresh it rather than storing it. |
| `thumb_url` | string \| null | Signed URL to a 512x512 WebP thumbnail, same seven-day expiry. Falls back to the same value as url until the thumbnail has been generated. |
| `created` | string (ISO 8601) | When it was created, in UTC. |

> **Page with next_cursor, not last_cursor**
>
> `last_cursor` echoes the cursor you sent, so paging with it re-requests the page
> you just read. Read `has_more`, and when it is true send `next_cursor` as
> `cursor`.

> **Re-list to refresh expired URLs**
>
> `url` and `thumb_url` are signed and expire after roughly seven days. There is
> no refresh endpoint: listing again returns freshly signed URLs for the same
> files. Store the `id`, not the URL.

## Errors

| Name | Type | Description |
| --- | --- | --- |
| `400` | INVALID_CURSOR | The cursor could not be parsed. Start again without one. |
| `401` | UNAUTHENTICATED | Missing, malformed, or expired token. |
| `429` | RATE_LIMITED | Too many requests. Back off and retry per the Retry-After header. See [Rate limits](/docs/api/rate-limits#uploads). |

See [Errors](/docs/api/errors) for the full envelope and the code list.
