> 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/developer/recipes/upload-train-and-deploy.md).

# Upload, Train, and Deploy a Model

This recipe takes a folder of labeled images and ends with a hosted Roboflow model you can call from your application. Each step is short - the whole flow is \~30 lines of Python.

## What you'll need

* A Roboflow workspace and API key - see [Find Your Roboflow API Key](/developer/authentication/find-your-roboflow-api-key.md).
* A folder of images plus matching annotations (COCO / VOC / YOLO).
* `Python >=3.10` and `pip install roboflow`.
* Time and credits to train (typically minutes for small datasets).

```bash
export ROBOFLOW_API_KEY=rf_xxxxx
```

## 1. Create the project

```python
import roboflow

rf = roboflow.Roboflow()
ws = rf.workspace()

project = ws.create_project(
    project_name="My Detector",
    project_type="object-detection",
    project_license="MIT",            # or "Private" on paid plans
    annotation="objects",              # what you're labeling
)
print(project.id)
```

If the project already exists, fetch it instead:

```python
project = ws.project("my-detector")
```

## 2. Upload the dataset

```python
ws.upload_dataset(
    "./my-images",                     # path to your dataset
    "my-detector",                     # project id
    num_workers=10,                    # parallel uploads
    project_type="object-detection",
)
```

The expected layout for a COCO dataset:

```
my-images/
├── train/
│   ├── image1.jpg
│   ├── image2.jpg
│   └── _annotations.coco.json
├── valid/
│   └── _annotations.coco.json
└── test/
    └── _annotations.coco.json
```

For VOC and YOLO, drop the corresponding annotation files alongside the images.

## 3. Generate a version

A version freezes a snapshot of the dataset with preprocessing and augmentation applied. The web app has a UI for this; from the SDK:

```python
new_version = project.generate_version({
    "preprocessing": {
        "auto-orient": True,
        "resize": {"width": 640, "height": 640, "format": "Stretch to"},
    },
    "augmentation": {},
})
version = project.version(new_version)
```

## 4. Train

```python
model = version.train(
    model_type="rfdetr-nano",          # see version.train help for full list
    epochs=100,
)
```

`train()` schedules the job server-side and returns once the queue accepts it. Check progress in the web app or via [`GET /:workspace/:project/jobs/:jobId`](/developer/rest-api/train-a-model.md).

## 5. Run inference

Once training completes, call the hosted model:

```python
predictions = version.model.predict("test-image.jpg", confidence=40, overlap=30).json()
for p in predictions["predictions"]:
    print(p["class"], p["confidence"])
```

That's the same hosted Serverless v2 endpoint the [REST API](/developer/rest-api/run-a-model-on-an-image.md) and [CLI `infer`](/developer/command-line-interface/run-a-model-on-an-image.md) hit.

## 6. (Optional) Move to a Dedicated Deployment

For high throughput or pinned latency, provision a [Dedicated Deployment](/developer/python-sdk/manage-dedicated-deployments.md) and point inference at its `public_url`:

```python
from roboflow.adapters import deploymentapi
from inference_sdk import InferenceHTTPClient

deploymentapi.add_deployment(
    api_key=ROBOFLOW_API_KEY,
    creator_email="me@company.com",
    deployment_name="my-deployment",
    machine_type="gpu-small",
    duration=8,
    delete_on_expiration=False,
    inference_version=None,
)

# poll deploymentapi.get_deployment until status == "ready", then:
status, info = deploymentapi.get_deployment(ROBOFLOW_API_KEY, "my-deployment")

client = InferenceHTTPClient(api_url=info["public_url"], api_key=ROBOFLOW_API_KEY)
print(client.infer("test-image.jpg", model_id="my-detector/3"))
```

## Variations

* **CI-driven retraining**: replace step 1's `create_project` with `project = ws.project(...)`, automate steps 2–4 in a GitHub Action that runs whenever new labeled images land in your data store. The CLI flavor (`roboflow upload`, `roboflow version create`, `roboflow train start`) makes the YAML cleaner.
* **Active-learning loop**: skip step 5 and use [`Workspace.active_learning()`](/developer/python-sdk/active-learning.md) to triage new images against the trained model and feed back the uncertain ones.
* **Custom weights**: skip steps 3–4 and use [`Version.deploy()`](/developer/python-sdk/upload-custom-model-weights.md) to upload an externally trained model into the version.
