CVAT API Usage Guide
Complete guide for working with CVAT tasks, jobs, and annotations.
Table of Contents
Installation
pip install -r requirements.txt
Initialization
from cvat_api import CvatApiClient
from pathlib import Path
# Initialize client
client = CvatApiClient(
cvat_host="https://app.cvat.ai",
cvat_username="your_username",
cvat_password="your_password",
cvat_organization="your_organization"
)
Common Workflows
Download and Update Job Annotations
Complete workflow for downloading annotations, modifying them, and uploading back:
# 1. Download annotations
job_id = 2355063
annotations = client.annotations.get_job_annotations(job_id)
# 2. Modify annotations
# Add a new shape
from shared.schema.cvat import CvatApiShape
new_shape = CvatApiShape(
id=0, # Will be assigned by server
type="rectangle",
frame=0,
label_id=1,
points=[100.0, 100.0, 200.0, 200.0],
occluded=False,
z_order=0,
source="manual",
attributes=[]
)
annotations.shapes.append(new_shape)
# Or modify existing shapes
for shape in annotations.shapes:
if shape.label_id == 5:
shape.occluded = True # Mark as occluded
# 3. Upload modified annotations
updated_annotations = client.annotations.put_job_annotations(
job_id=job_id,
annotations=annotations
)
print(f"✅ Updated {len(updated_annotations.shapes)} shapes")
Download and Update Task Annotations
Same workflow but at task level:
# 1. Download task annotations
task_id = 1322143
annotations = client.annotations.get_task_annotations(task_id)
# 2. Modify annotations (same as above)
# ... make your changes ...
# 3. Upload modified annotations
updated_annotations = client.annotations.put_task_annotations(
task_id=task_id,
annotations=annotations
)
Update Job Metadata
Update job status, assignee, stage, etc.:
# 1. Get current job details
job_id = 2355063
job_details = client.jobs.get_job_details(job_id)
# 2. Modify job metadata
job_details.state = "completed" # Change state
job_details.stage = "acceptance" # Change stage
# 3. Update job
updated_job = client.jobs.update_job(
job_id=job_id,
job_data=job_details
)
print(f"✅ Job {job_id} updated: {updated_job.state} / {updated_job.stage}")
Update Task Metadata
Update task name, assignee, labels, etc.:
# 1. Get current task details
task_id = 1322143
task_details = client.tasks.get_task_details(task_id)
# 2. Modify task metadata
task_details.name = "Updated Task Name"
# Note: Use model_dump(exclude_unset=True) in update method to only send changed fields
# 3. Update task
updated_task = client.tasks.update_task(
task_id=task_id,
task_data=task_details
)
print(f"✅ Task {task_id} updated: {updated_task.name}")
Complete Examples
Example 1: Batch Process Job Annotations
Process annotations for all jobs in a task:
from cvat_api import CvatApiClient
# Initialize
client = CvatApiClient(...)
# Get all jobs in task
task_id = 1322143
job_ids = client.tasks.get_task_job_ids(task_id)
# Process each job
for job_id in job_ids:
# Download annotations
annotations = client.annotations.get_job_annotations(job_id)
# Apply your modifications
for shape in annotations.shapes:
if shape.label_id == 5: # Example: filter by label
# Modify shape properties
shape.occluded = True
# Upload modified annotations
client.annotations.put_job_annotations(job_id, annotations)
print(f"✅ Processed job {job_id}")
Example 2: Download All Images and Annotations
Download complete dataset from a task:
from pathlib import Path
task_id = 1322143
output_dir = Path("./output")
# 1. Download all images
images = client.downloads.download_task_images(
task_id=task_id,
output_dir=output_dir / "images"
)
print(f"Downloaded {len(images)} images")
# 2. Download annotations
annotations = client.annotations.get_task_annotations(task_id)
# 3. Save annotations to file
import json
output_file = output_dir / "annotations.json"
output_file.write_text(annotations.model_dump_json(indent=2))
print(f"Saved annotations to {output_file}")
Example 3: Quality Control Workflow
Implement a QC workflow that marks reviewed jobs:
from shared.schema.cvat import CvatApiJobsListRequest
# List all jobs in "annotation" stage
request = CvatApiJobsListRequest(
task_id=1322143,
stage="annotation",
state="completed"
)
response = client.jobs.list_jobs(request)
for job in response.results:
# Download and review annotations
annotations = client.annotations.get_job_annotations(job.id)
# Run your QC checks
issues_found = run_quality_checks(annotations)
# Update job based on QC results
job_details = client.jobs.get_job_details(job.id)
if not issues_found:
job_details.stage = "acceptance"
job_details.state = "completed"
else:
job_details.state = "rejected"
client.jobs.update_job(job.id, job_details)
print(f"✅ QC processed job {job.id}")
Example 4: Export Annotations with Metadata
Export complete annotation dataset with metadata:
# Get project details
project_id = 239220
project = client.projects.get_project_details(project_id)
labels = client.projects.get_project_labels(project_id)
# Get all tasks
task_ids = client.projects.get_project_tasks(project_id)
# Build export structure
export_data = {
"project": project.model_dump(),
"labels": [label.model_dump() for label in labels],
"tasks": []
}
# Export each task
for task_id in task_ids:
task_details = client.tasks.get_task_details(task_id)
annotations = client.annotations.get_task_annotations(task_id)
export_data["tasks"].append({
"task": task_details.model_dump(),
"annotations": annotations.model_dump()
})
# Save export
import json
Path("export.json").write_text(json.dumps(export_data, indent=2))
print(f"✅ Exported {len(task_ids)} tasks")
Tips and Best Practices
1. Use Partial Updates
When updating jobs or tasks, use only the fields you want to change:
# Good - only update specific fields
job_details = client.jobs.get_job_details(job_id)
job_details.state = "completed"
client.jobs.update_job(job_id, job_details)
2. Handle Errors
Always handle potential errors:
try:
annotations = client.annotations.get_job_annotations(job_id)
except requests.HTTPError as e:
print(f"Failed to get annotations: {e}")
# Handle error appropriately
3. Batch Operations
For multiple jobs/tasks, use batch operations:
job_ids = client.tasks.get_task_job_ids(task_id)
for job_id in job_ids:
try:
# Process each job
process_job(client, job_id)
except Exception as e:
print(f"Error processing job {job_id}: {e}")
continue # Continue with next job
4. Validate Before Upload
Validate annotations before uploading:
annotations = client.annotations.get_job_annotations(job_id)
# Validate
for shape in annotations.shapes:
assert shape.label_id in valid_label_ids
assert len(shape.points) >= 4 # For rectangles
# Upload after validation
client.annotations.put_job_annotations(job_id, annotations)
Error Handling
The client includes automatic retry logic for transient errors (500/502/503/504):
# This will automatically retry up to 10 times for server errors
annotations = client.annotations.get_job_annotations(job_id)
For custom error handling:
import requests
from requests.exceptions import Timeout, ConnectionError
try:
annotations = client.annotations.get_job_annotations(job_id)
except Timeout:
print("Request timed out")
except ConnectionError:
print("Connection failed")
except requests.HTTPError as e:
if e.response.status_code == 404:
print("Job not found")
elif e.response.status_code == 403:
print("Access denied")
else:
print(f"HTTP error: {e}")
See Also
- API Documentation - Complete API reference
- Test Suite - Usage examples in tests
- CVAT API Docs - Official CVAT API documentation