Skip to content

Tags

Info

Tag methods are accessed through the tag public member of your TickTickClient instance.

# Assumes that 'client' is the name that references the TickTickClient instance.

tag = client.tag.method()

Question About Logging In or Other Functionality Available?

API and Important Information

Tip

All supported methods are documented below with usage examples, take a look!

All usage examples assume that client is the name referencing the TickTickClient instance

Example TickTick Tag Dictionary

Members

Descriptions

It is possible that not all possible fields are present in the table.

Property Description Example Value Type Useful Values
name The lowercase of label 'books' str N/A
label The uppercase of name 'Books' str N/A
sortOrder A sort ID relative to other tags. 2748779069440 int N/A
sortType Sort type of the tag dueDate str dueDate, project, title, priority
color Hex color code string #4AA6EF str N/A
etag Etag identifier. 'ji35exmv' str N/A
parent name field of the parent tag 'friends' str N/A
{'name': 'test',
'label': 'Test',
'sortOrder': 2748779069440,
'sortType': 'project',
'color': '#4AA6EF',
'etag': 'zxdvlhqd',
'parent': 'friends'}

Sort Dictionary

SORT_DICTIONARY

The sort dictionary maps integers to the different sort types possible for tags. It is a public member called SORT_DICTIONARY available through the tag public member of the TickTickClient instance.

Descriptions
Value Sort Type
0 'project'
1 'dueDate'
2 'title'
3 'priority'

TagsManager

Handles all interactions for tags.

builder(self, label, color='random', parent=None, sort=None)

Creates and returns a local tag object. Helper method for create to make batch creating projects easier.

Note

The parent tag must already exist prior to calling this method.

Parameters:

Name Type Description Default
label str

Desired label of the tag - tag labels cannot be repeated.

required
color str

Hex color string. A random color will be generated if no color is specified.

'random'
parent str

The label of the parent tag if desired (include capitals in the label if it exists).

None
sort int

The desired sort type of the tag. Valid integer values are present in the sort dictionary. The default sort value will be by 'project'

None

Returns:

Type Description
dict

A dictionary containing all the fields necessary to create a tag remotely.

Exceptions:

Type Description
TypeError

If any of the types of the arguments are wrong.

ValueError

Tag label already exists.

ValueError

Parent tag does not exist.

ValueError

The hex string color inputted is invalid.

Example

tag_name = 'Books'  # The name for our tag
parent_name = 'Productivity'  # The desired parent tag -> this should already exist.
color_code = '#1387c4'
sort_type = 1  # Sort by `dueDate`
tag_object = client.tag.builder(tag_name, parent=parent_name, color=color_code, sort=sort_type)
Result

The required fields to create a tag object are created and returned in a dictionary.

{'label': 'Fiction', 'color': '#1387c4', 'parent': 'books', 'sortType': 'dueDate', 'name': 'fiction'}
Source code in managers/tags.py
def builder(self,
            label: str,
            color: str = 'random',
            parent: str = None,
            sort: int = None
            ) -> dict:
    """
    Creates and returns a local tag object. Helper method for [create][managers.tags.TagsManager.create]
    to make batch creating projects easier.

    !!! note
        The parent tag must already exist prior to calling this method.

    Arguments:
        label: Desired label of the tag - tag labels cannot be repeated.
        color: Hex color string. A random color will be generated if no color is specified.
        parent: The label of the parent tag if desired (include capitals in the label if it exists).
        sort: The desired sort type of the tag. Valid integer values are present in the [sort dictionary](tags.md#sort-dictionary). The default
            sort value will be by 'project'

    Returns:
        A dictionary containing all the fields necessary to create a tag remotely.

    Raises:
        TypeError: If any of the types of the arguments are wrong.
        ValueError: Tag label already exists.
        ValueError: Parent tag does not exist.
        ValueError: The hex string color inputted is invalid.

    !!! example
        ```python
        tag_name = 'Books'  # The name for our tag
        parent_name = 'Productivity'  # The desired parent tag -> this should already exist.
        color_code = '#1387c4'
        sort_type = 1  # Sort by `dueDate`
        tag_object = client.tag.builder(tag_name, parent=parent_name, color=color_code, sort=sort_type)
        ```

        ??? success "Result"
            The required fields to create a tag object are created and returned in a dictionary.

            ```python
            {'label': 'Fiction', 'color': '#1387c4', 'parent': 'books', 'sortType': 'dueDate', 'name': 'fiction'}
            ```
    """
    # Perform checks
    return self._check_fields(label, color=color, parent_label=parent, sort=sort)

color(self, label, color)

Change the color of a tag. For batch changing colors, see update.

Parameters:

Name Type Description Default
label str

The label of the tag to be changed.

required
color str

The new desired hex color string.

required

Returns:

Type Description
dict

The updated tag dictionary object.

Exceptions:

Type Description
TypeError

If label or color are not strings.

ValueError

If the tag label does not exist.

ValueError

If color is not a valid hex color string.

RuntimeError

If changing the color was not successful.

Changing a Tag's Color

# Lets assume that we have a tag named "Movies" that we want to change the color for.
new_color = '#134397'
movies_updated = client.tag.color('Movies', new_color)
Result

The updated tag dictionary object is returned.

{'name': 'movies', 'label': 'Movies', 'sortOrder': 0, 'sortType': 'project', 'color': '#134397', 'etag': 'wwb49yfr'}

Before

image

After

image

Source code in managers/tags.py
def color(self, label: str, color: str) -> dict:
    """
    Change the color of a tag. For batch changing colors, see [update][managers.tags.TagsManager.update].

    Arguments:
        label: The label of the tag to be changed.
        color: The new desired hex color string.

    Returns:
        The updated tag dictionary object.

    Raises:
        TypeError: If `label` or `color` are not strings.
        ValueError: If the tag `label` does not exist.
        ValueError: If `color` is not a valid hex color string.
        RuntimeError: If changing the color was not successful.

    !!! example "Changing a Tag's Color"
        ```python
        # Lets assume that we have a tag named "Movies" that we want to change the color for.
        new_color = '#134397'
        movies_updated = client.tag.color('Movies', new_color)
        ```

        ??? success "Result"
            The updated tag dictionary object is returned.

            ```python
            {'name': 'movies', 'label': 'Movies', 'sortOrder': 0, 'sortType': 'project', 'color': '#134397', 'etag': 'wwb49yfr'}
            ```

            **Before**

            ![image](https://user-images.githubusercontent.com/56806733/104661749-0eda7d00-567e-11eb-836f-3a8851bcf9a5.png)

            **After**

            ![image](https://user-images.githubusercontent.com/56806733/104661860-55c87280-567e-11eb-93b5-054fa4f1104a.png)
    """
    if not isinstance(label, str) or not isinstance(color, str):
        raise TypeError('Label and Color Must Be Strings')

    # Get the object
    label = label.lower()
    obj = self._client.get_by_fields(name=label, search='tags')
    if not obj:
        raise ValueError(f"Tag '{label}' Does Not Exist To Update")

    # Check the color
    if not check_hex_color(color):
        raise ValueError(f"Hex Color String '{color}' Is Not Valid")

    obj['color'] = color  # Set the color

    url = self._client.BASE_URL + 'batch/tag'
    payload = {
        'update': [obj]
    }
    response = self._client.http_post(url, json=payload, cookies=self._client.cookies, headers=self.headers)
    self._client.sync()
    return self._client.get_by_etag(response['id2etag'][obj['name']])

create(self, label, color='random', parent=None, sort=None)

Creates a tag remotely. Supports single tag creation or batch tag creation.

Tip

Allows creation with a label that may normally not be allowed by TickTick for tags.

Normal TickTick excluded characters are: \ / " # : * ? < > | Space

Parameters:

Name Type Description Default
label str or list

Single Tag (str): The desired label of the tag. Tag labels cannot be repeated.

Multiple Tags (list): A list of tag objects created using the builder method.

required
color str

Hex color string. A random color will be generated if no color is specified.

'random'
parent str

The label of the parent tag if desired (include capitals in if it exists).

None
sort int

The desired sort type of the tag. Valid integer values are present in the sort dictionary. The default sort value will be by 'project'

None

Returns:

Type Description
dict or list

Single Tag (dict): The created tag object dictionary.

Multiple Tags (list): A list of the created tag object dictionaries.

Exceptions:

Type Description
TypeError

If any of the types of the arguments are wrong.

ValueError

Tag label already exists.

ValueError

Parent tag does not exist.

ValueError

The hex string color inputted is invalid.

RuntimeError

The tag(s) could not be created.

Single Tag

tag = client.tag.create('Fun')
Result

The tag object dictionary is returned.

{'name': 'fun', 'label': 'Fun', 'sortOrder': 0, 'sortType': 'project', 'color': '#9b69f3', 'etag': '7fc8zb58'}
Our tag is created.

image

A random color can be generated using generate_hex_color. However, just not specifying a color will automatically generate a random color (as seen in the previous tab) You can always specify the color that you want.

tag = client.tag.create('Fun', color='#86bb6d')
Result

The tag object dictionary is returned and our project is created with the color specified.

{'name': 'fun', 'label': 'Fun', 'sortOrder': 0, 'sortType': 'project', 'color': '#86bb6d', 'etag': '8bzzdws3'}
image

Tags can be nested one level. To create a tag that is nested, include the label of the parent tag. The parent tag should already exist.

tag = client.tag.create('Fun', parent='Hobbies')
Result

The tag object dictionary is returned and our tag is created nested under the parent tag.

{'name': 'fun', 'label': 'Fun', 'sortOrder': 0, 'sortType': 'project', 'color': '#d2a6e4', 'etag': 'nauticx1', 'parent': 'hobbies'}

Before

image

After

image

You can specify the sort type of the created tag using integer values from the sort dictionary.

tag = client.tag.create('Fun', sort=2)  # Sort by `title`
Result

The tag object dictionary is returned and our tag has the specified sort type.

{'name': 'fun', 'label': 'Fun', 'sortOrder': 0, 'sortType': 'title', 'color': '#e7e7ba', 'etag': 'n4k3pezc'}

image

Multiple Tag Creation (batch)

To create multiple tags, build the tag objects first using the builder method. Pass in a list of the project objects to create them remotely.

parent_tag = client.tag.create('Hobbies')  # Create a parent tag.
# We will create tag objects using builder that will be nested under the parent tag
fun_tag = client.tag.builder('Fun', sort=2, parent='Hobbies')
read_tag = client.tag.builder('Read', color='#d2a6e4', parent='Hobbies')
movie_tag = client.tag.builder('Movies', parent='Hobbies')
# Create the tags
tag_list = [fun_tag, read_tag, movie_tag]
created_tags = client.tag.create(tag_list)
Result

The tag object dictionaries are returned in a list.

[{'name': 'fun', 'label': 'Fun', 'sortOrder': 0, 'sortType': 'title', 'color': '#172d1c', 'etag': '1tceclp4', 'parent': 'hobbies'},

{'name': 'read', 'label': 'Read', 'sortOrder': 0, 'sortType': 'project', 'color': '#d2a6e4', 'etag': 'ykdem8dg', 'parent': 'hobbies'},

{'name': 'movies', 'label': 'Movies', 'sortOrder': 0, 'sortType': 'project', 'color': '#94a5f8', 'etag': 'o0nifkbv', 'parent': 'hobbies'}]

image

Source code in managers/tags.py
def create(self,
           label,
           color: str = 'random',
           parent: str = None,
           sort: int = None
           ):
    """
    Creates a tag remotely. Supports single tag creation or batch tag creation.

    !!! tip
        Allows creation with a label that may normally not be allowed by `TickTick` for tags.

        Normal `TickTick` excluded characters are: \\ / " # : * ? < > | Space

    Arguments:
        label (str or list):
            **Single Tag (str)**: The desired label of the tag. Tag labels cannot be repeated.

            **Multiple Tags (list)**: A list of tag objects created using the [builder][managers.tags.TagsManager.builder] method.
        color: Hex color string. A random color will be generated if no color is specified.
        parent: The label of the parent tag if desired (include capitals in if it exists).
        sort: The desired sort type of the tag. Valid integer values are present in the [sort dictionary](tags.md#sort-dictionary). The default
            sort value will be by 'project'

    Returns:
        dict or list:
        **Single Tag (dict)**: The created tag object dictionary.

        **Multiple Tags (list)**: A list of the created tag object dictionaries.

    Raises:
        TypeError: If any of the types of the arguments are wrong.
        ValueError: Tag label already exists.
        ValueError: Parent tag does not exist.
        ValueError: The hex string color inputted is invalid.
        RuntimeError: The tag(s) could not be created.

    !!! example "Single Tag"

        === "Just A Label"
            ```python
            tag = client.tag.create('Fun')
            ```

            ??? success "Result"
                The tag object dictionary is returned.

                ```python
                {'name': 'fun', 'label': 'Fun', 'sortOrder': 0, 'sortType': 'project', 'color': '#9b69f3', 'etag': '7fc8zb58'}
                ```
                Our tag is created.

                ![image](https://user-images.githubusercontent.com/56806733/104658773-5bbb5500-5678-11eb-9d44-27214203d70e.png)

        === "Specify a Color"
            A random color can be generated using [generate_hex_color][helpers.hex_color.generate_hex_color].
            However, just not specifying a color will automatically generate a random color (as seen in the previous tab)
            You can always specify the color that you want.

            ```python
            tag = client.tag.create('Fun', color='#86bb6d')
            ```

            ??? success "Result"
                The tag object dictionary is returned and our project is created with the color specified.

                ```python
                {'name': 'fun', 'label': 'Fun', 'sortOrder': 0, 'sortType': 'project', 'color': '#86bb6d', 'etag': '8bzzdws3'}
                ```
                ![image](https://user-images.githubusercontent.com/56806733/104659184-0c295900-5679-11eb-9f3c-2cd154c0500c.png)

        === "Specifying a Parent Tag"
            Tags can be nested one level. To create a tag that is nested, include the label of the parent tag.
            The parent tag should already exist.

            ```python
            tag = client.tag.create('Fun', parent='Hobbies')
            ```

            ??? success "Result"
                The tag object dictionary is returned and our tag is created nested under the parent tag.

                ```python
                {'name': 'fun', 'label': 'Fun', 'sortOrder': 0, 'sortType': 'project', 'color': '#d2a6e4', 'etag': 'nauticx1', 'parent': 'hobbies'}
                ```

                **Before**

                ![image](https://user-images.githubusercontent.com/56806733/104659785-24e63e80-567a-11eb-9a62-01ebca55e649.png)

                **After**

                ![image](https://user-images.githubusercontent.com/56806733/104659814-33ccf100-567a-11eb-8dca-c91aea68b4c7.png)

        === "Sort Type"
            You can specify the sort type of the created tag using integer values from the [sort dictionary](#sort-dictionary).

            ```python
            tag = client.tag.create('Fun', sort=2)  # Sort by `title`
            ```

            ??? success "Result"
                The tag object dictionary is returned and our tag has the specified sort type.

                ```python
                {'name': 'fun', 'label': 'Fun', 'sortOrder': 0, 'sortType': 'title', 'color': '#e7e7ba', 'etag': 'n4k3pezc'}
                ```

                ![image](https://user-images.githubusercontent.com/56806733/104660156-e4d38b80-567a-11eb-8c61-8fb874a515a2.png)

    !!! example "Multiple Tag Creation (batch)"
        To create multiple tags, build the tag objects first using the [builder][managers.projects.ProjectManager.builder] method. Pass
        in a list of the project objects to create them remotely.

        ```python
        parent_tag = client.tag.create('Hobbies')  # Create a parent tag.
        # We will create tag objects using builder that will be nested under the parent tag
        fun_tag = client.tag.builder('Fun', sort=2, parent='Hobbies')
        read_tag = client.tag.builder('Read', color='#d2a6e4', parent='Hobbies')
        movie_tag = client.tag.builder('Movies', parent='Hobbies')
        # Create the tags
        tag_list = [fun_tag, read_tag, movie_tag]
        created_tags = client.tag.create(tag_list)
        ```

        ??? success "Result"
            The tag object dictionaries are returned in a list.

            ```python
            [{'name': 'fun', 'label': 'Fun', 'sortOrder': 0, 'sortType': 'title', 'color': '#172d1c', 'etag': '1tceclp4', 'parent': 'hobbies'},

            {'name': 'read', 'label': 'Read', 'sortOrder': 0, 'sortType': 'project', 'color': '#d2a6e4', 'etag': 'ykdem8dg', 'parent': 'hobbies'},

            {'name': 'movies', 'label': 'Movies', 'sortOrder': 0, 'sortType': 'project', 'color': '#94a5f8', 'etag': 'o0nifkbv', 'parent': 'hobbies'}]
            ```

            ![image](https://user-images.githubusercontent.com/56806733/104660625-cb7f0f00-567b-11eb-8649-68646870ccfa.png)
    """
    batch = False  # Bool signifying batch create or not
    if isinstance(label, list):
        # Batch tag creation triggered
        obj = label  # Assuming all correct objects
        batch = True
    else:
        if not isinstance(label, str):
            raise TypeError('Required Positional Argument Must Be A String or List of Tag Objects')
        # Create a single object
        obj = self.builder(label=label, color=color, parent=parent, sort=sort)

    if not batch:
        obj = [obj]

    url = self._client.BASE_URL + 'batch/tag'
    payload = {'add': obj}
    response = self._client.http_post(url, json=payload, cookies=self._client.cookies, headers=self.headers)
    self._client.sync()

    if not batch:
        return self._client.get_by_etag(self._client.parse_etag(response), search='tags')
    else:
        etag = response['id2etag']
        etag2 = list(etag.keys())  # Tag names are out of order
        labels = [x['name'] for x in obj]  # Tag names are in order
        items = [''] * len(obj)  # Create enough spots for the objects
        for tag in etag2:
            index = labels.index(tag)  # Object of the index is here
            actual_etag = etag[tag]  # Get the actual etag
            found = self._client.get_by_etag(actual_etag, search='tags')
            items[index] = found  # Place at the correct index
        if len(items) == 1:
            return items[0]
        else:
            return items

delete(self, label)

Delete tag(s). Supports single tag deletion and "mock" batch tag deletion.

Info

Batch deleting for tags is not supported by TickTick. However, passing in a list of labels to delete will "mock" batch deleting - but individual requests will have to be made for each deletion.

Parameters:

Name Type Description Default
label str or list

Single Tag (str): The label of the tag.

Multiple Tags (list): A list of tag label strings.

required

Returns:

Type Description
dict or list

Single Tag (dict): The dictionary object of the deleted tag.

Multiple Tags (list): The dictionary objects of the deleted tags in a list.

Exceptions:

Type Description
TypeError

If label is not a string or list.

ValueError

If a label does not exist.

RuntimeError

If the tag could not be deleted successfully.

Tag Deletion

Deleting a single tag requires passing in the label string of the tag.

# Lets delete a tag named "Fun"
delete_tag = client.tag.delete("Fun")
Result

The dictionary object of the deleted tag returned.

{'name': 'fun', 'label': 'Fun', 'sortOrder': -3298534883328, 'sortType': 'project', 'color': '#A9949E', 'etag': '32balm5l'}

Before

"Fun" Tag Exists

image

After

"Fun" Tag Does Not Exist

image

Deleting multiple tags requires passing the label strings of the tags in a list.

# Lets delete tags named "Fun", "Movies", and "Hobbies"
delete_labels = ["Fun", "Movies", "Hobbies"]
deleted_tags = client.tag.delete(delete_labels)
Result

The dictionary object of the deleted tags returned in a list.

[{'name': 'fun', 'label': 'Fun', 'sortOrder': -3848290697216, 'sortType': 'project', 'color': '#FFD966', 'etag': '56aa6dva'},

{'name': 'movies', 'label': 'Movies', 'sortOrder': -2748779069440, 'sortType': 'dueDate', 'color': '#134397', 'etag': 's0czro3e'},

{'name': 'hobbies', 'label': 'Hobbies', 'sortOrder': -2199023255552, 'sortType': 'project', 'color': '#ABA6B5', 'etag': 'shu2xbvq'}]

Before

All three tags exist.

image

After

All three tags don't exist.

image

Source code in managers/tags.py
def delete(self, label):
    """
    Delete tag(s). Supports single tag deletion and "mock" batch tag deletion.

    !!! info
        Batch deleting for tags is not supported by TickTick. However, passing in
        a list of labels to delete will "mock" batch deleting - but individual requests
        will have to be made for each deletion.

    Arguments:
        label (str or list):
            **Single Tag (str)**: The label of the tag.

            **Multiple Tags (list)**: A list of tag label strings.

    Returns:
        dict or list:
        **Single Tag (dict)**: The dictionary object of the deleted tag.

        **Multiple Tags (list)**: The dictionary objects of the deleted tags in a list.

    Raises:
        TypeError: If `label` is not a string or list.
        ValueError: If a label does not exist.
        RuntimeError: If the tag could not be deleted successfully.

    !!! example "Tag Deletion"
        === "Single Tag Deletion"
            Deleting a single tag requires passing in the label string of the tag.

            ```python
            # Lets delete a tag named "Fun"
            delete_tag = client.tag.delete("Fun")
            ```

            ??? success "Result"
                The dictionary object of the deleted tag returned.

                ```python
                {'name': 'fun', 'label': 'Fun', 'sortOrder': -3298534883328, 'sortType': 'project', 'color': '#A9949E', 'etag': '32balm5l'}
                ```

                **Before**

                "Fun" Tag Exists

                ![image](https://user-images.githubusercontent.com/56806733/104668024-2c164800-568c-11eb-853e-5b7eba1f4528.png)

                **After**

                "Fun" Tag Does Not Exist

                ![image](https://user-images.githubusercontent.com/56806733/104667768-ac887900-568b-11eb-9bfb-597c752e4c3b.png)

        === "Multiple Tag Deletion"
            Deleting multiple tags requires passing the label strings of the tags in a list.

            ```python
            # Lets delete tags named "Fun", "Movies", and "Hobbies"
            delete_labels = ["Fun", "Movies", "Hobbies"]
            deleted_tags = client.tag.delete(delete_labels)
            ```

            ??? success "Result"

                The dictionary object of the deleted tags returned in a list.

                ```python
                [{'name': 'fun', 'label': 'Fun', 'sortOrder': -3848290697216, 'sortType': 'project', 'color': '#FFD966', 'etag': '56aa6dva'},

                {'name': 'movies', 'label': 'Movies', 'sortOrder': -2748779069440, 'sortType': 'dueDate', 'color': '#134397', 'etag': 's0czro3e'},

                {'name': 'hobbies', 'label': 'Hobbies', 'sortOrder': -2199023255552, 'sortType': 'project', 'color': '#ABA6B5', 'etag': 'shu2xbvq'}]
                ```

                **Before**

                All three tags exist.

                ![image](https://user-images.githubusercontent.com/56806733/104668135-61bb3100-568c-11eb-8707-314deb42cd1d.png)

                **After**

                All three tags don't exist.

                ![image](https://user-images.githubusercontent.com/56806733/104668185-7b5c7880-568c-11eb-8da0-aaee68d53500.png)

    """
    # Determine if the tag exists
    if not isinstance(label, str) and not isinstance(label, list):
        raise TypeError('Label Must Be A String or List Of Strings')

    url = self._client.BASE_URL + 'tag'
    if isinstance(label, str):
        label = [label]  # If a singular string we are going to add it to a list

    objects = []
    for lbl in label:
        if not isinstance(lbl, str):
            raise TypeError(f"'{lbl}' Must Be A String")
        lbl = lbl.lower()
        tag_obj = self._client.get_by_fields(name=lbl, search='tags')  # Get the tag object
        if not tag_obj:
            raise ValueError(f"Tag '{lbl}' Does Not Exist To Delete")
        # We can assume that only one tag has the name
        params = {
            'name': tag_obj['name']
        }
        response = self._client.http_delete(url, params=params, cookies=self._client.cookies, headers=self.headers)
        # Find the tag in the tags list and delete it, then return the deleted object
        objects.append(self._client.delete_from_local_state(search='tags', etag=tag_obj['etag']))
    self._client.sync()
    if len(objects) == 1:
        return objects[0]
    else:
        return objects

merge(self, label, merged)

Merges the tasks of the passed tags into the argument merged and deletes all the tags except merged Args can be individual label strings, or a list of strings

Parameters:

Name Type Description Default
label str or list

Single Tag (str): The label string of the tag to merge.

Multiple Tags (list): The label strings of the tags to merge in a list.

required
merged str

The label of the tag that will remain after the merge.

required

Returns:

Type Description
dict

The tag dictionary object that remains after the merge.

Exceptions:

Type Description
TypeError

If merged is not a str or if label is not a str or list.

ValueError

If any of the labels do not exist.

RuntimeError

If the merge could not be successfully completed.

Merging Tags

Merging two tags requires the label of the tag that you want kept after the merge, and the label of the tag that will be merged.

Lets assume that we have two tags: "Work" and "School". I want to merge the tag "School" into "Work". What should happen is that any tasks that are tagged "School", will be updated to have the tag "Work", and the "School" tag will be deleted.

merged_tags = client.tag.merge("School", "Work")
Result

The tag that remains after the merge is returned.

{'name': 'work', 'label': 'Work', 'sortOrder': 2199023255552, 'sortType': 'project', 'color': '#3876E4', 'etag': 'eeh8zrup'}

Before

"School" has two tasks that have it's tag.

image

"Work" has no tasks.

image

After

"School" has been deleted. The tasks that used to be tagged with "School" are now tagged with "Work".

image

Merging multiple tags into a single tag requires passing the labels of the tags to merge in a list.

Lets assume that we have three tags: "Work", "School", and "Hobbies" . I want to merge the tag "School" and the tag "Hobbies" into "Work". What should happen is that any tasks that are tagged with "School" or "Hobbies", will be updated to have the tag "Work", and the "School" and "Hobbies" tags will be deleted.

merge_tags = ["School", "Hobbies"]
result = client.tag.merge(merge_tags, "Work")
Result

The tag that remains after the merge is returned.

{'name': 'work', 'label': 'Work', 'sortOrder': 2199023255552, 'sortType': 'project', 'color': '#3876E4', 'etag': 'ke23lp06'}

Before

"School" has two tasks.

image

"Hobbies" has two tasks.

image

"Work" has one task.

image

After

"Work" has five tasks now, and the tags "School" and "Hobbies" have been deleted.

image

Source code in managers/tags.py
def merge(self, label, merged: str):
    """

    Merges the tasks of the passed tags into the argument `merged` and deletes all the tags except `merged`
    Args can be individual label strings, or a list of strings

    Arguments:
        label (str or list):
            **Single Tag (str)**: The label string of the tag to merge.

            **Multiple Tags (list)**: The label strings of the tags to merge in a list.
        merged: The label of the tag that will remain after the merge.

    Returns:
        dict: The tag dictionary object that remains after the merge.

    Raises:
        TypeError: If `merged` is not a str or if `label` is not a str or list.
        ValueError: If any of the labels do not exist.
        RuntimeError: If the merge could not be successfully completed.

    !!! example "Merging Tags"
        === "Merging Two Tags"
            Merging two tags requires the label of the tag that you want kept after the merge, and the
            label of the tag that will be merged.

            Lets assume that we have two tags: "Work" and "School". I want to merge the tag "School"
            into "Work". What should happen is that any tasks that are tagged "School", will be updated
            to have the tag "Work", and the "School" tag will be deleted.

            ```python
            merged_tags = client.tag.merge("School", "Work")
            ```

            ??? success "Result"
                The tag that remains after the merge is returned.

                ```python
                {'name': 'work', 'label': 'Work', 'sortOrder': 2199023255552, 'sortType': 'project', 'color': '#3876E4', 'etag': 'eeh8zrup'}
                ```

                **Before**

                "School" has two tasks that have it's tag.

                ![image](https://user-images.githubusercontent.com/56806733/104680244-45c38980-56a4-11eb-968d-884160c77247.png)

                "Work" has no tasks.

                ![image](https://user-images.githubusercontent.com/56806733/104680366-90dd9c80-56a4-11eb-975f-5e769e9ea491.png)

                **After**

                "School" has been deleted. The tasks that used to be tagged with "School" are now
                tagged with "Work".

                ![image](https://user-images.githubusercontent.com/56806733/104680576-0c3f4e00-56a5-11eb-9536-ef3a7fcf20ec.png)

        === "Merging Three Or More Tags"
            Merging multiple tags into a single tag requires passing the labels of the tags to merge in a list.

            Lets assume that we have three tags: "Work", "School", and "Hobbies" . I want to merge the tag "School"
            and the tag "Hobbies" into "Work". What should happen is that any tasks that are tagged with "School" or "Hobbies", will be updated
            to have the tag "Work", and the "School" and "Hobbies" tags will be deleted.

            ```python
            merge_tags = ["School", "Hobbies"]
            result = client.tag.merge(merge_tags, "Work")
            ```

            ??? success "Result"
                The tag that remains after the merge is returned.

                ```python
                {'name': 'work', 'label': 'Work', 'sortOrder': 2199023255552, 'sortType': 'project', 'color': '#3876E4', 'etag': 'ke23lp06'}
                ```

                **Before**

                "School" has two tasks.

                ![image](https://user-images.githubusercontent.com/56806733/104681135-7ad0db80-56a6-11eb-81dd-03e4a151cfd9.png)

                "Hobbies" has two tasks.

                ![image](https://user-images.githubusercontent.com/56806733/104681104-67257500-56a6-11eb-99b0-57bbb876a59e.png)

                "Work" has one task.

                ![image](https://user-images.githubusercontent.com/56806733/104681164-89b78e00-56a6-11eb-99a8-c85ef418d2a0.png)

                **After**

                "Work" has five tasks now, and the tags "School" and "Hobbies" have been deleted.

                ![image](https://user-images.githubusercontent.com/56806733/104681239-b7043c00-56a6-11eb-9b45-5522b9c69cb0.png)
    """
    # Make sure merged is a string
    if not isinstance(merged, str):
        raise ValueError('Merged Must Be A String')

    # Make sure label is a string or list
    if not isinstance(label, str) and not isinstance(label, list):
        raise ValueError(f"Label must be a string or a list.")

    # Lowercase merged
    merged = merged.lower()
    # Make sure merged exists
    kept_obj = self._client.get_by_fields(name=merged, search='tags')
    if not kept_obj:
        raise ValueError(f"Kept Tag '{merged}' Does Not Exist To Merge")

    merge_queue = []
    # Verify all args are valid, and add them to a list
    if isinstance(label, str):
        string = label.lower()
        # Make sure it exists
        retrieved = self._client.get_by_fields(name=string, search='tags')
        if not retrieved:
            raise ValueError(f"Tag '{label}' Does Not Exist To Merge")
        merge_queue.append(retrieved)
    else:
        for item in label:  # Loop through the items in the list and check items are a string and exist
            # Make sure the item is a string
            if not isinstance(item, str):
                raise ValueError(f"Item '{item}' Must Be A String")
            string = item.lower()
            # Make sure it exists
            found = self._client.get_by_fields(name=string, search='tags')
            if not found:
                raise ValueError(f"Tag '{item}' Does Not Exist To Merge")
            merge_queue.append(found)

    for labels in merge_queue:
        # Merge
        url = self._client.BASE_URL + 'tag/merge'
        payload = {
            'name': labels['name'],
            'newName': kept_obj['name']
        }
        self._client.http_put(url, json=payload, cookies=self._client.cookies, headers=self.headers)
    self._client.sync()

    return kept_obj

nesting(self, child, parent)

Update tag nesting. Move an already created tag to be nested underneath a parent tag - or ungroup an already nested tag.

Nesting Tags More Than One Level Does Not Work

Example

Parent Tag -> Level Zero
    Child Tag 1 -> Level One: This is the most nesting that is allowed by TickTick for tags.
        Child Tag 2 -> Level Two: Not allowed

Parameters:

Name Type Description Default
child str

Label of the tag to become the child

required
parent str

Label of the tag that will become the parent.

required

Returns:

Type Description
dict

The updated tag object dictionary.

Exceptions:

Type Description
TypeError

If child and parent are not strings

ValueError

If child does not exist to update.

ValueError

If parent does not exist.

RuntimeError

If setting the parent was unsuccessful.

Nesting

To nest a tag underneath another tag, pass in the labels of the child and parent.

# Lets assume that we have a tag named "Movies"
# We have another tag named "Hobbies" that we want to make the parent to "Movies"
child = "Movies"
parent = "Hobbies"
nesting_update = client.tag.nesting(child, parent)
Result

The updated child tag dictionary object is returned.

{'name': 'movies', 'label': 'Movies', 'sortOrder': 0, 'sortType': 'dueDate', 'color': '#134397', 'etag': 'ee34aft9', 'parent': 'hobbies'}

Before

image

After

image

If the tag is already nested, changing the parent is still no different.

# We have a tag named "Movies" that is already nested underneath "Hobbies"
# We want to nest "Movies" underneath the tag "Fun" instead.
child = "Movies"
parent = "Fun"
nesting_update = client.tag.nesting(child, parent)
Result

The updated child tag dictionary object is returned.

{'name': 'movies', 'label': 'Movies', 'sortOrder': 0, 'sortType': 'dueDate', 'color': '#134397', 'etag': '91qpuq71', 'parent': 'fun'}

Before

image

After

image

If the tag is nested and you want to ungroup it, pass in None for parent.

# We have a tag named "Movies" that is nested underneath "Fun"
# We don't want to have "Movies" nested anymore.
child = "Movies"
parent = None
nesting_update = client.tag.nesting(child, parent)
Result

The updated child tag dictionary object is returned.

{'name': 'movies', 'label': 'Movies', 'sortOrder': 0, 'sortType': 'dueDate', 'color': '#134397', 'etag': 'jcoc94p6'}

Before

image

After

image

Source code in managers/tags.py
def nesting(self, child: str, parent: str) -> dict:
    """
    Update tag nesting. Move an already created tag to be nested underneath a parent tag - or ungroup an already
    nested tag.

    !!! warning "Nesting Tags More Than One Level Does Not Work"
        !!! example

            === "Nesting Explanation"
                ```md
                Parent Tag -> Level Zero
                    Child Tag 1 -> Level One: This is the most nesting that is allowed by TickTick for tags.
                        Child Tag 2 -> Level Two: Not allowed
                ```

    Arguments:
        child: Label of the tag to become the child
        parent: Label of the tag that will become the parent.

    Returns:
        The updated tag object dictionary.

    Raises:
        TypeError: If `child` and `parent` are not strings
        ValueError: If `child` does not exist to update.
        ValueError: If `parent` does not exist.
        RuntimeError: If setting the parent was unsuccessful.

    !!! example "Nesting"

        === "Nesting A Tag"
            To nest a tag underneath another tag, pass in the labels of the child and parent.

            ```python
            # Lets assume that we have a tag named "Movies"
            # We have another tag named "Hobbies" that we want to make the parent to "Movies"
            child = "Movies"
            parent = "Hobbies"
            nesting_update = client.tag.nesting(child, parent)
            ```

            ??? success "Result"
                The updated child tag dictionary object is returned.

                ```python
                {'name': 'movies', 'label': 'Movies', 'sortOrder': 0, 'sortType': 'dueDate', 'color': '#134397', 'etag': 'ee34aft9', 'parent': 'hobbies'}
                ```

                **Before**

                ![image](https://user-images.githubusercontent.com/56806733/104665300-da6abf00-5685-11eb-947f-889187cec008.png)

                **After**

                ![image](https://user-images.githubusercontent.com/56806733/104665366-f706f700-5685-11eb-93eb-9316befec5fc.png)

        === "Changing The Parent Of An Already Nested Tag"
            If the tag is already nested, changing the parent is still no different.

            ```python
            # We have a tag named "Movies" that is already nested underneath "Hobbies"
            # We want to nest "Movies" underneath the tag "Fun" instead.
            child = "Movies"
            parent = "Fun"
            nesting_update = client.tag.nesting(child, parent)
            ```

            ??? success "Result"
                The updated child tag dictionary object is returned.

                ```python
                {'name': 'movies', 'label': 'Movies', 'sortOrder': 0, 'sortType': 'dueDate', 'color': '#134397', 'etag': '91qpuq71', 'parent': 'fun'}
                ```

                **Before**

                ![image](https://user-images.githubusercontent.com/56806733/104665599-ab088200-5686-11eb-8b36-5ee873289db7.png)

                **After**

                ![image](https://user-images.githubusercontent.com/56806733/104665821-35e97c80-5687-11eb-8098-426816970f3e.png)

        === "Un-grouping A Child Tag"
            If the tag is nested and you want to ungroup it, pass in `None` for `parent`.

            ```python
            # We have a tag named "Movies" that is nested underneath "Fun"
            # We don't want to have "Movies" nested anymore.
            child = "Movies"
            parent = None
            nesting_update = client.tag.nesting(child, parent)
            ```

            ??? success "Result"
                The updated child tag dictionary object is returned.

                ```python
                {'name': 'movies', 'label': 'Movies', 'sortOrder': 0, 'sortType': 'dueDate', 'color': '#134397', 'etag': 'jcoc94p6'}
                ```

                **Before**

                ![image](https://user-images.githubusercontent.com/56806733/104666038-be681d00-5687-11eb-8490-83c370977267.png)

                **After**

                ![image](https://user-images.githubusercontent.com/56806733/104666080-dcce1880-5687-11eb-9ca8-5abcdb4109ba.png)
    """
    if not isinstance(child, str):
        raise TypeError('Inputs Must Be Strings')

    if parent is not None:
        if not isinstance(parent, str):
            raise TypeError('Inputs Must Be Strings')

    # Get the object
    child = child.lower()
    obj = self._client.get_by_fields(name=child, search='tags')
    if not obj:
        raise ValueError(f"Tag '{child}' Does Not Exist To Update")

    # Four Cases
    # Case 1: No Parent -> Want a Parent
    # Case 2: No Parent -> Doesn't Want a Parent
    # Case 3: Has Parent -> Wants a Different Parent
    # Case 4: Has Parent -> Doesn't Want a Parent

    # Case 1: Determine if the object has a parent
    try:
        if obj['parent']:
            # It has a parent
            if parent is not None:  # Case 3
                # check if the parent is already the same, if it is just return
                if obj['parent'] == parent.lower():
                    return obj
                else:
                    new_p = parent.lower()
                    obj['parent'] = new_p
            else:
                new_p = obj['parent']  # Case 4
                obj['parent'] = ''
        elif obj['parent'] is None:
            raise ValueError('Parent Does Not Exist')

    except KeyError:
        # It does not have a parent
        if parent is not None:  # Wants a different parent
            new_p = parent.lower()  # -> Case 1
            obj['parent'] = new_p
        else:  # Doesn't want a parent -> Case 2
            return obj  # We don't have to do anything if no parent and doesn't want a parent

    # Have to find the project
    pobj = self._client.get_by_fields(name=new_p, search='tags')
    if not pobj:
        raise ValueError(f"Tag '{parent}' Does Not Exist To Set As Parent")

    url = self._client.BASE_URL + 'batch/tag'
    payload = {
        'update': [pobj, obj]
    }
    response = self._client.http_post(url, json=payload, cookies=self._client.cookies, headers=self.headers)
    self._client.sync()
    return self._client.get_by_etag(response['id2etag'][obj['name']], search='tags')

rename(self, old, new)

Renames a tag.

Parameters:

Name Type Description Default
old str

Current label of the tag to be changed.

required
new str

Desired new label of the tag.

required

Returns:

Type Description
dict

The tag object with the updated label.

Exceptions:

Type Description
TypeError

If old and new are not strings.

ValueError

If the old tag label does not exist.

ValueError

If the new tag label already exists.

RuntimeError

If the renaming was unsuccessful.

Changing a Tag's Label

Pass in the current label of the tag, and the desired new label of the tag.

# Lets assume that we have a tag that already exists named "Movie"
old_label = "Movie"
new_label = "Movies"
updated_tag = client.tag.rename(old_label, new_label)
Result

The updated tag object dictionary is returned.

{'name': 'movies', 'label': 'Movies', 'sortOrder': 0, 'sortType': 'project', 'color': '#134397', 'etag': 'qer1jygy'}

Before

image

After

image

Source code in managers/tags.py
def rename(self, old: str, new: str) -> dict:
    """
    Renames a tag.

    Arguments:
        old: Current label of the tag to be changed.
        new: Desired new label of the tag.

    Returns:
        The tag object with the updated label.

    Raises:
        TypeError: If `old` and `new` are not strings.
        ValueError: If the `old` tag label does not exist.
        ValueError: If the `new` tag label already exists.
        RuntimeError: If the renaming was unsuccessful.

    !!! example "Changing a Tag's Label"

        Pass in the current label of the tag, and the desired new label of the tag.

        ```python
        # Lets assume that we have a tag that already exists named "Movie"
        old_label = "Movie"
        new_label = "Movies"
        updated_tag = client.tag.rename(old_label, new_label)
        ```

        ??? success "Result"
            The updated tag object dictionary is returned.

            ```python
            {'name': 'movies', 'label': 'Movies', 'sortOrder': 0, 'sortType': 'project', 'color': '#134397', 'etag': 'qer1jygy'}
            ```

            **Before**

            ![image](https://user-images.githubusercontent.com/56806733/104661255-fcac0f00-567c-11eb-9f10-69af8b50e0b4.png)

            **After**

            ![image](https://user-images.githubusercontent.com/56806733/104661299-19e0dd80-567d-11eb-825f-758d83178295.png)
    """
    # Check that both old and new are strings
    if not isinstance(old, str) or not isinstance(new, str):
        raise TypeError('Old and New Must Be Strings')

    # Make sure the old tag exists
    old = old.lower()
    # Check if the tag object exists
    obj = self._client.get_by_fields(name=old, search='tags')
    if not obj:
        raise ValueError(f"Tag '{old}' Does Not Exist To Rename")

    # Make sure the new tag does not exist
    temp_new = new.lower()
    # Check if the tag object exists
    found = self._client.get_by_fields(name=temp_new, search='tags')
    if found:
        raise ValueError(f"Name '{new}' Already Exists -> Cannot Duplicate Name")

    url = self._client.BASE_URL + 'tag/rename'
    payload = {
        'name': obj['name'],
        'newName': new
    }
    response = self._client.http_put(url, json=payload, cookies=self._client.cookies, headers=self.headers)
    self._client.sync()
    # Response from TickTick does not return the new etag of the object, we must find it ourselves
    new_obj = self._client.get_by_fields(name=temp_new, search='tags')
    # Return the etag of the updated object
    return self._client.get_by_etag(new_obj['etag'], search='tags')

sorting(self, label, sort)

Change the sort type of a tag. For batch changing sort types, see update.

Parameters:

Name Type Description Default
label str

The label of the tag to be changed.

required
sort int

The new sort type specified by an integer 0-3. See sort dictionary.

required

Returns:

Type Description
dict

The updated tag dictionary object.

Exceptions:

Type Description
TypeError

If label is not a string or if sort is not an int.

ValueError

If the tag label does not exist.

RuntimeError

If the updating was unsuccessful.

Changing the Sort Type

# Lets assume that we have a tag named "Movies" with the sort type "project"
changed_sort_type = client.tag.sorting("Movies", 1)  # Sort by 'dueDate'
Result

The updated task dictionary object is returned.

{'name': 'movies', 'label': 'Movies', 'sortOrder': 0, 'sortType': 'dueDate', 'color': '#134397', 'etag': 'fflj8iy0'}

Before

image

After

image

Source code in managers/tags.py
def sorting(self, label: str, sort: int) -> dict:
    """
    Change the sort type of a tag. For batch changing sort types, see [update][managers.tags.TagsManager.update].

    Arguments:
        label: The label of the tag to be changed.
        sort: The new sort type specified by an integer 0-3. See [sort dictionary](tags.md#sort-dictionary).

    Returns:
        The updated tag dictionary object.

    Raises:
        TypeError: If `label` is not a string or if `sort` is not an int.
        ValueError: If the tag `label` does not exist.
        RuntimeError: If the updating was unsuccessful.

    !!! example "Changing the Sort Type"

        ```python
        # Lets assume that we have a tag named "Movies" with the sort type "project"
        changed_sort_type = client.tag.sorting("Movies", 1)  # Sort by 'dueDate'
        ```

        ??? success "Result"
            The updated task dictionary object is returned.

            ```python
            {'name': 'movies', 'label': 'Movies', 'sortOrder': 0, 'sortType': 'dueDate', 'color': '#134397', 'etag': 'fflj8iy0'}
            ```

            **Before**

            ![image](https://user-images.githubusercontent.com/56806733/104663625-3f241a80-5682-11eb-93a7-73d280c59b3e.png)

            **After**

            ![image](https://user-images.githubusercontent.com/56806733/104663663-5531db00-5682-11eb-9440-5673a70840b4.png)
    """
    if not isinstance(label, str) or not isinstance(sort, int):
        raise TypeError('Label Must Be A String and Sort Must Be An Int')

    # Get the object
    label = label.lower()
    obj = self._client.get_by_fields(name=label, search='tags')
    if not obj:
        raise ValueError(f"Tag '{label}' Does Not Exist To Update")
    sort = self._sort_string_value(sort)  # Get the sort string for the value

    obj['sortType'] = sort  # set the object field

    url = self._client.BASE_URL + 'batch/tag'
    payload = {
        'update': [obj]
    }
    response = self._client.http_post(url, json=payload, cookies=self._client.cookies, headers=self.headers)
    self._client.sync()
    return self._client.get_by_etag(response['id2etag'][obj['name']])

update(self, obj)

Generic update method. Supports single and batch tag update.

Important

Updating tag properties like parent and renaming tags must be completed through their respective class methods to work: nesting and renaming. These updates use different endpoints to the traditional updating.

Important

You are able to batch update sorting and color of tag objects through this method. If you only need to update single tags, it is recommended you use the class methods: sorting and color

Info

More information on Tag Object properties here

Parameters:

Name Type Description Default
obj dict or list

Single Tag (dict): The tag dictionary object to update.

Multiple Tags (list): The tag dictionaries to update in a list.

required

Returns:

Type Description
dict or list

Single Tag (dict): The updated tag dictionary object.

Multiple Tags (list): The updated tag dictionaries in a list.

Exceptions:

Type Description
TypeError

If obj is not a dict or list.

RuntimeError

If the updating was unsuccessful.

Updating Tags

Change a field directly in the task object then pass it to the method. See above for more information about what can actually be successfully changed through this method.

# Lets say we have a tag named "Fun" that we want to change the color of.
# We can change the color by updating the field directly.
fun_tag = client.get_by_fields(label='Fun', search='tags')  # Get the tag object
new_color = '#d00000'
fun_tag['color'] = new_color  # Change the color
updated_fun_tag = client.tag.update(fun_tag)  # Pass the object to update.
Result

The updated tag dictionary object is returned.

{'name': 'fun', 'label': 'Fun', 'sortOrder': 2199023255552, 'sortType': 'project', 'color': '#d00000', 'etag': 'i85c8ijo'}

Before

image

After

image

Changing the fields is the same as with updating a single tag, except you will need to pass the objects in a list to the method.

# Lets update the colors for three tags: "Fun", "Hobbies", and "Productivity"
fun_tag = client.get_by_fields(label="Fun", search='tags')
hobbies_tag = client.get_by_fields(label="Hobbies", search='tags')
productivity_tag = client.get_by_fields(label="Productivity", search='tags')
fun_color_new = "#951a63"
hobbies_color_new = "#0f8a1f"
productivity_color_new = "#493293"
# Change the fields directly
fun_tag['color'] = fun_color_new
hobbies_tag['color'] = hobbies_color_new
productivity_tag['color'] = productivity_color_new
# The objects must be passed in a list
update_tag_list = [fun_tag, hobbies_tag, productivity_tag]
updated_tags = client.tag.update(update_tag_list)
Result

The updated task dictionary objects are returned in a list.

[{'name': 'fun', 'label': 'Fun', 'sortOrder': -1099511627776, 'sortType': 'project', 'color': '#951a63', 'etag': 'n543ajq2'},

{'name': 'hobbies', 'label': 'Hobbies', 'sortOrder': -549755813888, 'sortType': 'project', 'color': '#0f8a1f', 'etag': 'j4nspkg4'},

{'name': 'productivity', 'label': 'Productivity', 'sortOrder': 0, 'sortType': 'project', 'color': '#493293', 'etag': '34qz9bzq'}]

Before

image

After

image

Source code in managers/tags.py
def update(self, obj):
    """
    Generic update method. Supports single and batch tag update.

    !!! important
        Updating tag properties like `parent` and renaming tags must be completed through
        their respective class methods to work: [nesting][managers.tags.TagsManager.nesting]
        and [renaming][managers.tags.TagsManager.rename]. These updates use different
        endpoints to the traditional updating.

    !!! important
        You are able to batch update sorting and color of tag objects through this method. If you only
        need to update single tags, it is recommended you use the class methods: [sorting][managers.tags.TagsManager.sorting]
        and [color][managers.tags.TagsManager.color]

    !!! info
        More information on Tag Object properties [here](tags.md#example-ticktick-tag-dictionary)

    Arguments:
        obj (dict or list):
            **Single Tag (dict)**: The tag dictionary object to update.

            **Multiple Tags (list)**: The tag dictionaries to update in a list.

    Returns:
        dict or list:
        **Single Tag (dict)**: The updated tag dictionary object.

        **Multiple Tags (list)**: The updated tag dictionaries in a list.

    Raises:
        TypeError: If `obj` is not a dict or list.
        RuntimeError: If the updating was unsuccessful.

    !!! example "Updating Tags"
        === "Single Tag Update"
            Change a field directly in the task object then pass it to the method. See above
            for more information about what can actually be successfully changed through this method.

            ```python
            # Lets say we have a tag named "Fun" that we want to change the color of.
            # We can change the color by updating the field directly.
            fun_tag = client.get_by_fields(label='Fun', search='tags')  # Get the tag object
            new_color = '#d00000'
            fun_tag['color'] = new_color  # Change the color
            updated_fun_tag = client.tag.update(fun_tag)  # Pass the object to update.
            ```

            ??? success "Result"
                The updated tag dictionary object is returned.

                ```python
                {'name': 'fun', 'label': 'Fun', 'sortOrder': 2199023255552, 'sortType': 'project', 'color': '#d00000', 'etag': 'i85c8ijo'}
                ```

                **Before**

                ![image](https://user-images.githubusercontent.com/56806733/104669635-4aca0e00-568f-11eb-8bc6-9572a432b623.png)

                **After**

                ![image](https://user-images.githubusercontent.com/56806733/104669824-ac8a7800-568f-11eb-93d6-ac40235bcd3f.png)

        === "Multiple Tag Update"
            Changing the fields is the same as with updating a single tag, except you will need
            to pass the objects in a list to the method.

            ```python
            # Lets update the colors for three tags: "Fun", "Hobbies", and "Productivity"
            fun_tag = client.get_by_fields(label="Fun", search='tags')
            hobbies_tag = client.get_by_fields(label="Hobbies", search='tags')
            productivity_tag = client.get_by_fields(label="Productivity", search='tags')
            fun_color_new = "#951a63"
            hobbies_color_new = "#0f8a1f"
            productivity_color_new = "#493293"
            # Change the fields directly
            fun_tag['color'] = fun_color_new
            hobbies_tag['color'] = hobbies_color_new
            productivity_tag['color'] = productivity_color_new
            # The objects must be passed in a list
            update_tag_list = [fun_tag, hobbies_tag, productivity_tag]
            updated_tags = client.tag.update(update_tag_list)
            ```

            ??? success "Result"
                The updated task dictionary objects are returned in a list.

                ```python
                [{'name': 'fun', 'label': 'Fun', 'sortOrder': -1099511627776, 'sortType': 'project', 'color': '#951a63', 'etag': 'n543ajq2'},

                {'name': 'hobbies', 'label': 'Hobbies', 'sortOrder': -549755813888, 'sortType': 'project', 'color': '#0f8a1f', 'etag': 'j4nspkg4'},

                {'name': 'productivity', 'label': 'Productivity', 'sortOrder': 0, 'sortType': 'project', 'color': '#493293', 'etag': '34qz9bzq'}]
                ```

                **Before**

                ![image](https://user-images.githubusercontent.com/56806733/104670498-cd070200-5690-11eb-9fdd-0287fa6c7e7b.png)

                **After**

                ![image](https://user-images.githubusercontent.com/56806733/104670531-dc864b00-5690-11eb-844a-899031335922.png)

    """
    batch = False  # Bool signifying batch create or not
    if isinstance(obj, list):
        # Batch tag creation triggered
        obj_list = obj  # Assuming all correct objects
        batch = True
    else:
        if not isinstance(obj, dict):
            raise TypeError('Required Positional Argument Must Be A Dict or List of Tag Objects')

    if not batch:
        obj_list = [obj]

    url = self._client.BASE_URL + 'batch/tag'
    payload = {'update': obj_list}
    response = self._client.http_post(url, json=payload, cookies=self._client.cookies, headers=self.headers)
    self._client.sync()

    if not batch:
        return self._client.get_by_etag(self._client.parse_etag(response), search='tags')
    else:
        etag = response['id2etag']
        etag2 = list(etag.keys())  # Tag names are out of order
        labels = [x['name'] for x in obj_list]  # Tag names are in order
        items = [''] * len(obj_list)  # Create enough spots for the objects
        for tag in etag2:
            index = labels.index(tag)  # Object of the index is here
            actual_etag = etag[tag]  # Get the actual etag
            found = self._client.get_by_etag(actual_etag, search='tags')
            items[index] = found  # Place at the correct index
        return items