Ultralytics Platform
REST API at https://platform.ultralytics.com, auth Authorization: Bearer ul_....
Interactive spec: GET /openapi.json (needs the bearer header).
The auth shape and the four gotchas below were run against the live API on
2026-08-04. Endpoint shapes not covered by those runs come from /openapi.json and are marked
unverified in references/.
Get the key first
export ULTRALYTICS_API_KEY=ul_... # Settings > API Keys on platform.ultralytics.com
The ultralytics package reads ULTRALYTICS_API_KEY env first, then api_key in
settings.json (yolo settings api_key=ul_...). Check both before assuming the key is missing.
Pick the path before writing any code
| Goal | Path |
|---|---|
| Track a run that has not started yet | Package callback, no REST |
| Push a run that already finished | REST retro upload |
| Read a platform dataset or model in training code | ul:// URI |
| Upload/download datasets, search, cloud train, export, deploy | REST |
Package callback (live runs)
Set the key and pass project=, then train normally. ultralytics/utils/callbacks/platform.py
streams per-epoch metrics, system metrics, console output, PR/F1/confusion-matrix plots, and
uploads best.pt at the end.
model.train(data="coco8.yaml", epochs=100, project="my-project", name="run1")
project= is mandatory. The callback returns early without it, so no key and no project=
both produce the same silent nothing. That is the #1 cause of "I trained but the platform is
empty". See references/recipes.md for the full enablement gate and the other failure modes.
ul:// URIs
Resolve to signed URLs inside data= and model=, no manual download:
YOLO("ul://username/my-project/my-model").train(data="ul://username/datasets/my-dataset", epochs=100)
REST
Read references/recipes.md before writing a request. It carries working code for retro upload
of a finished run, dataset upload and download, search, cloud training, export, deployment, and
cleanup. Each section states whether it was run live or derived from the spec.
references/api-reference.md is the endpoint table plus the request shapes.
Four things that break first-time requests
POST /api/modelsandGET /api/modelsneed the 24-char project ID, not the slug. Every other endpoint accepts a slug in the path. These two return400 Invalid project IDfor a slug even though the parameter is documented as "name or ID". Create the project first and keep the returnedprojectId.- Top-level
metricsis a closed key set, listed inreferences/api-reference.md. Posting a rawresults.csvkey likemetrics/mAP50-95(B)returns400 Unrecognized key.trainResults[].metricsis free-form, so csv rows go in there as is. - Uploads are three calls, not one.
POST /api/upload/signed-urlto get the URL, plainPUTof the bytes to that URL with no auth header, thenPOST /api/upload/complete. Skipping the third call leaves the file unattached. - Deletes are soft.
DELETE /api/projects/{id}moves to trash and cascades to its models.DELETE /api/trash/emptymakes it permanent.
Cost and destructive actions
POST /api/training/start, POST /api/exports, and POST /api/deployments spend credits.
The cost comes back in the create response, not from a lookup beforehand, so report
billing.estimatedCostDisplay from the POST result before letting the job run.
Confirm with the user before any create-with-cost or any delete.