---
title: Folders
description: Inspect a project's virtual folder tree (read-only) — list folders, fetch the nested tree, and get per-folder image stats.
section: API Reference
order: 20
---
Pictograph organizes a project's images into **virtual folders**. The folders are
virtual: GCS object paths never move, so a folder is just a `virtual_folder_path`
label on the image rows. The developer API exposes the read side (list, tree, stats);
folder mutations (create, rename, delete) stay in the web app for now. A cross-org
project id returns a 404, consistent with the other developer resources.

```python
from pictograph import Client
client = Client()  # reads PICTOGRAPH_API_KEY
```

## list

List a project's folders. Pass `parent_path` to return only the direct children of a
folder (use `""` for the root-level folders); omit it to return every folder in the
project.

| Arg | Type | Default | Notes |
|---|---|---|---|
| `project_id` | `str` | required | The project (dataset) UUID |
| `parent_path` | `str \| None` | `None` | `None` returns all folders; `""` returns root-level only; a path returns that folder's direct children |

```python
all_folders = client.folders.list("project-uuid")
top_level = client.folders.list("project-uuid", parent_path="")
for f in all_folders:
    print(f.full_path, f.image_count)
```

```bash
# All folders in the project:
curl -s "https://api.pictograph.io/api/v1/developer/folders/list/project-uuid" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY"

# Direct children of the root only:
curl -s "https://api.pictograph.io/api/v1/developer/folders/list/project-uuid?parent_path=" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY"
```

Returns `list[Folder]`.

## tree

Fetch the project's folder hierarchy as a nested tree (each node carries its
children). Handy for rendering a folder sidebar in one request rather than walking
`list` level by level.

```python
tree = client.folders.tree("project-uuid")
for node in tree:
    print(node.name, len(node.children))
```

```bash
curl -s "https://api.pictograph.io/api/v1/developer/folders/tree/project-uuid" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY"
```

Returns `list[FolderTreeNode]` (the root-level nodes, with `children` nested).

## stats

Image statistics for a single folder, by default including its subfolders.

| Arg | Type | Default | Notes |
|---|---|---|---|
| `folder_id` | `str` | required | The folder UUID |
| `include_subfolders` | `bool` | `True` | When `True`, roll up the folder's subfolders too |

```python
stats = client.folders.stats("folder-uuid", include_subfolders=True)
print(stats.total_images, stats.total_folders, stats.total_size_bytes)
```

```bash
curl -s "https://api.pictograph.io/api/v1/developer/folders/folder-uuid/stats?include_subfolders=true" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY"
```

Returns `FolderStats`.

## create

Create a virtual folder (idempotent — creating an existing path returns it).
Parents are auto-created, the same way an upload into a `folder_path` does —
use this to pre-stage an empty folder structure ahead of uploads. Requires
`member`+ role.

```python
folder = client.folders.create(dataset.id, "/train/positive")
print(folder.id, folder.full_path)
```

```bash
curl -s -X POST "https://api.pictograph.io/api/v1/developer/folders/" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY" -H "Content-Type: application/json" \
  -d '{"dataset_id": "<dataset-uuid>", "folder_path": "/train/positive"}'

pictograph folders create <dataset-uuid> /train/positive
```

Returns the folder in the canonical shape: `{"data": {id, dataset_id, name,
folder_path, parent_folder_id, image_count, created_at}}`.

## rename

Rename a folder — the folder row, every descendant folder's path, and every
contained image's folder path move together in one call (storage paths are
immutable; virtual folders are metadata, so no bytes move). A sibling folder
with the target name returns `409 ConflictError`. Requires `member`+ role.

```python
folder = client.folders.rename(folder.id, "negatives")
```

```bash
curl -s -X PATCH "https://api.pictograph.io/api/v1/developer/folders/<folder-uuid>/rename" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY" -H "Content-Type: application/json" \
  -d '{"new_name": "negatives"}'

pictograph folders rename <folder-uuid> negatives
```

## delete

Delete a folder (`cascade=true` moves its images to the parent folder first;
empty-only otherwise). Requires `member`+ role.

```python
client.folders.delete(folder.id, cascade=True)
```

## Common errors

| Status | Exception | Cause |
|---|---|---|
| 404 | `NotFoundError` | Project or folder id does not exist in your org |
| 403 | `ForbiddenError` | No access to the organization, or mutations without `member`+ role |
| 409 | `ConflictError` | `rename` target name already exists as a sibling folder |