> For the complete documentation index, see [llms.txt](https://docs.roboflow.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.roboflow.com/datasets/create-and-upload/upload-a-dataset.md).

# Upload a Dataset

## Python SDK

`Workspace.upload_dataset()` uploads a structured dataset (images + matching annotations) to a Roboflow project. The project will be created if it doesn't exist; otherwise the new images get added to the existing project.

```python
import roboflow

rf = roboflow.Roboflow(api_key="YOUR_API_KEY")
workspace = rf.workspace()

workspace.upload_dataset(
    "./dataset/",                  # path to a structured dataset directory
    "my-detector",                 # project id (created if it doesn't exist)
    num_workers=10,
    project_license="MIT",
    project_type="object-detection",
    batch_name=None,
    num_retries=0,
    is_prediction=False,           # True for model-generated annotations awaiting review
)
```

### Parameters

* `dataset_path` (str) - path to the dataset root.
* `project_name` (str) - destination project's id. Created if it doesn't exist.
* `num_workers` (int, default `10`) - concurrent uploads. We recommend not exceeding 25.
* `project_license` (str, default `"MIT"`) - license for a newly-created project. Set to `"Private"` for private projects (paid plans only).
* `project_type` (str, default `"object-detection"`) - type for a newly-created project. Ignored if the project already exists.
* `batch_name` (str, optional) - group these uploads under a named batch. Useful for tracking the source of a labeling round.
* `num_retries` (int, default `0`) - retry transient upload failures.
* `is_prediction` (bool, default `False`) - set to `True` to upload annotations as model predictions awaiting review rather than ground truth.

### Expected directory layout

For a COCO dataset:

```
my_dataset/
├── train/
│   ├── image1.jpg
│   └── _annotations.coco.json
├── valid/
│   ├── image2.jpg
│   └── _annotations.coco.json
└── test/
    ├── image3.jpg
    └── _annotations.coco.json
```

For VOC, drop matching `.xml` files alongside each image. For YOLO, drop matching `.txt` files plus a `data.yaml` describing the class list.

### Note on SHA-256 dedup (v1.3.6+)

As of `roboflow` 1.3.6, the SDK uploads original image bytes rather than re-encoding via Pillow. This brings parity with the web uploader and lets the Roboflow server deduplicate uploads by SHA-256 - re-uploading the same image (e.g. into a different batch) succeeds without consuming additional storage credits.

### REST and CLI equivalents

* REST: see [Upload an Image (REST)](/datasets/manage/manage-images.md#upload-an-image) for the per-image endpoint that `upload_dataset` calls under the hood.
* CLI: see [Upload a Dataset (CLI)](#cli).

## CLI

You can upload datasets with images and/or annotations using the Roboflow CLI.

We have prepared a video that walks through how to upload a dataset:

{% embed url="<https://www.loom.com/share/19637984033a466b831af56f9404fa89>" %}

### Upload a Directory

Use `roboflow image upload` with a directory path to bulk-upload a dataset with parallel per-image uploads:

```bash
roboflow image upload /path/to/dataset/folder -p PROJECT_ID
```

Or use the shorthand alias:

```bash
roboflow upload /path/to/dataset/folder -p PROJECT_ID
```

The CLI auto-detects whether the path is a file, directory, or `.zip` file. A directory triggers a bulk import with parallel per-image uploads unless you use `--zip-upload`.

#### Options

| Flag                  | Description                               |
| --------------------- | ----------------------------------------- |
| `-p`, `--project`     | Project ID (required)                     |
| `-c`, `--concurrency` | Number of parallel uploads (default: 10)  |
| `-b`, `--batch`       | Batch name for grouping uploads           |
| `-r`, `--retries`     | Retry failed uploads N times (default: 0) |
| `-s`, `--split`       | Override split for all uploaded images    |

Example with options:

```bash
roboflow upload ./my-dataset -p my-project -c 20 -b "april-batch" -r 3
```

### Upload a Zip File

Use zip uploads for larger datasets or when your dataset is already packaged as a `.zip` file. Zip uploads use Roboflow's asynchronous zip upload flow. By default, the CLI uploads the zip file and waits for processing to finish.

To upload an existing zip file:

```bash
roboflow image upload /path/to/dataset.zip -p PROJECT_ID
```

To zip a local directory client-side and upload it with the async zip flow, use `--zip-upload` with the primary command:

```bash
roboflow image upload /path/to/dataset/folder -p PROJECT_ID --zip-upload
```

#### Options for Zip Uploads

| Flag              | Description                                                                              |
| ----------------- | ---------------------------------------------------------------------------------------- |
| `-p`, `--project` | Project ID (required)                                                                    |
| `--zip-upload`    | Zip a directory client-side and upload it with the async zip upload flow                 |
| `--no-wait`       | Return immediately after the zip is uploaded instead of waiting for processing to finish |
| `-b`, `--batch`   | Batch name for grouping uploads                                                          |
| `-s`, `--split`   | Split set: train, valid, or test                                                         |
| `-t`, `--tag`     | Comma-separated tag names                                                                |

Example with zip upload options:

```bash
roboflow image upload ./my-dataset.zip -p my-project -s train -t "outdoor,daytime" -b "april-batch"
```

To start processing and return a task ID immediately:

```bash
roboflow image upload ./my-dataset.zip -p my-project --no-wait --json
```

The JSON response includes the asynchronous task ID:

```json
{
  "status": "pending",
  "task_id": "task-123",
  "path": "./my-dataset.zip",
  "project": "my-project",
  "result": {
    "task_id": "task-123",
    "status": "pending"
  }
}
```

Zip uploads do not support `--is-prediction`. Use the regular per-image upload flow for prediction uploads.

### Upload a Single Image

To upload a single image:

```bash
roboflow image upload photo.jpg -p PROJECT_ID
```

#### Options for Single Image Upload

| Flag                 | Description                                       |
| -------------------- | ------------------------------------------------- |
| `-p`, `--project`    | Project ID (required)                             |
| `-a`, `--annotation` | Path to annotation file                           |
| `-m`, `--labelmap`   | Path to labelmap file                             |
| `-s`, `--split`      | Split set: train, valid, or test (default: train) |
| `-t`, `--tag`        | Comma-separated tag names                         |
| `-M`, `--metadata`   | JSON string of metadata                           |
| `--is-prediction`    | Mark upload as a prediction                       |
| `-b`, `--batch`      | Batch name                                        |

#### Examples

Upload with an annotation:

```bash
roboflow upload photo.jpg -p my-project -a annotation.xml -s valid
```

Upload with tags and metadata:

```bash
roboflow upload photo.jpg -p my-project -t "outdoor,daytime" -M '{"camera_id":"cam001"}'
```

### Supported Project Types

You can upload data for the following project types:

* Object Detection
* Single-Label Classification
* Multi-Label Classification
* Instance Segmentation
* Semantic Segmentation
* Keypoint Detection

### Supported data.yaml Formats

The CLI supports both list-style and key-value pair formatted class name mappings in `data.yaml` files during dataset uploads:

```yaml
nc: 3
names: ['Paper', 'Rock', 'Scissors']
```

OR

```yaml
nc: 3
names:
  0: Paper
  1: Rock
  2: Scissors
```

### JSON Output

For automation, use `--json`:

```bash
roboflow upload photo.jpg -p my-project --json
```

```json
{"status": "uploaded", "path": "photo.jpg", "project": "my-project"}
```

## Next steps

* Label any unannotated images so they can be used for training. See [Introduction to Roboflow Annotate](/datasets/annotate/annotate/annotation-tools.md).
* Turn your images into a trainable snapshot. See [Create a Dataset Version](/datasets/versions/dataset-versions/create-a-dataset-version.md).
