YOLO Vision 2026:

REST API Reference#

Ultralytics Platform provides a REST API for programmatic access to datasets, images, projects, models, training, exports, and deployments.

Ultralytics Platform Interactive API Documentation

Quick Start
# List the datasets owned by a workspace
curl -H "Authorization: Bearer YOUR_API_KEY" \
  https://platform.ultralytics.com/api/datasets/YOUR_USERNAME

Every endpoint below lists its client.<resource>.<method>(...) call from the ultralytics-platform SDK, which is generated from the same contract as this reference.

Interactive API Reference

This page is a guided tour of the API. The generated, always-current reference lives at platform.ultralytics.com/api/docs, and the machine-readable OpenAPI 3.2 document that powers it is published at platform.ultralytics.com/openapi.json. Both are generated directly from the server-side contract, so they are the authority whenever this page and the schema disagree.

API Overview#

The API is organized around the core Platform resources:

graph LR
    A[API Key]:::start --> B[Datasets]:::proc
    A --> C[Projects]:::proc
    B -->|images| G[Images]:::proc
    C -->|contains| D[Models]:::proc
    B -->|train on| D
    D -->|deploy| E[Deployments]:::proc
    D -->|export| F[Exports]:::proc

    classDef start fill:#4CAF50,color:#fff
    classDef proc fill:#2196F3,color:#fff
ResourceDescriptionKey Operations
DatasetsLabeled image collectionsCRUD, ingest, versions, classes, splits, clone
ImagesIndividual images and labelsRead, annotate, move split, delete, auto-annotate
ProjectsModel workspacesCRUD, clone
ModelsTrained checkpointsCRUD, predict, download, clone, training status
TrainingCloud GPU training jobsGPU availability, start, progress, cancel
ExportsFormat conversion jobsCreate, list, status, cancel
DeploymentsDedicated inference endpointsCreate, start/stop/replace, predict, metrics, logs
TrashSoft-deleted resourcesList, restore, permanently delete
StorageCloud storage integrationsConnect, discover, browse, disconnect
AccountPlan, credits, storage, profileAccount summary, API keys, storage usage, user lookup
BillingPlan usage and ledgerUsage summary, transactions
ExplorePublic content searchSearch projects and datasets

Authentication#

Most endpoints require an API key. Endpoints that expose public content — reading a public dataset, project, or model, listing public dataset images, running inference on a public model, or searching Explore — also accept anonymous requests and simply return more when a key is supplied.

Get an API Key#

  1. Go to Settings > API Keys
  2. Click Create Key
  3. Copy the generated key

See API Keys for detailed instructions.

Authorization Header#

Include your API key as a bearer token:

Authorization: Bearer YOUR_API_KEY
API Key Format

API keys are the literal prefix ul_ followed by 40 hexadecimal characters, 43 characters in total (for example ul_a1b2c3d4e5f6789012345678901234567890abcd). Requests with a missing header, a malformed key, or a revoked key return 401. Keep your key secret -- never commit it to version control or share it publicly.

Example#

curl -H "Authorization: Bearer YOUR_API_KEY" \
  https://platform.ultralytics.com/api/account/summary

Base URL#

All API endpoints use:

https://platform.ultralytics.com/api

Resource Paths#

Resources are addressed by the same human-readable names that appear in Platform URLs, not by database IDs:

ResourcePathExample
Dataset/api/datasets/{owner}/{dataset}/api/datasets/acme-vision/warehouse
Project/api/projects/{owner}/{project}/api/projects/acme-vision/inspection
Model/api/models/{owner}/{project}/{model}/api/models/acme-vision/inspection/v3
Deployment/api/deployments/{owner}/{deployment}/api/deployments/acme-vision/edge-1
Image/api/images/{imageId}/api/images/65f1c0a2b3d4e5f601234567
  • {owner} is a personal username or a team workspace handle: 4-32 characters, lowercase alphanumeric with single hyphens between segments.
  • {dataset}, {project}, {model}, and {deployment} follow the same lowercase-hyphenated pattern, up to 128 characters.
  • {imageId} and {exportId} are 24-character hexadecimal IDs returned by the API.
  • Renaming a resource through PATCH changes the display name and the URL name together, and the response returns the current URL name so you can keep following it.
Workspace Selection

There is no owner query parameter. Workspace-scoped paths carry the owner in the path, and account-scoped endpoints (/api/account/summary, /api/api-keys, /api/storage, /api/billing/*, /api/trash, /api/integrations/buckets) operate on the workspace that issued the API key. To act on a team workspace, use an API key created in that workspace.

Rate Limits#

The API enforces sliding-window limits per API key. Each route falls into one category, and each category has an independent counter, so 20 predict requests do not consume your default allowance.

CategoryLimitApplies To
Default100 requests/minEvery route not listed below
Training10 requests/minPOST /api/training/start
Upload10 requests/minSigned upload URLs, upload completion, and dataset ingest
Predict20 requests/minModel and deployment inference through Platform API routes
Export20 requests/minModel export routes and dataset export/version routes
Download30 requests/minModel file downloads
Mutation10 requests/minListing API keys, connecting or discovering cloud storage, and deployment PATCH actions
Hydrate20 requests/minPOST /api/datasets/{owner}/{dataset}/images (fetching a selected set of images)
Clustering10 requests/minGET /api/datasets/{owner}/{dataset}/images/clustering

Browser-only Platform routes, such as billing checkout and team management, have their own limits that do not apply to API-key traffic.

When throttled, the API returns 429 with both headers and a JSON body:

Retry-After: 12
X-RateLimit-Reset: 2026-02-21T12:34:56.000Z
{
    "error": "Rate limit exceeded",
    "retryAfter": 12,
    "resetAt": "2026-02-21T12:34:56.000Z"
}

Dedicated Endpoints (Unlimited)#

Dedicated endpoints are not subject to Platform API-key rate limits when you call the deployment's own serviceUrl directly (for example, https://predict-abc123.run.app/predict). Throughput then depends on the deployed service configuration.

Handling Rate Limits

When you receive a 429, wait for Retry-After seconds (or until X-RateLimit-Reset) before retrying. See the rate limit FAQ for an exponential backoff implementation.

Response Format#

Success Responses#

Responses are JSON objects with resource-specific fields. There is no generic envelope: list endpoints return a named collection alongside counts, and mutations return the changed identifiers.

{
    "datasets": [{ "id": "65f1c0a2b3d4e5f601234567", "owner": "acme-vision", "dataset": "warehouse" }],
    "total": 1,
    "region": "us"
}

Data-bearing responses also include region (us, eu, or ap), the storage region for that workspace.

Error Responses#

Every error response is a JSON object with an error message:

{
    "error": "Dataset not found"
}
HTTP StatusMeaning
200Success
201Created
202Accepted, work continues asynchronously
400Invalid path, query, or request body
401Missing or invalid authentication
402Insufficient credits (training)
403Insufficient permissions, plan, or quota
404Resource not found
409Conflict with current state (duplicate name, job in flight)
413Prediction input too large
422Model classes do not match the dataset (auto-annotation)
429Rate limit exceeded
500Server error
502Upstream provider or service call failed
503Dependent service temporarily unavailable

Pagination#

Pagination style depends on the collection:

StyleEndpointsParameters
Limit onlyDatasets, projects, models, exports, deployments listslimit
Offset and limitDataset images, image clustering, Explore searchoffset, limit, plus hasMore in the response
CursorDataset images (large datasets)cursor, includeTotal, plus nextCursor
Page numberTrashpage, limit, plus totalPages
Opaque page tokenDeployment logspageToken, plus nextPageToken

Datasets API#

Create, browse, and manage labeled image datasets for training YOLO models. See Datasets documentation.

List Datasets#

GET /api/datasets/{owner}

Python SDK: client.datasets.list(owner)

Returns the owner's public datasets, plus private datasets when your key can view that workspace.

Query Parameters:

ParameterTypeDescription
limitintMaximum datasets to return (default: 1000, max: 1000)
includeSamplesbooleanInclude sample image previews (default: true)
includeImageUrlsbooleanInclude full-size sample image fallback URLs (default: false)
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://platform.ultralytics.com/api/datasets/acme-vision?limit=10&includeSamples=false"

Response:

{
    "datasets": [
        {
            "id": "65f1c0a2b3d4e5f601234567",
            "owner": "acme-vision",
            "dataset": "warehouse",
            "name": "Warehouse",
            "task": "detect",
            "visibility": "private",
            "imageCount": 1000,
            "classCount": 2,
            "classNames": ["person", "forklift"],
            "splits": { "train": 800, "val": 200, "test": 0, "labeled": 1000 },
            "annotationCount": 5400,
            "starCount": 3,
            "isStarred": false,
            "status": "ready",
            "createdAt": "2026-01-15T10:00:00Z",
            "updatedAt": "2026-01-16T08:30:00Z"
        }
    ],
    "total": 1,
    "region": "us"
}

Get Dataset#

GET /api/datasets/{owner}/{dataset}

Python SDK: client.datasets.retrieve(owner, dataset)

Returns the full dataset object under a dataset key, including classNames, splits, versions, source, and the user-defined metadata object.

Create Dataset#

POST /api/datasets

Python SDK: client.datasets.create(dataset=..., name=...)

Body:

{
    "dataset": "warehouse",
    "name": "Warehouse",
    "task": "detect",
    "description": "Forklift and pedestrian safety dataset",
    "classNames": ["person", "forklift"],
    "visibility": "private",
    "metadata": { "location": "factory-1", "reviewed": true },
    "owner": "acme-vision"
}
FieldTypeRequiredDescription
datasetstringYesDataset name used in Platform URLs (lowercase, hyphenated, max 128 chars)
namestringYesDisplay name (max 100 chars)
descriptionstringNoDescription (max 1000 chars)
taskstringNoTask type (default: detect)
classNamesarrayNoClass names in index order (max 25,000)
formatstringNoAnnotation format: yolo (default), coco, raw, ndjson
visibilitystringNopublic or private
tagsarrayNoUp to 50 tags of 50 characters each
licensestringNoDataset license identifier
metadataobjectNoCustom JSON metadata
ownerstringNoTeam workspace handle; defaults to your personal workspace
Supported Tasks

Valid task values when creating or updating a dataset: detect, segment, semantic, depth, classify, pose, and obb. Depth datasets have no classes.

Response (201):

{
    "id": "65f1c0a2b3d4e5f601234567",
    "owner": "acme-vision",
    "dataset": "warehouse",
    "region": "us"
}

Update Dataset#

PATCH /api/datasets/{owner}/{dataset}

Python SDK: client.datasets.update(owner, dataset)

Body (partial update):

{
    "name": "Warehouse Safety",
    "description": "New description",
    "visibility": "public",
    "metadata": { "location": "factory-2", "reviewed": true }
}

Accepted fields: name, description, visibility, metadata, tags, classNames, classColors, format, task, license, iconColor, iconLetter, and starred. Send an empty metadata object ({}) to clear custom metadata. Metadata keys are limited to 128 characters and the serialized object to 500,000 characters.

Response:

{
    "success": true,
    "dataset": "warehouse-safety"
}

Renaming changes the URL name, so use the returned dataset value for subsequent requests.

Delete Dataset#

DELETE /api/datasets/{owner}/{dataset}

Python SDK: client.datasets.delete(owner, dataset)

Moves the dataset to trash, where it is recoverable for 30 days.

Clone Dataset#

POST /api/datasets/{owner}/{dataset}/clone

Python SDK: client.datasets.clone(owner, dataset)

Copies an accessible dataset, with its images and labels, into your personal workspace or a team workspace.

Optional body (all fields optional):

{
    "dataset": "warehouse-copy",
    "name": "Warehouse Copy",
    "description": "Cloned for experimentation",
    "visibility": "private",
    "license": "CC-BY-4.0",
    "owner": "acme-vision"
}

Response (201): id, owner, dataset, name, imageCount, classCount, and region. Datasets backed by a connected storage source return 409 because their files are not copied.

Download a Dataset Export#

GET /api/datasets/{owner}/{dataset}/export

Python SDK: client.datasets.export(owner, dataset)

Returns a signed NDJSON download URL. Omit v to export the dataset's current state, reusing the cached export when nothing changed since it was generated.

Query Parameters:

ParameterTypeDescription
vintegerSaved version number (1-indexed). Omit for the current dataset.

Response:

{
    "downloadUrl": "https://storage.googleapis.com/...&signature=...",
    "cached": true
}

Requesting a specific version returns downloadUrl and version instead of cached.

Create Dataset Version#

POST /api/datasets/{owner}/{dataset}/export

Python SDK: client.datasets.create_export(owner, dataset)

Creates an immutable numbered snapshot of the dataset and stores its NDJSON export. Requires editor access.

Body (optional):

{
    "description": "Added 500 training images"
}

Response:

{
    "version": 3,
    "downloadUrl": "https://storage.googleapis.com/...&signature=...",
    "reused": false
}

reused is true when the dataset is unchanged since the previous version and that snapshot was returned instead.

Update Version Description#

PATCH /api/datasets/{owner}/{dataset}/export

Python SDK: client.datasets.update_export(owner, dataset, version=..., description=...)

Body:

{
    "version": 2,
    "description": "Fixed mislabeled classes"
}

Response: {"ok": true}

Restore Dataset Version#

POST /api/datasets/{owner}/{dataset}/restore

Python SDK: client.datasets.restore(owner, dataset, version=...)

Rebuilds images, annotations, and classes from a saved version without copying image bytes.

Body:

{
    "version": 2
}

Response: {"version": 2, "imageCount": 1000}

Get Dataset Statistics#

GET /api/datasets/{owner}/{dataset}/class-stats

Python SDK: client.datasets.class_stats(owner, dataset)

Returns per-class annotation counts, image and annotation histograms, and heatmaps. Large datasets are sampled, in which case sampleSize reports how many images contributed.

Response (abbreviated):

{
    "classes": [{ "classId": 0, "count": 1500, "imageCount": 450 }],
    "imageStats": {
        "widthHistogram": [{ "bin": 640, "count": 120, "size": 1 }],
        "heightHistogram": [{ "bin": 480, "count": 95, "size": 1 }],
        "pointsHistogram": [{ "bin": 4, "count": 200, "size": 1 }],
        "formatDistribution": { "jpg": 900, "png": 100 },
        "fileSizeHistogram": [{ "bin": 250000, "count": 300, "size": 50000 }],
        "objectsPerImageHistogram": [{ "bin": 5, "count": 210, "size": 1 }],
        "bboxWidthHistogram": [{ "bin": 120, "count": 340, "size": 20 }],
        "bboxHeightHistogram": [{ "bin": 90, "count": 300, "size": 20 }]
    },
    "locationHeatmap": {
        "bins": [
            [5, 10],
            [8, 3]
        ],
        "maxCount": 50
    },
    "dimensionHeatmap": {
        "bins": [
            [2, 5],
            [3, 1]
        ],
        "maxCount": 12,
        "minWidth": 10,
        "maxWidth": 1920,
        "minHeight": 10,
        "maxHeight": 1080
    },
    "classNames": ["person", "forklift"],
    "cached": true,
    "sampleSize": null
}

Manage Classes#

Merge classes (reassign annotations to a target class, then remove the sources):

POST /api/datasets/{owner}/{dataset}/classes/merge

Python SDK: client.datasets.merge_classes(owner, dataset, source_class_ids=..., target_class_id=...)

{
    "sourceClassIds": [2, 4],
    "targetClassId": 1
}

Delete classes (their annotations are deleted and the remaining class IDs shift down):

POST /api/datasets/{owner}/{dataset}/classes/delete

Python SDK: client.datasets.delete_classes(owner, dataset, class_ids=...)

{
    "classIds": [2, 4]
}

Both operations return success, the updated classNames and classColors, and a summary of what changed (mergedClassIds and targetClassId, or deletedClassIds and deletedAnnotations).

Class IDs Are Positional

Because remaining IDs shift after a merge or delete, these operations are not idempotent. Re-fetch the dataset to get current class indices before issuing another class operation.

Redistribute Splits#

POST /api/datasets/{owner}/{dataset}/splits/redistribute

Python SDK: client.datasets.redistribute_splits(owner, dataset, train=..., val=..., test=...)

Randomly reassigns images across splits. The three percentages must total 100.

{
    "train": 80,
    "val": 20,
    "test": 0
}

Response: success, the resulting splits counts, and modified (number of images moved).

Dataset Embeddings#

GET /api/datasets/{owner}/{dataset}/embeddings
POST /api/datasets/{owner}/{dataset}/embeddings
DELETE /api/datasets/{owner}/{dataset}/embeddings

Python SDK: client.datasets.embeddings(owner, dataset), client.datasets.create_embeddings(owner, dataset), client.datasets.delete_embeddings(owner, dataset)

GET returns the analysis summary (analyzedAt, embeddingsCount, latestImageAt, activeJob). POST queues an embedding analysis and returns 202 with a jobId. DELETE cancels the active job and returns the cancelled job ID or null.

Image Clustering#

GET /api/datasets/{owner}/{dataset}/images/clustering

Python SDK: client.datasets.clustering(owner, dataset)

Returns the UMAP 2D layout from a completed analysis, paginated with offset and limit (default and max 50,000). Each entry has id, umapX, umapY, split, classIds, width, height, bytes, labelCount, and missing.

List Models Trained on a Dataset#

GET /api/datasets/{owner}/{dataset}/models

Python SDK: client.datasets.models(owner, dataset)

Response:

{
    "models": [
        {
            "id": "65f1c0a2b3d4e5f601234599",
            "owner": "acme-vision",
            "project": "inspection",
            "model": "v3",
            "name": "v3",
            "status": "completed",
            "task": "detect",
            "epochs": 100,
            "bestEpoch": 87,
            "metrics": { "mAP50": 0.85, "mAP50-95": 0.72, "precision": 0.88, "recall": 0.81 },
            "startedAt": "2026-01-14T22:00:00Z",
            "completedAt": "2026-01-15T10:00:00Z",
            "createdAt": "2026-01-14T21:55:00Z"
        }
    ],
    "count": 1
}

List Dataset Images#

GET /api/datasets/{owner}/{dataset}/images

Python SDK: client.datasets.images(owner, dataset)

Query Parameters:

ParameterTypeDescription
limitintMaximum images to return (default: 50, max: 5000)
offsetintImages to skip (default: 0)
cursorstringLast image ID from the previous page, for cursor pagination
includeTotalbooleanInclude the total matching count (default: true)
splitstringFilter by split: train, val, test
hasLabelbooleanFilter by annotation state
hasErrorbooleanFilter by processing error state
classIdsstringComma-separated class IDs; returns images containing any of them
searchstringSubstring match on filename and custom metadata (max 200 chars)
sortstringnewest (default), oldest, name-asc, name-desc, height-asc, height-desc, width-asc, width-desc, size-asc, size-desc, labels-asc, labels-desc
includeThumbnailsbooleanInclude signed thumbnail URLs (default: true)
includeImageUrlsbooleanInclude signed full-size image URLs (default: false)
includeLabelsbooleanInclude capped preview annotations (default: false)

Response:

{
    "images": [
        {
            "id": "65f1c0a2b3d4e5f601234567",
            "hash": "9f2c1d4b6a8e0f3c5d7b9a1e2f4c6d8b",
            "ext": "jpg",
            "name": "aisle-04.jpg",
            "thumbnailUrl": "https://storage.googleapis.com/...&signature=...",
            "width": 1920,
            "height": 1080,
            "split": "train",
            "labelCount": 6,
            "bytes": 284213,
            "error": null
        }
    ],
    "total": 1000,
    "hasMore": true,
    "classes": ["person", "forklift"],
    "errorCount": 0,
    "nextCursor": "65f1c0a2b3d4e5f601234567"
}

Get Selected Images#

POST /api/datasets/{owner}/{dataset}/images

Python SDK: client.datasets.selected_images(owner, dataset, image_ids=...)

Returns the same image shape for up to 1,000 supplied image IDs, and accepts the same filter and URL query parameters as the list operation.

{
    "imageIds": ["65f1c0a2b3d4e5f601234567", "65f1c0a2b3d4e5f601234568"]
}

Ingest Dataset Data#

POST /api/datasets/{owner}/{dataset}/ingest

Python SDK: client.datasets.ingest(owner, dataset, body=...)

Processes a completed upload, a remote archive, or a connected storage source into an existing dataset. Supply exactly one source:

FieldTypeDescription
sessionIdstringUpload session from POST /api/upload/signed-url, already completed
sourceUrlstringPublic HTTP or HTTPS URL of a ZIP, TAR, TAR.GZ, TGZ, or NDJSON file (max 4096 chars)
referenceobjectA connected source: cloud storage (provider: "cloud", integrationId, target, prefix) or On Premise (provider: "local", keyId, root, prefix)
targetSplitstringtrain, val, or test; overrides the archive's split structure
conflictPolicystringskip, keep_both, or replace for filename or content conflicts
classMappingobjectMaps incoming class names to a class index, an existing or new class name, or null to skip
imageMetadataobjectCustom metadata keyed by each image's archive-relative path or NDJSON file value

Upload sessions are bound to a dataset by the assetId passed to POST /api/upload/signed-url, and ingest rejects a session that belongs to a different dataset.

Body (uploaded archive):

{
    "sessionId": "session_abc123",
    "targetSplit": "train"
}

Body (remote archive or NDJSON):

{
    "sourceUrl": "https://example.com/my-dataset.zip"
}

Body (importing labels on a later ingest):

{
    "sessionId": "session_abc123",
    "classMapping": { "person": 0, "automobile": "forklift", "background": null }
}

Body (attaching per-image metadata):

{
    "sessionId": "session_abc123",
    "imageMetadata": {
        "airbus-wing.jpg": { "aircraft": { "family": "A350" }, "inspectionStatus": "reviewed" },
        "images/tail.jpg": { "aircraft": { "family": "A320" }, "inspectionSeverity": 2 }
    }
}

Metadata keys must match the normalized path inside the archive, including folders. For NDJSON imports, each record can carry its own metadata object, which takes precedence over a matching imageMetadata entry. Archive paths are limited to 1,024 characters, top-level metadata keys to 128 characters, and each metadata object — as well as the whole imageMetadata map — to 500,000 serialized characters.

Class Mapping

The first ingest creates classes from the archive automatically. On later ingests, archive classes omitted from classMapping fall back to a case-insensitive match against existing dataset classes. Labels are skipped only for classes explicitly mapped to null or without a matching existing class.

Response (201):

{
    "jobId": "65f1c0a2b3d4e5f6012345aa",
    "status": "queued"
}
graph LR
    A[POST /api/datasets]:::start --> B[POST /api/upload/signed-url]:::proc
    B --> C[PUT archive to signed URL]:::proc
    C --> D[POST /api/upload/complete]:::proc
    D --> E["POST /api/datasets/{owner}/{dataset}/ingest"]:::proc
    E --> F[Process archive]:::proc
    F --> G[Dataset ready]:::out

    classDef start fill:#4CAF50,color:#fff
    classDef proc fill:#2196F3,color:#fff
    classDef out fill:#9C27B0,color:#fff
Upload one image with metadata using Python

The same code handles a group of images: add more files to the ZIP and matching entries to imageMetadata.

import io
import zipfile
from pathlib import Path

import requests

api = "https://platform.ultralytics.com/api"
headers = {"Authorization": "Bearer YOUR_API_KEY"}
owner, dataset = "acme-vision", "warehouse"
dataset_id = "65f1c0a2b3d4e5f601234567"  # id returned by POST /api/datasets
image_path = Path("airbus-wing.jpg")

archive = io.BytesIO()
with zipfile.ZipFile(archive, "w", zipfile.ZIP_DEFLATED) as zf:
    zf.write(image_path, image_path.name)
data = archive.getvalue()

signed = requests.post(
    f"{api}/upload/signed-url",
    headers=headers,
    json={
        "assetType": "datasets",
        "assetId": dataset_id,
        "filename": "images.zip",
        "contentType": "application/zip",
        "totalBytes": len(data),
    },
)
signed.raise_for_status()
upload = signed.json()

requests.put(upload["uploadUrl"], headers={"Content-Type": "application/zip"}, data=data).raise_for_status()
requests.post(
    f"{api}/upload/complete",
    headers=headers,
    json={"sessionId": upload["sessionId"]},
).raise_for_status()

ingest = requests.post(
    f"{api}/datasets/{owner}/{dataset}/ingest",
    headers=headers,
    json={
        "sessionId": upload["sessionId"],
        "imageMetadata": {
            "airbus-wing.jpg": {
                "aircraft": {"family": "A350", "section": "wing"},
                "inspectionStatus": "reviewed",
            }
        },
    },
)
ingest.raise_for_status()
print(ingest.json())

Images API#

Inspect, annotate, move, and delete dataset images by their 24-character image ID. See Annotation documentation.

Get Image#

GET /api/images/{imageId}

Python SDK: client.images.retrieve(image_id)

Returns metadata (custom, user-defined), properties (filename, hash, dimensions, split, counts, timestamps), labels, and the dataset's classNames.

Update Image#

PATCH /api/images/{imageId}

Python SDK: client.images.update(image_id, body=...)

Replaces either the annotations or the custom metadata — send one of the two shapes, not both.

Body (annotations):

{
    "labels": [
        { "classId": 0, "bbox": [0.5, 0.5, 0.2, 0.3] },
        { "classId": 1, "segments": [0.1, 0.2, 0.3, 0.2, 0.2, 0.4] }
    ]
}

Body (metadata):

{
    "metadata": { "location": "strasbourg", "reviewed": true }
}
Coordinate Format

Label coordinates use YOLO normalized values between 0 and 1. Bounding boxes use [x_center, y_center, width, height]. Segmentation labels use segments, a flattened list of polygon vertices [x1, y1, x2, y2, ...]. Pose labels use keypoints in one consistent flat shape: pairs [x1, y1, x2, y2, ...] or triples [x1, y1, v1, x2, y2, v2, ...], where visibility conventionally uses 0, 1, or 2. Oriented boxes use obb corners. Saved coordinates are rounded to 5 decimal places, and an image accepts at most 10,000 annotations.

Delete Image#

DELETE /api/images/{imageId}

Python SDK: client.images.delete(image_id)

Permanently deletes one image and its annotations.

Auto-Annotate Image#

POST /api/images/{imageId}/predict

Python SDK: client.images.predict(image_id, model_id=...)

Runs YOLO inference on the image and returns predicted annotations. It does not save them — write the results back with PATCH /api/images/{imageId} when you are happy with them.

FieldTypeRequiredDescription
modelIdstringYesFully qualified model URI, ul://{owner}/{project}/{model}
confidencefloatNoConfidence threshold, 0.01 – 1.0 (default: 0.25)
ioufloatNoIoU threshold for non-maximum suppression, 0.0 – 0.95 (default: 0.7)

Response: success, predictions (annotation objects), modelUsed, and inferenceTime. A model whose classes do not match the dataset returns 422.

Bulk Move Images#

PATCH /api/images/bulk

Python SDK: client.images.update_bulk(image_ids=..., split=...)

Moves up to 1,000 images from one dataset into a different split.

{
    "imageIds": ["65f1c0a2b3d4e5f601234567"],
    "split": "val",
    "conflictPolicy": "skip"
}

Filename or content conflicts return 409 until you choose a basket-wide conflictPolicy of skip, keep_both, or replace. The response reports modifiedCount, skippedCount, and targetSplit.

Bulk Delete Images#

DELETE /api/images/bulk

Python SDK: client.images.delete_bulk(image_ids=...)

{
    "imageIds": ["65f1c0a2b3d4e5f601234567", "65f1c0a2b3d4e5f601234568"]
}

Deletes up to 1,000 images from a single dataset and returns deletedCount and deletedImageIds.

Get Signed Image URLs#

POST /api/images/urls

Python SDK: client.images.urls(image_ids=...)

Returns temporary signed URLs for up to 100 image IDs from one dataset.

{
    "imageIds": ["65f1c0a2b3d4e5f601234567"]
}

Response: urls and thumbnails, both keyed by image ID.


Projects API#

Organize your models into projects. Each model belongs to one project. See Projects documentation.

List Projects#

GET /api/projects/{owner}

Python SDK: client.projects.list(owner)

Query Parameters:

ParameterTypeDescription
limitintMaximum projects to return (default: 20, max: 500)

Get Project#

GET /api/projects/{owner}/{project}

Python SDK: client.projects.retrieve(owner, project)

Returns the project object, a models array of per-model summaries (status, metrics, epochs, weights, train args), and isOwner.

Create Project#

POST /api/projects

Python SDK: client.projects.create(project=..., name=...)

FieldTypeRequiredDescription
projectstringYesProject name used in Platform URLs
namestringYesDisplay name (max 100 chars)
descriptionstringNoDescription (max 1000 chars)
visibilitystringNopublic or private
tagsarrayNoUp to 50 tags
licensestringNoProject license identifier
metadataobjectNoCustom JSON metadata
ownerstringNoTeam workspace handle; defaults to your personal workspace
curl -X POST \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "project": "inspection",
    "name": "Inspection",
    "description": "Detection experiments",
    "metadata": {"department": "manufacturing", "cost_center": "cv-01"}
  }' \
  https://platform.ultralytics.com/api/projects

Response (201): id, owner, project, region.

Update Project#

PATCH /api/projects/{owner}/{project}

Python SDK: client.projects.update(owner, project)

Accepted fields: name, description, visibility, metadata, tags, license, archived, iconColor, iconLetter, viewPreferences, and starred.

{
    "metadata": { "department": "research", "program": "inspection" }
}

Send an empty metadata object ({}) to clear it. Project metadata uses the same 128-character key and 500,000-character serialized-object limits as dataset metadata.

Delete Project#

DELETE /api/projects/{owner}/{project}

Python SDK: client.projects.delete(owner, project)

Moves the project and its models to trash, returning cascadedModels.

Clone Project#

POST /api/projects/{owner}/{project}/clone

Python SDK: client.projects.clone(owner, project)

Clones an accessible project and its completed models. The optional body accepts project, name, description, visibility, license, and a destination owner.


Models API#

Manage trained YOLO models — view metrics, download weights, run inference, and monitor training. See Models documentation.

List Models in a Project#

GET /api/models/{owner}/{project}

Python SDK: client.models.list(owner, project)

Query Parameters:

ParameterTypeDescription
limitintMaximum models to return (default: 20, max: 100)

Get Model#

GET /api/models/{owner}/{project}/{model}

Python SDK: client.models.retrieve(owner, project, model)

Query Parameters:

ParameterTypeDescription
analysisintSet to 1 to return per-image validation analysis instead of the model

The default response contains the model object — status, task, metrics, trainArgs, trainResults, classNames, computeCost, metadata, and more — plus isOwner.

Create Model#

POST /api/models

Python SDK: client.models.create(body=...)

Creates an untrained model record you can attach weights to or train.

FieldTypeRequiredDescription
projectstringYesDestination project name
ownerstringNoWorkspace handle; defaults to your personal workspace
modelstringNoModel name used in Platform URLs; generated when omitted
namestringNoDisplay name (only accepted alongside model)
descriptionstringNoDescription (max 1000 chars)
taskstringNodetect, segment, semantic, depth, classify, pose, or obb
metadataobjectNoCustom JSON metadata
trainArgsobjectNoTraining arguments to record
metricsobjectNoMetrics such as mAP50, mAP50-95, precision, recall
epochsnumberNoEpoch count for an already-trained model
versionstringNoVersion label (max 50 chars)

Response (201): id, owner, project, model, region.

Model File Upload

To attach .pt weights, request a signed upload URL with assetType: "models" and this model's id as assetId, PUT the file to the returned URL, then call POST /api/upload/complete with the returned sessionId.

Update Model#

PATCH /api/models/{owner}/{project}/{model}

Python SDK: client.models.update(owner, project, model)

Accepted fields include name, description, color, metadata, status, license, datasetSlug, trainArgs, trainResults, epochs, bestEpoch, bestFitness, version, trainingError, and starred.

{
    "metadata": { "release": "candidate-3", "reviewed": true }
}

Custom metadata is separate from training-owned fields such as trainArgs, environment, and trainResults, and uses the same size limits as dataset metadata.

Delete Model#

DELETE /api/models/{owner}/{project}/{model}

Python SDK: client.models.delete(owner, project, model)

Moves the model to trash for 30 days.

Download Model Files#

GET /api/models/{owner}/{project}/{model}/files

Python SDK: client.models.files(owner, project, model)

Returns short-lived signed URLs for the model's weights.

{
    "files": [
        {
            "name": "best.pt",
            "size": 6534127,
            "downloadUrl": "https://storage.googleapis.com/...&signature=..."
        }
    ]
}

Clone Model#

POST /api/models/{owner}/{project}/{model}/clone

Python SDK: client.models.clone(owner, project, model, project_body=...)

Copies an accessible model into an existing project.

{
    "owner": "acme-vision",
    "project": "inspection",
    "model": "v3-copy",
    "name": "V3 Copy",
    "description": "Cloned from a public model"
}
FieldTypeRequiredDescription
projectstringYesDestination project name
ownerstringNoDestination workspace; defaults to your personal one
modelstringNoDestination model name
namestringNoDestination display name
descriptionstringNoDescription for the clone

Run Inference#

POST /api/models/{owner}/{project}/{model}/predict

Python SDK: client.models.predict(owner, project, model, body=...)

Public models can be predicted without authentication. Private and shared models require an API key with access to the parent project.

Multipart Form:

ParameterTypeDefaultRangeDescription
filefile--Image or video file (required unless source set)
conffloat0.250.01 – 1.0Minimum confidence threshold
ioufloat0.70.0 – 0.95NMS IoU threshold
imgszint64032 – 1280Input image size in pixels
normalizeboolfalse-Return bounding box coordinates as 0 – 1
decimalsint50 – 10Decimal precision for coordinate values
bitsint88, 12, 16Depth map quantization, depth models only
sourcestring--Image URL or base64 string (alternative to file)

Provide either file or source. Depth models also accept bits (8, 12, or 16) to select the depth map's PNG quantization. Requests that exceed the service's input limits return 413.

curl -X POST \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "file=@image.jpg" \
  -F "conf=0.5" \
  https://platform.ultralytics.com/api/models/acme-vision/inspection/v3/predict

Response:

Each entry in images carries shape, speed, results, and, for dense-prediction tasks, a semantic_mask or depth PNG payload (depth values are pixel × max / divisor, with divisor 255 for the default 8-bit map and 65535 when bits is 12 or 16). The metadata object reports image count, function timings, task, and service versions. Internal model paths are never returned.

{
    "images": [
        {
            "shape": [1080, 1920],
            "speed": { "preprocess": 2.1, "inference": 12.4, "postprocess": 1.3 },
            "results": [
                {
                    "class": 0,
                    "name": "person",
                    "confidence": 0.92,
                    "box": { "x1": 100, "y1": 50, "x2": 300, "y2": 400 }
                }
            ]
        }
    ],
    "metadata": {
        "imageCount": 1,
        "functionTimeAlive": 184.2,
        "functionTimeCall": 0.31,
        "task": "detect",
        "version": { "ultralytics": "8.4.120" }
    }
}

Check Training Progress#

GET /api/models/{owner}/{project}/{model}/training

Python SDK: client.models.training(owner, project, model)

Returns job, containing status, epoch progress, timing, compute details, train args, epoch metrics, and safe error details, or null when the model has never been trained. Models in public projects are readable without authentication.

Cancel Training#

DELETE /api/models/{owner}/{project}/{model}/training

Python SDK: client.models.delete_training(owner, project, model)

Terminates the running compute instance and marks the job cancelled. Returns 409 when training is no longer active.


Training API#

Launch YOLO training on cloud GPUs and monitor progress in real time. See Cloud Training documentation.

graph LR
    A[POST /api/training/start]:::start --> B[Job Created]:::proc
    B --> C{Training}:::decide
    C -->|progress| D[GET .../training]:::proc
    C -->|cancel| E[DELETE .../training]:::error
    C -->|complete| F[Model Ready]:::out
    F --> G[Deploy or Export]:::proc

    classDef start fill:#4CAF50,color:#fff
    classDef proc fill:#2196F3,color:#fff
    classDef decide fill:#FF9800,color:#fff
    classDef out fill:#9C27B0,color:#fff
    classDef error fill:#F44336,color:#fff

Get GPU Availability#

GET /api/training/gpu-availability

Python SDK: client.training.gpu_availability()

Returns current stock status keyed by GPU ID. Public and unauthenticated; pass managed=true to include managed training capacity, which does require an API key.

Start Training#

POST /api/training/start

Python SDK: client.training.start(model_id=..., train_args=...)

FieldTypeRequiredDescription
modelIdstringYesID of the model to train
trainArgsobjectYesYOLO training arguments; model, data, and epochs are required
gpuTypestringNoCloud GPU to use (default: rtx-4090)
captureDatasetVersionbooleanNoSave an immutable dataset version for this run (default: false)
curl -X POST \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "modelId": "65f1c0a2b3d4e5f601234599",
    "gpuType": "rtx-4090",
    "trainArgs": {
      "model": "yolo26n.pt",
      "data": "ul://acme-vision/datasets/warehouse",
      "epochs": 100,
      "imgsz": 640,
      "batch": 16
    }
  }' \
  https://platform.ultralytics.com/api/training/start

Response:

{
    "modelId": "65f1c0a2b3d4e5f601234599",
    "status": "starting",
    "gpuType": "rtx-4090",
    "estimatedCost": { "pricePerHour": 0.69, "gpuMemoryGb": 24 },
    "billing": {
        "estimatedCostCents": 138,
        "estimatedCostDisplay": "$1.38",
        "balanceCents": 2500
    }
}

Training returns 402 when your credit balance is too low and 503 when no capacity is available for the requested GPU.

GPU Types

26 GPU types are available, from rtx-2000-ada through b300, including rtx-4090, l40s, a100-80gb-pcie, a100-80gb-sxm, rtx-pro-6000, h100-sxm, h200-sxm, and b200. See Cloud Training for the full list with pricing.


Exports API#

Convert models to optimized formats like ONNX, TensorRT, CoreML, and LiteRT for edge deployment. See Deploy documentation.

List Exports#

GET /api/models/{owner}/{project}/{model}/exports

Python SDK: client.exports.list(owner, project, model)

Query Parameters:

ParameterTypeDescription
statusstringFilter by queued, starting, running, completed, failed, or cancelled
limitintMaximum exports to return (default: 20, max: 100)

Create Export#

POST /api/models/{owner}/{project}/{model}/exports

Python SDK: client.exports.create(owner, project, model, format=...)

FieldTypeRequiredDescription
formatstringYesTarget export format (see table below)
gpuTypestringConditionalRequired when format is engine; use a supported GPU or Jetson target
argsobjectNoExport options: imgsz, quantize, dynamic, simplify, opset, conf, iou, batch, workspace, nms, end2end, optimize, keras, and name (device target for RKNN, QNN, Hailo, and Ascend formats)
curl -X POST \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"format": "onnx", "args": {"imgsz": 640, "quantize": 16}}' \
  https://platform.ultralytics.com/api/models/acme-vision/inspection/v3/exports

Response (201): id, format, status (queued or running), gpuType, region. An equivalent export that is already in flight returns 409.

Supported Formats:

Use the format argument from the shared export table below. PyTorch is the source format and is not an API export target.

Formatformat ArgumentModelMetadataArguments
PyTorch-yolo26n.pt-
TorchScripttorchscriptyolo26n.torchscriptimgsz, quantize, dynamic, nms, batch, device
ONNXonnxyolo26n.onnximgsz, quantize, dynamic, simplify, opset, nms, batch, data, fraction, device
OpenVINOopenvinoyolo26n_openvino_model/imgsz, quantize, dynamic, nms, batch, data, fraction, device
TensorRTengineyolo26n.engineimgsz, quantize, dynamic, simplify, opset, workspace, nms, batch, data, fraction, device
CoreMLcoremlyolo26n.mlpackageimgsz, dynamic, quantize, nms, batch, device
TF SavedModelsaved_modelyolo26n_saved_model/imgsz, keras, quantize, opset, nms, batch, data, fraction, device
TF GraphDefpbyolo26n.pbimgsz, opset, batch, device
TF Edge TPUedgetpuyolo26n_edgetpu.tfliteimgsz, quantize, opset, data, fraction, device
PaddlePaddlepaddleyolo26n_paddle_model/imgsz, batch, device
MNNmnnyolo26n.mnnimgsz, batch, dynamic, quantize, simplify, opset, nms, device
NCNNncnnyolo26n_ncnn_model/imgsz, quantize, batch, device
IMX500imxyolo26n_imx_model/imgsz, quantize, data, fraction, nms, device
RKNNrknnyolo26n_rknn_model/imgsz, batch, name, quantize, simplify, opset, data, fraction, device
ExecuTorchexecutorchyolo26n_executorch_model/imgsz, batch, device
Axeleraaxelerayolo26n_axelera_model/imgsz, batch, quantize, data, fraction, device
DEEPXdeepxyolo26n_deepx_model/imgsz, quantize, simplify, opset, data, optimize, device
Qualcomm QNNqnnyolo26n_qnn.onnximgsz, batch, name, quantize, simplify, opset, data, fraction, device
LiteRTlitertyolo26n.tfliteimgsz, quantize, batch, data, fraction, device
Hailohailoyolo26n_hailo_model/imgsz, name, quantize, data, fraction, simplify, conf, iou
Huawei Ascendascendyolo26n_ascend_model/imgsz, batch, name, quantize, opset, simplify, nms
Apple Core AIcoreaiyolo26n.aimodelimgsz, batch, quantize

Get Export Status#

GET /api/models/{owner}/{project}/{model}/exports/{exportId}

Python SDK: client.exports.retrieve(owner, project, model, export_id)

Returns the export object with status, format, args, gpuType, timestamps, and — once complete — a file object containing size, downloadUrl, and downloadFilename.

Cancel or Delete Export#

DELETE /api/models/{owner}/{project}/{model}/exports/{exportId}

Python SDK: client.exports.delete(owner, project, model, export_id)

Cancels an active export or deletes a finished one and its file. The response reports which happened:

{
    "success": true,
    "action": "cancelled"
}

Deployments API#

Deploy models to dedicated inference endpoints with health checks and monitoring. See Endpoints documentation.

graph LR
    A[Create]:::start --> B[Deploying]:::proc
    B --> C[Ready]:::out
    C -->|action stop| D[Stopped]:::extern
    C -->|action replace| B
    D -->|action start| C
    C -->|delete| E[Deleted]:::error
    D -->|delete| E
    C -->|predict| F[Inference Results]:::out

    classDef start fill:#4CAF50,color:#fff
    classDef proc fill:#2196F3,color:#fff
    classDef out fill:#9C27B0,color:#fff
    classDef error fill:#F44336,color:#fff
    classDef extern fill:#607D8B,color:#fff

List Deployments#

GET /api/deployments/{owner}

Python SDK: client.deployments.list(owner)

Query Parameters:

ParameterTypeDescription
statusstringcreating, deploying, ready, stopping, stopped, or failed
modelstringFilter by {project}/{model}, for example inspection/v3
limitintMaximum deployments to return (default: 20, max: 100)

Anonymous callers must filter by one public model; listing a whole workspace requires authentication.

Create Deployment#

POST /api/deployments/{owner}

Python SDK: client.deployments.create(owner, project=..., model=..., deployment=..., name=..., region=...)

Body:

{
    "project": "inspection",
    "model": "v3",
    "deployment": "edge-1",
    "name": "Edge 1",
    "region": "us-central1"
}
FieldTypeRequiredDescription
projectstringYesProject containing the model
modelstringYesModel to deploy
deploymentstringYesDeployment name used in Platform URLs
namestringYesDisplay name
regionstringYesOne of 42 supported deployment regions

Response (201): id, deployment, status (creating), message, and region.

Resource Sizing

CPU, memory, and instance scaling are managed by the Platform from your plan limits, and the create request does not accept a resource configuration. The current values are returned in the resources object on every deployment read.

Region Selection

Choose a region close to your users for lowest latency. The Platform UI shows latency estimates for all 42 available regions.

Get Deployment#

GET /api/deployments/{owner}/{deployment}

Python SDK: client.deployments.retrieve(owner, deployment)

Returns the deployment object with status, statusMessage, region, serviceUrl, and resources.

Start, Stop, or Replace a Deployment#

PATCH /api/deployments/{owner}/{deployment}

Python SDK: client.deployments.update(owner, deployment, body=...)

A single action field selects the operation:

{ "action": "start" }

Replacing rolls out a new revision while preserving the deployment ID, region, and endpoint URL; the existing revision stays live if the rollout fails. The replacement model must be a completed model with weights that your key can access. Completed operations return 200 with status ready or stopped; operations still rolling out return 202 with deploying or stopping.

Delete Deployment#

DELETE /api/deployments/{owner}/{deployment}

Python SDK: client.deployments.delete(owner, deployment)

Permanently removes the inference endpoint.

Health Check#

GET /api/deployments/{owner}/{deployment}/health

Python SDK: client.deployments.health(owner, deployment)

Pings and warms the endpoint, returning healthy, latencyMs, and the upstream status code.

Run Inference on a Deployment#

POST /api/deployments/{owner}/{deployment}/predict

Python SDK: client.deployments.predict(owner, deployment, body=...)

Routes an image or video through the dedicated endpoint. The request and response contracts match model inference.

Multipart Form:

ParameterTypeDefaultRangeDescription
filefile--Image or video file (required unless source set)
conffloat0.250.01 – 1.0Minimum confidence threshold
ioufloat0.70.0 – 0.95NMS IoU threshold
imgszint64032 – 1280Input image size in pixels
normalizeboolfalse-Return bounding box coordinates as 0 – 1
decimalsint50 – 10Decimal precision for coordinate values
bitsint88, 12, 16Depth map quantization, depth models only
sourcestring--Image URL or base64 string (alternative to file)

Get Metrics#

GET /api/deployments/{owner}/{deployment}/metrics

Python SDK: client.deployments.metrics(owner, deployment)

Query Parameters:

ParameterTypeDescription
rangestring1h, 6h, 24h (default), 7d, or 30d
sparklinebooleanReturn the compact dashboard summary instead of full series (default: false)

The full response contains summary (request totals, error rate, average and p50/p95/p99 latency) and timeSeries (requests, errors, latency, CPU, memory, instance count). The sparkline response returns requests24h, totalRequests, errorRate, and avgLatencyMs.

Get Logs#

GET /api/deployments/{owner}/{deployment}/logs

Python SDK: client.deployments.logs(owner, deployment)

Query Parameters:

ParameterTypeDescription
severitystringComma-separated: DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL, ALERT, EMERGENCY
limitintEntries to return (default: 50, max: 200)
pageTokenstringPagination token from a previous response

Trash API#

View, restore, and permanently delete soft-deleted projects, datasets, and models. Items are purged automatically after 30 days. See Trash documentation.

List Trash#

GET /api/trash

Python SDK: client.lifecycle.trash()

Query Parameters:

ParameterTypeDescription
typestringall (default), project, dataset, or model
pageintPage number (default: 1)
limitintItems per page (default: 50, max: 200)

The response includes items (each with daysRemaining), total, page, limit, totalPages, and a summary with totals by type.

Restore Item#

POST /api/trash

Python SDK: client.lifecycle.restore(id=..., type=...)

{
    "id": "65f1c0a2b3d4e5f601234567",
    "type": "dataset"
}

Restoring a project also restores the models that were trashed with it, reported as restoredModels.

Permanently Delete#

DELETE /api/trash

Python SDK: client.lifecycle.delete_trash(body=...)

Delete one item:

{
    "id": "65f1c0a2b3d4e5f601234567",
    "type": "dataset"
}

Or empty the whole trash:

{
    "all": true
}

The response reports deletedCount, plus cascadedModels and survivingDeployments where relevant.

Irreversible

Permanent deletion cannot be undone. The resource and all associated data are removed.


Upload API#

Upload files directly to cloud storage using signed URLs. Completing a model upload attaches its weights; completing a dataset archive upload records the session, which you then pass to dataset ingest. See Data documentation.

Get Signed Upload URL#

POST /api/upload/signed-url

Python SDK: client.upload.signed_url(body=...)

Body:

{
    "assetType": "datasets",
    "assetId": "65f1c0a2b3d4e5f601234567",
    "filename": "warehouse.zip",
    "contentType": "application/zip",
    "totalBytes": 52428800
}
FieldTypeRequiredDescription
assetTypestringYesdatasets, models, images, or videos
assetIdstringYesID of the target dataset or model
filenamestringYesOriginal filename (max 256 chars)
contentTypestringYesMIME type
totalBytesnumberYesFile size in bytes
Dataset Archive Filenames

When assetType is datasets, filename must end in .zip, .tar, .tar.gz, .tgz, or .ndjson. Package loose images into an archive before uploading.

Response:

{
    "sessionId": "session_abc123",
    "uploadUrl": "https://storage.googleapis.com/...&signature=...",
    "expiresAt": "2026-02-22T12:00:00Z"
}

Upload the file with a PUT request to uploadUrl, using the same Content-Type you declared.

Complete Upload#

POST /api/upload/complete

Python SDK: client.upload.complete(session_id=...)

{
    "sessionId": "session_abc123",
    "checksum": "<optional sha-256 hex>"
}

Response: success and a file object with size and contentType. For models this attaches the weights; for dataset archives, call ingest next to start processing.


Storage Integrations API#

Connect read-only Google Cloud Storage, Amazon S3, or Azure Blob Storage accounts and browse them as dataset sources. See Integrations documentation.

List Integrations#

GET /api/integrations/buckets

Python SDK: client.storage_integrations.list()

Returns integrations, each with id, provider, credentialIdentity, targets, and createdAt. Credentials are never returned.

Discover Locations#

POST /api/integrations/buckets/discover

Python SDK: client.storage_integrations.discover(body=...)

Lists the buckets or containers readable with the supplied credentials, without saving them.

{
    "provider": "gcs",
    "credentials": {
        "client_email": "svc@project.iam.gserviceaccount.com",
        "private_key": "-----BEGIN PRIVATE KEY-----\n...",
        "project_id": "my-project"
    }
}

Response: {"targets": ["my-bucket", "another-bucket"]}

Connect Storage#

POST /api/integrations/buckets

Python SDK: client.storage_integrations.create(body=...)

Same credential shapes as discovery, plus a required targets array of 1-50 bucket or container names. Returns 201 with the stored integration. Temporary S3 credentials (ASIA access keys) are rejected.

Browse Objects#

GET /api/integrations/buckets/{id}/objects

Python SDK: client.storage_integrations.objects(id, target=...)

Query Parameters:

ParameterTypeRequiredDescription
targetstringYesBucket or container name
prefixstringNoFolder prefix (max 1024 chars)
cursorstringNoProvider pagination cursor from a prior page

Returns entries (each kind is folder or file) and an optional cursor for the next page.

Disconnect Storage#

DELETE /api/integrations/buckets/{id}

Python SDK: client.storage_integrations.delete(id)

Removes the saved credentials without deleting provider data. Connected datasets remain visible, but their files stay unavailable until the same storage account is reconnected. Requires workspace admin access.


Dataset Import API#

Import datasets from third-party services. See Roboflow integration.

Preview a Roboflow Import#

POST /api/integrations/roboflow/preview

Python SDK: client.datasets.preview_roboflow(api_key=...)

Resolves a Roboflow API key into an import plan: workspace details, newDatasets that would be imported, counts of skipped, unsupported, and unresolved projects, bytesTotal, and your storage headroom. The Roboflow API key is read from the body and is not persisted.

{
    "apiKey": "ROBOFLOW_API_KEY"
}

Import from Roboflow#

POST /api/integrations/roboflow/import

Python SDK: client.datasets.import_roboflow(api_key=..., items=...)

Queues ingest jobs for up to 500 selected Roboflow project versions, using the items returned by the preview.

{
    "apiKey": "ROBOFLOW_API_KEY",
    "items": [
        {
            "workspace": "my-workspace",
            "projectId": "warehouse-safety",
            "projectName": "Warehouse Safety",
            "projectType": "object-detection",
            "latestVersion": 4
        }
    ]
}

Response (201): imported, failed, and skipped arrays. Imports require storage headroom, and each dataset must fit your plan's per-import size limit.


Account API#

Inspect your Platform account, keys, storage, and public profiles. See Settings documentation.

Account Summary#

GET /api/account/summary

Python SDK: client.account.summary()

Returns the plan, credit balance, and resource counts for the workspace that issued the key.

{
    "username": "acme-vision",
    "name": "Acme Vision",
    "accountType": "team",
    "plan": "pro",
    "creditsCents": 2500,
    "counts": { "projects": 4, "datasets": 7, "models": 21 },
    "teams": []
}
Team List

teams is populated for browser sessions. API-key responses return an empty list, because a key is already scoped to a single workspace.

List API Keys#

GET /api/api-keys

Python SDK: client.account.api_keys()

Returns keys with keyId, name, keyPrefix, and createdAt for the key's workspace. API-key-authenticated requests receive metadata only; full key values are shown to the workspace owner in Settings > API Keys in the Platform UI, which is also where keys are created and revoked.

Check Storage Usage#

GET /api/storage

Python SDK: client.account.storage()

Query Parameters:

ParameterTypeDescription
detailsbooleanInclude the ten largest storage consumers (default: false)

Response:

{
    "tier": "pro",
    "usage": {
        "storage": { "current": 1073741824, "limit": 107374182400, "percent": 1.0 },
        "datasets": { "current": 536870912, "limit": 107374182400, "percent": 0.5 }
    },
    "breakdown": {
        "byCategory": {
            "datasets": { "bytes": 536870912, "count": 2 },
            "models": { "bytes": 268435456, "count": 4 },
            "exports": { "bytes": 268435456, "count": 3 }
        },
        "topItems": [
            {
                "_id": "65f1c0a2b3d4e5f601234567",
                "name": "Warehouse",
                "slug": "warehouse",
                "sizeBytes": 536870912,
                "type": "dataset"
            }
        ]
    },
    "region": "us",
    "username": "acme-vision",
    "updatedAt": "2026-01-15T10:00:00Z"
}

Get a Public User Profile#

GET /api/users

Python SDK: client.account.profile(username=...)

Query Parameters:

ParameterTypeRequiredDescription
usernamestringYesUsername to look up

Returns the public user profile with followerCount and, for authenticated callers, isFollowed.

Follow or Unfollow a User#

PATCH /api/users

Python SDK: client.account.follow(username=..., followed=...)

{
    "username": "target-user",
    "followed": true
}

Response: followed and the updated followerCount.


Billing API#

Check plan usage and your credit ledger. See Billing documentation.

Currency Units

Billing amounts are integers in US cents, where 100 = $1.00.

View Plan and Usage#

GET /api/billing/usage-summary

Python SDK: client.billing.usage_summary()

Returns plan (ID, status, billing cycle, period end), metrics (storage limit and usage), trainingCredit, features, creditsCents, and seat counts.

View Transactions#

GET /api/billing/transactions

Python SDK: client.billing.transactions()

Query Parameters:

ParameterTypeDescription
fromstringEarliest transaction timestamp (ISO 8601)
tostringLatest transaction timestamp (ISO 8601)

Each transaction includes id, type (such as purchase, training, monthly_grant, or refund), amountCents, balanceAfter, createdAt, an optional receiptUrl, and model context for training charges. Internal billing details are never returned.


Explore API#

Search public projects and datasets shared by the community. See Explore documentation.

Search Public Content#

GET /api/explore/search

Python SDK: client.explore.search()

Query Parameters:

ParameterTypeDescription
qstringSearch term (max 200 chars)
typestringall (default), projects, or datasets
sortstringnewest (default), oldest, stars, name-asc, name-desc, count-desc, count-asc
offsetintResults to skip (default: 0)
limitintMaximum results per resource type (default: 20, max: 100)
taskstringComma-separated task filters: detect, segment, semantic, depth, classify, pose, obb
authorstringOwner username filter
starredbooleanReturn only content starred by the authenticated caller; requires an API key

Response: projects, datasets, and hasMore.

curl "https://platform.ultralytics.com/api/explore/search?type=datasets&task=detect&sort=stars&limit=20"

Python SDK#

ultralytics-platform is a typed Python client generated from the OpenAPI contract, with one method per endpoint (client.datasets.list, client.models.predict, client.exports.create, ...). Every method accepts the path parameters positionally, other inputs as keyword arguments, and optional per-request timeout and extra_headers.

pip install "ultralytics-platform>=0.1.5" # Python 3.11+
from ultralytics_platform import Platform

with Platform() as client:  # reads ULTRALYTICS_API_KEY
    dataset = client.datasets.retrieve("acme-vision", "warehouse")
    images = client.datasets.images("acme-vision", "warehouse", limit=10)
    export = client.exports.create("acme-vision", "inspection", "v3", format="onnx")

AsyncPlatform exposes the same resource tree for async/await code, unsuccessful responses raise APIError with status_code, body, and parsed json, and connection failures raise APIConnectionError. See the SDK repository for the full README.

Python Integration#

For training and inference workflows, use the Ultralytics Python package, which handles authentication, uploads, and real-time metric streaming automatically.

Installation & Setup#

pip install "ultralytics>=8.4.120"

Verify installation:

yolo check

Authentication#

yolo login YOUR_API_KEY

Using Platform Datasets#

Reference datasets with ul:// URIs:

from ultralytics import YOLO

model = YOLO("yolo26n.pt")

# Train on your Platform dataset
model.train(
    data="ul://your-username/datasets/your-dataset",
    epochs=100,
    imgsz=640,
)

URI Format:

PatternDescription
ul://username/datasets/slugDataset
ul://username/project-nameProject
ul://username/project/model-nameSpecific model
ul://ultralytics/yolo26/yolo26nOfficial model

Pushing to Platform#

Send results to a Platform project:

from ultralytics import YOLO

model = YOLO("yolo26n.pt")

# Results automatically sync to Platform
model.train(
    data="coco8.yaml",
    epochs=100,
    project="your-username/my-project",
    name="experiment-1",
)

What syncs:

  • Training metrics (real-time)
  • Final model weights
  • Validation plots
  • Console output
  • System metrics

API Examples#

Load a model from Platform:

# Your own model
model = YOLO("ul://username/project/model-name")

# Official model
model = YOLO("ul://ultralytics/yolo26/yolo26n")

Run inference:

results = model("image.jpg")

# Access results
for r in results:
    boxes = r.boxes  # Detection boxes
    masks = r.masks  # Segmentation masks
    keypoints = r.keypoints  # Pose keypoints
    probs = r.probs  # Classification probabilities

Export model:

# Export to ONNX
model.export(format="onnx", imgsz=640, quantize=16)

# Export to TensorRT
model.export(format="engine", imgsz=640, quantize=16)

# Export to CoreML
model.export(format="coreml", imgsz=640)  # use imgsz=224 for classification

Validation:

metrics = model.val(data="ul://username/datasets/my-dataset")

print(f"mAP50: {metrics.box.map50}")
print(f"mAP50-95: {metrics.box.map}")

FAQ#

  • Use the same owner and name segments that appear in the Platform URL. A model at https://platform.ultralytics.com/acme-vision/inspection/v3 is GET /api/models/acme-vision/inspection/v3. Database IDs are still returned in responses (as id), and a few routes take them directly — image routes take an imageId, uploads take an assetId, and POST /api/training/start takes a modelId.

  • It depends on the collection. Most list endpoints accept limit:

    curl -H "Authorization: Bearer YOUR_API_KEY" \
      "https://platform.ultralytics.com/api/datasets/acme-vision?limit=50"

    Dataset images, clustering, and Explore search use offset with limit and report hasMore:

    curl "https://platform.ultralytics.com/api/explore/search?type=datasets&offset=20&limit=20&sort=stars"

    Very large image sets are best walked with the cursor returned as nextCursor:

    curl -H "Authorization: Bearer YOUR_API_KEY" \
      "https://platform.ultralytics.com/api/datasets/acme-vision/warehouse/images?limit=1000&includeTotal=false&cursor=LAST_IMAGE_ID"

    Trash uses page, and deployment logs use the opaque pageToken returned as nextPageToken.

  • Yes. Every operation on this page is a plain HTTPS request, and the complete contract is published as OpenAPI 3.2 at platform.ultralytics.com/openapi.json, which you can feed to a client generator in any language. The ultralytics-platform package is exactly that: a typed client generated from the contract, while the ultralytics package adds real-time metric streaming and automatic model uploads on top of training and inference. Browser-session-only account flows, such as billing checkout and team management, remain in the Platform UI.

  • Use the Retry-After header from the 429 response to wait the right amount of time:

    import time
    
    import requests
    
    def api_request_with_retry(url, headers, max_retries=3):
        for attempt in range(max_retries):
            response = requests.get(url, headers=headers)
            if response.status_code != 429:
                return response
            wait = int(response.headers.get("Retry-After", 2**attempt))
            time.sleep(wait)
        raise RuntimeError("Rate limit exceeded")
  • 404 means the resource does not exist or is not visible to your key at all. 403 means the resource was found but the action needs more access than your key has — editor access to modify a dataset, owner access to delete a deployment, admin access to disconnect storage, or a higher plan or quota for exports and deployments.

  • Reading public datasets, projects, and models, including their images, signed image URLs, class statistics, embedding status, clustering layout, and export list; checking training progress on a public model; downloading a public model's files; running inference on a public model; looking up a public user profile; listing deployments filtered to one public model; and searching Explore. GET /api/training/gpu-availability is fully public unless you request managed capacity. Everything else requires a key, and supplying one on a public endpoint also reveals your private resources.

Comments