# List boards

> Page through the boards you can see, filtered by access level, owner, name, or date.

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

---

A board holds an ordered set of nodes. This returns the boards the credential can
see on the account it acts as, newest first.

`GET /api/v2/boards`

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

Which boards those are depends on the account. On a personal account it is your
own boards, and nothing else. On a team it follows the same rules the app does:

| Access level | Who sees it |
| --- | --- |
| `SHARED` | everyone on the team |
| `RESTRICTED` | the members listed on it, the owner always among them |
| `PRIVATE` | the owner alone |

A member holding **Can manage everyone's generations and boards**
(`MANAGE_ALL_GENERATIONS`) sees every board on the team regardless of level. See
[Identity](/docs/api/me) for reading your own permissions.

This listing reads one account at a time: omit `team_id` for your own boards, or
name a single team.

## 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). |

**Query parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `team_id` | string | No | The team whose boards to read, one per request. Omit it to read your personal account. Part of the account selection rather than a filter, so it behaves identically on every endpoint. |
| `limit` | integer | No | Boards per page, 1 to 100. Defaults to 24. |
| `cursor` | string | No | The next_cursor from a previous response. Keep every other parameter identical between pages: the cursor is a position within one filtered listing, so changing a filter mid-walk returns 400 INVALID_CURSOR rather than a silently different page. |
| `access_level` | string | No | Return only boards at this access level. One of: `PRIVATE`, `SHARED`, `RESTRICTED` |
| `owner_user_id` | string | No | Return only boards created by one user. Useful on a team; on a personal account every board is already yours. |
| `start_utc` | string (ISO 8601) | No | Start of the window, inclusive, in UTC. A board created at exactly this instant is included. |
| `end_utc` | string (ISO 8601) | No | End of the window, exclusive, in UTC. A board created at exactly this instant is NOT included. |

cURL:

```bash
curl -G https://api2.rundiffusion.com/api/v2/boards \
  -H "Authorization: Bearer $RUNDIFFUSION_TOKEN" \
  --data-urlencode "limit=24"
```

JavaScript:

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

for (const board of page.data) {
  console.log(board.id, board.title, board.access_level);
}
```

Python:

```python
page = requests.get(
    f"{BASE}/boards",
    headers=HEADERS,
    params={"limit": 24},
).json()

for board in page["data"]:
    print(board["id"], board["title"], board["access_level"])
```

## Response

`200 OK` with one page of boards.

```json
{
  "data": [
    {
      "id": "Tq8vNc…",
      "created_at": "2026-07-30T14:02:11+00:00",
      "title": "Hero shots",
      "description": "Landing page art",
      "access_level": "SHARED",
      "owner_user_id": "k3PqV9…",
      "team_id": "Kp7mZq…",
      "avatar_url": "https://rundiffusion.com/..."
    }
  ],
  "next_cursor": "Tq8vNc…",
  "has_more": true
}
```

### Top level

| Name | Type | Description |
| --- | --- | --- |
| `data` | array | One page of boards, newest first. See [Board object](#board-object) below. |
| `next_cursor` | string \| null | Pass as cursor for the next page. Null on the last page. |
| `has_more` | boolean | Whether another page exists. Prefer this over checking whether data is short. |

### Board object

| Name | Type | Description |
| --- | --- | --- |
| `id` | string | Board ID. Send it as board_id on the board endpoints, and as board_id on [Library](/docs/api/library/list) to filter that board's generations. Example: Tq8vNc…. |
| `created` | string (ISO 8601) | When it was created, in UTC. |
| `title` | string \| null | Board name. Example: Hero shots. |
| `description` | string \| null | Optional description, null when unset. |
| `access_level` | string | Who can reach the board. Always PRIVATE on a personal account, where there is nobody to share with. One of: `PRIVATE`, `SHARED`, `RESTRICTED` |
| `owner_user_id` | string | The user who created the board. |
| `team_id` | string \| null | The team the board belongs to, or null for a personal board. |
| `avatar_url` | string \| null | The board's picture, as a png URL you can render directly. Assigned automatically when the board is created, so it is normally set; null only where a board was created without one. |

This listing carries the board itself, not its contents. For the nodes on a
board, and its members, fetch one board with
[Get a board](/docs/api/boards/get).

## Errors

**Status codes**

| Name | Type | Description |
| --- | --- | --- |
| `400` | INVALID_REQUEST | A parameter failed validation, such as a limit outside 1 to 100 or a malformed datetime. |
| `400` | INVALID_CURSOR | The cursor does not belong to this listing. It usually means a filter changed between pages, or the board it pointed at is gone. Restart without a cursor. |
| `401` | TOKEN_INVALID | Missing or invalid credential. |
| `403` | ACCOUNT_HEADER_INVALID | The selected account is not one this credential may act on. |
| `429` | RATE_LIMITED | Too many requests. See [Rate limits](/docs/api/rate-limits#boards). |
