Low-level API
Overview
The low-level API is useful if you need to work directly with REST API, but wantto have data validation and syntax assistance from your code editor. The codeon this layer is autogenerated.
Code of this component is located incvat_sdk.api_client
.
Example
Let’s see how a task with local files can be created. We will use the basic authto make things simpler.
fromtimeimportsleepfromcvat_sdk.api_clientimportConfiguration,ApiClient,models,apis,exceptionsconfiguration=Configuration(host="http://localhost",username='YOUR_USERNAME',password='YOUR_PASSWORD',)# Enter a context with an instance of the API clientwithApiClient(configuration)asapi_client:# Parameters can be passed as a plain dict with JSON-serialized data# or as model objects (from cvat_sdk.api_client.models), including# mixed variants.## In case of dicts, keys must be the same as members of models.I<ModelName># interfaces and values must be convertible to the corresponding member# value types (e.g. a date or string enum value can be parsed from a string).## In case of model objects, data must be of the corresponding# models.<ModelName> types.## Let's use a dict here. It should look like models.ITaskWriteRequesttask_spec={'name':'example task',"labels":[{"name":"car","color":"#ff00ff","attributes":[{"name":"a","mutable":True,"input_type":"number","default_value":"5","values":["4","5","6"]}]}],}try:# Apis can be accessed as ApiClient class members# We use different models for input and output data. For input data,# models are typically called like "*Request". Output data models have# no suffix.(task,response)=api_client.tasks_api.create(task_spec)exceptexceptions.ApiExceptionase:# We can catch the basic exception type, or a derived typeprint("Exception when trying to create a task:%s\n"%e)# Here we will use models instead of a dicttask_data=models.DataRequest(image_quality=75,client_files=[open('image1.jpg','rb'),open('image2.jpg','rb'),],)# If we pass binary file objects, we need to specify content type.(result,response)=api_client.tasks_api.create_data(task.id,data_request=task_data,_content_type="multipart/form-data",# we can choose to check the response status manually# and disable the response data parsing_check_status=False,_parse_response=False)assertresponse.status==202,response.msg# Wait till task data is processedfor_inrange(100):request_details,response=api_client.requests_api.retrieve(result.rq_id)status,message=request_details.status,request_details.messageifstatus.valuein{'finished','failed'}:breaksleep(0.1)assertstatus.value=='finished',status.message# Update the task object and check the task size(task,_)=api_client.tasks_api.retrieve(task.id)asserttask.size==4
ApiClient and configuration
The starting point in the low-level API is thecvat_sdk.api_client.ApiClient
class.It encapsulates session and connection logic, manages headers and cookies,and provides access to various APIs.
To create an instance ofApiClient
, you need to set up acvat_sdk.api_client.Configuration
object and pass it to theApiClient
class constructor. Additional connection-specificoptions, such as extra headers and cookies can be specified in the class constructor.ApiClient
implements the context manager protocol. Typically, you createApiClient
this way:
fromcvat_sdk.api_clientimportApiClient,Configurationconfiguration=Configuration(host="http://localhost")withApiClient(configuration)asapi_client:...
After creating anApiClient
instance, you can send requests to various server endpointsvia*_api
member properties and directly, using therest_client
member.Read more about API wrappers below.
Typically, the first thing you do withApiClient
is log in.Read more about authentication options below.
Authentication
CVAT supports 3 authentication options:
- Basic authentication, with a username and a password
- Session authentication, with a session ID and a CSRF token
- Token authentication, with an API key (deprecated)
Token authentication requires an API key, which can be obtained after logging invia the/api/auth/login
endpoint using the basic authentication credentials.
Session authentication requires a session ID and a CSRF token, which can be obtained afterlogging in via the/api/auth/login
endpoint using the basic authentication credentials.
Authentication credentials for anApiClient
instance can be specified in aConfiguration
object:
configuration=Configuration(username='YOUR_USERNAME',password='YOUR_PASSWORD',...)withApiClient(configuration)asapi_client:...
configuration=Configuration(api_key={"sessionAuth":"<sessionid cookie value>","csrfAuth":"<csrftoken cookie value>",},...)withApiClient(configuration)asapi_client:...
This authentication option is deprecated and will be removed in future.
configuration=Configuration(api_key={"tokenAuth":"Token <api key value>",},...)withApiClient(configuration)asapi_client:...
Session authentication and token authentication tokens can be received by logging inusing theApiClient.auth_api.create_login()
function. Then, the authentication keyscan be set in theApiClient
instance.
fromcvat_sdk.api_clientimportmodels(auth,_)=api_client.auth_api.create_login(models.LoginSerializerExRequest(username="username",password="password"))# Set up required headersassert"sessionid"inapi_client.cookies# managed by ApiClient automaticallyapi_client.set_default_header("X-CSRFToken",api_client.cookies["csrftoken"].value)api_client.set_default_header("Origin",api_client.build_origin_header())
This authentication option is deprecated and will be removed in future.
fromcvat_sdk.api_clientimportmodels(auth,_)=api_client.auth_api.create_login(models.LoginSerializerExRequest(username="username",password="password"))api_client.set_default_header("Authorization","Token "+auth.key)
API wrappers
API endpoints are grouped by tags into separate classes in thecvat_sdk.api_client.apis
package.
APIs can be accessed asApiClient
object members:
api_client.auth_api.<operation>(...)api_client.tasks_api.<operation>(...)
And APIs can be instantiated directly like this:
fromcvat_sdk.api_clientimportApiClient,apisapi_client=ApiClient(...)auth_api=apis.AuthApi(api_client)auth_api.<operation>(...)tasks_api=apis.TasksApi(api_client)tasks_api.<operation>(...)
For each operation, the API wrapper class has a corresponding<operation>_endpoint
member.This member represents the endpoint as a first-class object, which provides metainformationabout the endpoint, such as the relative URL of the endpoint, parameter names,types and their placement in the request. It also allows to pass the operation to otherfunctions and invoke it from there.
For a typical server entity likeTask
,Project
,Job
etc., the*Api
classes provide methodsthat reflect Create-Read-Update-Delete (CRUD) operations:create
,retrieve
,list
,update
,partial_update
,delete
. The set of available operations depends on the entity type.
You can find the list of the available APIs and their documentationhere.
Models
Requests and responses can include data. It can be represented as plain Pythondata structures and model classes (or models). In CVAT API, model for requests and responsesare separated: the request models have theRequest
suffix in the name, while the responsemodels have no suffix. Models can be found in thecvat_sdk.api_client.models
package.
Models can be instantiated like this:
fromcvat_sdk.api_clientimportmodelsuser_model=models.User(...)
Model parameters can be passed as models, or as plain Python data structures. This rule appliesrecursively, starting from the method parameters. In particular, this means you can passa dict into a method or into a model constructor, and corresponding fields willbe parsed from this data automatically:
task_spec=models.TaskWriteRequest(name='example task',labels=[models.PatchedLabelRequest(name="car",color="#ff00ff",attributes=[model.AttributeRequest(name="a",mutable=True,input_type="number",default_value="5",values=["4","5","6"])])],)api_client.tasks_api.create(task_spec)
Is equivalent to:
api_client.tasks_api.create({'name':'example task',"labels":[{"name":"car","color":"#ff00ff","attributes":[{"name":"a","mutable":True,"input_type":"number","default_value":"5","values":["4","5","6"]}]}],})
You can mix these variants.
Most models provide corresponding interface classes called likeI<model name>
. They can beused to implement your own classes or describe APIs. They just provide type annotationsand descriptions for model fields.
You can export model values to plain Python dicts using theto_dict()
method andthecvat_sdk.api_client.model_utils.to_json()
function.
You can find the list of the available models and their documentationhere.
Sending requests
To send a request to a server endpoint, you need to obtain an instance of the corresponding*Api
class. You can find summary about available API classes and supported endpointshere. The*Api
instance object allows to send requests to the relevantserver endpoints.
By default, all operations return 2 objects: the parsed response data and the response itself.
The first returned value is a model parsed from the response data. If a method doesnot have any return value,None
is always returned as the first value. You can controlautomatic parsing using the_parse_response
method kwarg. When disabled,None
is returned.
The second value is the raw response, which can be useful to get response parameters, such asstatus code, headers, or raw response data. By default, the status code of the response ischecked to be positive. In the case of request failure, an exception is raised by default.This behavior can be controlled by the_check_status
method kwarg. If the status is notchecked, you will need to manually check the response status code and perform actions needed.
A typical endpoint call looks like this:
fromcvat_sdk.api_clientimportApiClient,apiswithApiClient(...)asapi_client:...(data,response)=api_client.tasks_api.list()# process the response ...
Operation parameters can be passed as positional or keyword arguments. API methods provideextra common arguments which control invocation logic:
_parse_response
(bool
) - Allows to enable and disable response data parsing. When enabled,the response data is parsed into a model or a basic type and returned as the first value.When disabled, the response is not parsed, andNone
is returned. Can be useful,for instance, if you need to parse data manually, or if you expect an error in the response.Default isTrue
._check_status
(bool
) - Allows to enable or disable response status checks. When enabled, theresponse status code is checked to be positive as defined in theHTTP standards.In the case of negative status, an exception is raised. Default isTrue
._validate_inputs
(bool
): specifies if type checking should be done on the datasent to the server. Default isTrue
._validate_outputs
(bool
): specifies if type checking should be done on the datareceived from the server. Default isTrue
._request_timeout
(None | int | float | Tuple[int | float, int | float]
) -Allows to control timeouts. If one number is provided, it will be the total request timeout. It can alsobe a tuple with (connection, read) timeouts. Default isNone
, which means no timeout._content_type
(None | str
) - Allows to specify theContent-Type
header valuefor the request. Endpoints can support different content types and behave differentlydepending on the value. For file uploads_content_type="multipart/form-data"
must be specified.Read more about file uploadshere. Default isapplication/json
.
Note
The API is autogenerated. In some cases the server API schema may be incompleteor underspecified. Please report to us all the problems found. A typical problem is that aresponse data can’t be parsed automatically due to the incorrect schema. In this case, thesimplest workaround is to disable response parsing using the_parse_response=False
method argument.You can find many examples of API client usage in REST API testshere.
Organizations
To create resource in the context of an organization, use one of these method arguments:
org
- The unique organization slugorg_id
- The organization id
...(task,response)=api_client.tasks_api.create(task_spec,org_id=org_id)
Paginated responses
There are several endpoints that allow to request multiple server entities. Typically, theseendpoints are calledlist_...
. When there are lots of data, the responses can be paginated toreduce server load. If an endpoint returns paginated data, a single page is returned per request.In some cases all entries need to be retrieved. CVAT doesn’t provide specific API or parametersfor this, so the solution is to write a loop to collect and join data from multiple requests.SDK provides an utility function for this atcvat_sdk.core.helpers.get_paginated_collection()
.
Example:
fromcvat_sdk.core.helpersimportget_paginated_collection...project_tasks=get_paginated_collection(api_client.projects_api.list_tasks_endpoint,id=project_id,)
Binary data in requests and responses
At the moment, sending and receiving binary data - such as files - can be difficult via thelow-level SDK API. Please use the following recommendations.
Sending data
By default, requests use theapplication/json
content type, which is a text type.However, it’s inefficient to send binary data in this encoding, and the data passedwon’t be converted automatically. If you need to send files or other binary data,please specify_content_type="multipart/form-data"
in the request parameters:
Example:
(result,response)=api_client.tasks_api.create_data(id=42,data_request=models.DataRequest(client_files=[open("image.jpg",'rb')],image_quality=70,),_content_type="multipart/form-data",# required)
Please also note that if there are complex fields in the data (such as nested lists or dicts),they, in turn, cannot be encoded asmultipart/form-data
, so the recommended solution is tosplit fields into files and others, and send them in different requests with different contenttypes:
Example:
data={'client_files':[...],# a list of binary files'image_quality':...,# a simple type - int'job_file_mapping':[...],# a complex type - list}# Initialize uploadingapi_client.tasks_api.create_data(id=42,data_request=models.DataRequest(image_quality=data["image_quality"]),upload_start=True,)# Upload binary dataapi_client.tasks_api.create_data(id=42,data_request=models.DataRequest(client_files=data.pop("client_files"),image_quality=data["image_quality"],),upload_multiple=True,_content_type="multipart/form-data",)# Finalize the uploading and send the remaining fieldsapi_client.tasks_api.create_data(id=42,data_request=models.DataRequest(**data),upload_finish=True,)
Receiving data
Receiving binary files can also be difficult with the low-level API. To avoid unexpectedbehavior, it is recommended to specify_parse_response=False
in the request parameters.In this case, SDK will not try to parse models from responses, and the response datacan be fetched directly from the response:
importjsonfromhttpimportHTTPStatusfromtimeimportsleepfromurllib.parseimportparse_qsl,urlparsefromcvat_sdk.api_clientimportApiClient,Configuration,modelsinterval=1withApiClient(configuration=Configuration(host="<cvat_host>",username="<username>",password="<password>"))asapi_client:# Initiate the process to export a task as a dataset(_,response)=api_client.tasks_api.create_dataset_export(id=task_id,format="COCO 1.0",save_images=True,_parse_response=False,)assertresponse.status==HTTPStatus.ACCEPTED# Obtain the background request ID from the server responserq_id=json.loads(response.data).get("rq_id")assertrq_id,"The rq_id parameter was not found in the server response"# Check the status of the background processwhileTrue:(background_request,response)=api_client.requests_api.retrieve(rq_id)assertresponse.status==HTTPStatus.OKprocess_status=background_request.status.valueifprocess_statusin(models.RequestStatus.allowed_values[("value",)]["FINISHED"],models.RequestStatus.allowed_values[("value",)]["FAILED"],):breaksleep(interval)ifprocess_status!=models.RequestStatus.allowed_values[("value",)]["FINISHED"]:exception_msg=f"Export failed with status:{process_status}"ifbackground_request.message:exception_msg+=f". Details:{background_request.message}"assertFalse,exception_msg# Download a prepared fileresult_url=background_request.result_urlassertresult_url,"No 'result_url' in the server response"parsed_result_url=urlparse(result_url)query_params=parse_qsl(parsed_result_url.query)_,response=api_client.call_api(parsed_result_url.path,method="GET",query_params=query_params,auth_settings=api_client.configuration.auth_settings(),_parse_response=False,)# Save the resulting filewithopen("output_file.zip","wb")asoutput_file:while(chunk:=response.read(8192)):output_file.write(chunk)
Different versions of API endpoints
The cloudstorages/id/content REST API endpoint
Warning
Theretrieve_content
method ofcloudstorages_api
will be deprecated in 2.5.0 version.We recommend usingretrieve_content_v2
method that matches to revised API when using SDK.For backward compatibility, we continue to support the prior interface version until version 2.6.0 is released.Here you can find the example how to get the bucket content using new methodretrieve_content_v2
.
frompprintimportpprintfromcvat_sdk.api_clientimportApiClient,Configurationnext_token=Nonefiles,prefixes=[],[]prefix=""withApiClient(configuration=Configuration(host=BASE_URL,username=user,password=password))asapi_client:whileTrue:data,response=api_client.cloudstorages_api.retrieve_content_v2(cloud_storage_id,**({"prefix":prefix}ifprefixelse{}),**({"next_token":next_token}ifnext_tokenelse{}),)# the data will have the following structure:# {'content': [# {'mime_type': <image|video|archive|pdf|DIR>, 'name': <name>, 'type': <REG|DIR>},# ],# 'next': <next_token_string|None>}files.extend([prefix+f["name"]forfindata["content"]ifstr(f["type"])=="REG"])prefixes.extend([prefix+f["name"]forfindata["content"]ifstr(f["type"])=="DIR"])next_token=data["next"]ifnext_token:continueifnotlen(prefixes):breakprefix=f"{prefixes.pop()}/"pprint(files)# ['sub/image_1.jpg', 'image_2.jpg']