Todoist Python SDK

repository·main·Indexed 42 days ago

https://github.com/doist/todoist-api-python

The official Python SDK for the Todoist API, version 4.0.0. It provides both synchronous (TodoistAPI) and asynchronous (TodoistAPIAsync) interfaces for managing tasks, projects, and labels. Features include support for OAuth authentication, ResultsPaginator for handling paginated responses, and specialized methods for task creation via standard parameters or Quick Add syntax.

Tokens
10.2K
Snippets
30
Records
57
Agent score
54%

What's inside Todoist

  1. Use TodoistAPIAsync for asynchronous operations

    main
    For asynchronous workflows, use the TodoistAPIAsync class. It is recommended to use the async with context manager to ensure the underlying httpx2.AsyncClient is properly closed. If not using a context manager, you must manually call await api.close().
  2. Migrate from version 3.x to 4.x

    main

    Version 4.x migrates the HTTP stack from requests to httpx2. This introduces the following breaking changes:

    • The session parameter in TodoistAPI and TodoistAPIAsync has been renamed to client and expects an httpx2.Client or httpx2.AsyncClient respectively.
    • Error handling must now catch httpx2.HTTPStatusError instead of requests.exceptions.HTTPError.
  3. Install the Todoist API Python Client

    main

    You can install the SDK using pip or by adding it as a dependency in your pyproject.toml file.

    To install via pip:

    pip install todoist-api-python

    To add as a dependency in pyproject.toml:

    dependencies = [
      "todoist-api-python>=3.1.0,<4",
    ]
  4. Authenticate with Todoist using OAuth

    main

    The todoist_api_python.authentication module provides helpers to implement the OAuth flow. To authenticate a user, you must generate an authorization URL, redirect the user to it, capture the authorization code from the callback, and exchange that code for an access token.

    OAuth Flow Steps:

    1. Generate a random state: Create a unique string (e.g., using uuid.uuid4()) to prevent CSRF attacks.
    2. Build the authorization URL: Use get_authentication_url() with your client_id, desired scopes, and the generated state.
    3. Redirect the user: Send the user to the generated URL.
    4. Handle the callback: After the user authorizes, capture the code provided in the redirect.
    5. Exchange code for token: Use get_auth_token() with your client_id, client_secret, and the captured code.
    6. Verify state: Ensure the state returned in the auth_result matches the state you originally generated.
    import uuid
    from todoist_api_python.authentication import get_auth_token, get_authentication_url
    
    # 1. Generate a random state
    state = str(uuid.uuid4())
    
    # 2. Build the authorization URL
    url = get_authentication_url(
        client_id="YOUR_CLIENT_ID",
        scopes=["data:read", "task:add"],
        state=state,
    )
    
    # 3. Redirect the user to `url` (handled by your application logic)
    
    # 4. Handle the OAuth callback and obtain the auth code
    code = "CODE_YOU_OBTAINED"
    
    # 5. Exchange code for an access token
    auth_result = get_auth_token(
        client_id="YOUR_CLIENT_ID",
        client_secret="YOUR_CLIENT_SECRET",
        code=code,
    )
    
    # 6. Ensure state is consistent, and done!
    assert auth_result.state == state
    access_token = auth_result.access_token
  5. How ResultsPaginator handles pagination

    main

    The ResultsPaginator is an internal iterator used by many get_* methods to handle Todoist's cursor-based pagination automatically.

    When you iterate over a method returning a ResultsPaginator (like get_labels or get_comments), the class:

    1. Fetches the first page of results.
    2. Extracts the next_cursor from the API response.
    3. Automatically makes subsequent network requests using the cursor parameter as you continue to iterate.

    Note: Each iteration triggers a network request. Be mindful of rate limits when iterating over large datasets.

  6. How AsyncResultsPaginator handles pagination

    main

    The AsyncResultsPaginator is an AsyncIterator used by the client to handle cursor-based pagination automatically.

    When you iterate over a method that returns an AsyncIterator (like get_labels or get_comments), the paginator:

    1. Fetches the first page of results.
    2. Extracts the next_cursor from the API response.
    3. Automatically includes the cursor parameter in the next network request when you request the next batch of items.

    This allows you to use a simple async for loop to traverse all results without manually managing cursors or page limits.

    # The paginator is used internally, but used by the consumer like this:
    async for label in api.get_labels(limit=50):
        print(label.name)
    # The loop continues automatically until all pages are exhausted.
  7. Use the asynchronous TodoistAPIAsync client

    main

    For asynchronous operations, use TodoistAPIAsync. It is highly recommended to use the async with context manager to ensure the client is closed explicitly. If not using a context manager, you must call await api.close() manually. Note that you will need to run your entry point using asyncio.run().

    from todoist_api_python.api_async import TodoistAPIAsync
    import asyncio
    
    async def main():
        async with TodoistAPIAsync("YOUR_API_TOKEN") as api:
            task = await api.get_task("6X4Vw2Hfmg73Q2XR")
            print(task.content)
    
    asyncio.run(main())
  8. Use the synchronous TodoistAPI client

    main

    To use the synchronous API, initialize TodoistAPI with your API token. You can fetch tasks and iterate through paginated results like comments using generator-based iterators.

    from todoist_api_python.api import TodoistAPI
    
    api = TodoistAPI("YOUR_API_TOKEN")
    
    task = api.get_task("6X4Vw2Hfmg73Q2XR")
    print(f"Task: {task.content}")
    
    comments_iter = api.get_comments(task_id=task.id)
    for comments in comments_iter:
        for comment in comments:
            print(f"Comment: {comment.content}")
  9. Use the TodoistAPI client for synchronous operations

    main

    To perform synchronous operations, initialize the TodoistAPI class with your API token. You can then use methods like get_task to retrieve task objects and get_comments to iterate through comments associated with a task. Note that get_comments returns an iterator that yields batches of comments.

    from todoist_api_python.api import TodoistAPI
    
    api = TodoistAPI("YOUR_API_TOKEN")
    
    task = api.get_task("6X4Vw2Hfmg73Q2XR")
    print(f"Task: {task.content}")
    
    comments_iter = api.get_comments(task_id=task.id)
    for comments in comments_iter:
        for comment in comments:
            print(f"Comment: {comment.content}")
  10. get_authentication_url()

    main

    Generates the Todoist authorization URL to initiate the OAuth flow.

    Parameters:

    • client_id (str): Your Todoist application client ID.
    • scopes (list[str]): A list of permission scopes requested (e.g., ["data:read", "task:add"]).
    • state (str): A random string used to prevent CSRF attacks. This value is returned in the callback and should be verified.
  11. get_auth_token()

    main

    Exchanges an OAuth authorization code for an access token.

    Parameters:

    • client_id (str): Your Todoist application client ID.
    • client_secret (str): Your Todoist application client secret.
    • code (str): The authorization code obtained from the OAuth callback.

    Returns: An object containing:

    • access_token (str): The token used for subsequent API requests.
    • state (str): The state string provided during the initial URL generation, used for verification.
  12. Complete or uncomplete a task

    main

    Manage task completion status using:

    • complete_task(task_id: str): Marks a task as completed. For recurring tasks, this schedules the next occurrence.
    • uncomplete_task(task_id: str): Reopens a completed task. This also uncompletes any parent tasks or sections.

    Both methods return a bool indicating success.

    success = await api.complete_task("task_id")
    if success:
        print("Task completed!")
    
    await api.uncomplete_task("task_id")