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.Configurationobject 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*Apiclass. 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=Falsemethod 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 slug
  • org_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']