Skip to main content
Glama

ticktick-sdk: A TickTick MCP Server & Full Python SDK

PyPI - Version Python 3.11+ License: MIT PyPI Downloads

A comprehensive async Python SDK for TickTick with MCP (Model Context Protocol) server support.

Includes full support for Dida365 (滴答清单) as well.

Use TickTick programmatically from Python, or let AI assistants manage your tasks.

Table of Contents


Related MCP server: ticktick-mcp-server

Features

MCP Server

  • 43 Tools: Streamlined coverage of TickTick functionality

  • Batch Operations: All mutations accept lists (1-100 items) for bulk operations

  • AI-Ready: Works with Claude, GPT, and other MCP-compatible assistants

  • Dual Output: Markdown for humans, JSON for machines

Python Library

  • Full Async Support: Built on httpx for high-performance async operations

  • Batch Operations: Create, update, delete, complete up to 100 tasks in a single call

  • Complete Task Management: Create, read, update, delete, complete, move, pin tasks

  • Kanban Boards: Full column management (create, update, delete, move tasks between columns)

  • Project Organization: Projects, folders, kanban boards

  • Tag System: Hierarchical tags with colors

  • Habit Tracking: Full CRUD for habits with batch check-ins, streaks, and goals

  • Focus/Pomodoro: Access focus session data and statistics

  • User Analytics: Productivity scores, levels, completion rates

Developer Experience

  • Type-Safe: Full Pydantic v2 validation with comprehensive type hints

  • Well-Tested: 300+ tests covering both mock and live API interactions

  • Documented: Extensive docstrings and examples


Why This Library?

The Two-API Problem

TickTick has two different APIs:

API

Type

What We Use It For

V1 (OAuth2)

Official, documented

Project with all tasks, basic operations

V2 (Session)

Unofficial, reverse-engineered

Tags, folders, habits, focus, subtasks, and more

The official V1 API is limited. Most of TickTick's power features (tags, habits, focus tracking) are only available through the undocumented V2 web API. This library combines both, routing each operation to the appropriate API automatically.

Compared to Other Libraries

Based on analysis of the actual source code of available TickTick Python libraries:

Feature

ticktick-sdk

pyticktick

ticktick-py

tickthon

ticktick-python

I/O Model

Async

Async

Sync

Sync

Sync

Type System

Pydantic V2

Pydantic V2

Dicts

attrs

addict

MCP Server

Yes

No

No

No

No

Habits

Full CRUD

No

Basic

Basic

No

Focus/Pomo

Yes

Yes

Yes

Yes

No

Unified V1+V2

Smart Routing

Separate

Both

V2 only

V2 only

Subtasks

Advanced

Batch

Yes

Basic

Basic

Tags

Full (merge/rename)

Yes

Yes

Yes

No

Key Differentiators:

  • MCP Server: Only ticktick-sdk provides AI assistant integration via Model Context Protocol

  • Unified API Routing: Automatically routes operations to V1 or V2 based on feature requirements

  • Full Habit CRUD: Complete habit management including check-ins, streaks, archive/unarchive

  • Async-First: Built on httpx for high-performance async operations


Installation

pip install ticktick-sdk

Requirements:

  • Python 3.11+

  • TickTick account (free or Pro)


MCP Server Setup & Usage

Use TickTick with AI assistants like Claude through the Model Context Protocol.

Step 1: Register Your App

  1. Go to the TickTick Developer Portal

  2. Click "Create App"

  3. Fill in:

    • App Name: e.g., "My TickTick MCP"

    • Redirect URI: http://127.0.0.1:8080/callback

  4. Save your Client ID and Client Secret

Step 2: Get OAuth2 Access Token

Run the auth command with your credentials:

TICKTICK_CLIENT_ID=your_client_id \
TICKTICK_CLIENT_SECRET=your_client_secret \
ticktick-sdk auth

This will:

  1. Open your browser to TickTick's authorization page

  2. Authorize the app - Click "Authorize" to grant access

  3. Return to terminal - After authorizing, you'll see output like this:

============================================================
  SUCCESS! Here is your access token:
============================================================

a]234abc-5678-90de-f012-34567890abcd

============================================================

NEXT STEPS:

For Claude Code users:
  Run (replace YOUR_* placeholders):
    claude mcp add ticktick \
      -e TICKTICK_CLIENT_ID=YOUR_CLIENT_ID \
      ...
  1. Copy this token - You'll need it in the next step

Note: Sometimes the browser shows an "invalid credentials" error page. Just refresh the page and it should work.

SSH/Headless Users: Add --manual flag for a text-based flow that doesn't require a browser.

Step 3: Configure Your AI Assistant

claude mcp add ticktick \
  -e TICKTICK_CLIENT_ID=your_client_id \
  -e TICKTICK_CLIENT_SECRET=your_client_secret \
  -e TICKTICK_ACCESS_TOKEN=your_access_token \
  -e TICKTICK_USERNAME=your_email \
  -e TICKTICK_PASSWORD=your_password \
  -- ticktick-sdk

Note: For TICKTICK_ACCESS_TOKEN, paste the token you copied from Step 2.

Verify it's working:

claude mcp list        # See all configured servers
/mcp                   # Within Claude Code, check server status

Claude Desktop

Add to your Claude Desktop config:

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json Windows: %APPDATA%\Claude\claude_desktop_config.json

{
  "mcpServers": {
    "ticktick": {
      "command": "ticktick-sdk",
      "env": {
        "TICKTICK_CLIENT_ID": "your_client_id",
        "TICKTICK_CLIENT_SECRET": "your_client_secret",
        "TICKTICK_ACCESS_TOKEN": "your_access_token",
        "TICKTICK_USERNAME": "your_email",
        "TICKTICK_PASSWORD": "your_password"
      }
    }
  }
}

Other MCP-Compatible Tools

This server works with any tool that supports the Model Context Protocol, which includes most modern AI assistants and IDEs. The configuration is similar - you just need to provide the command (ticktick-sdk) and the environment variables shown above.

CLI Reference

The ticktick-sdk command provides several subcommands:

Command

Description

ticktick-sdk

Start the MCP server (default)

ticktick-sdk server

Start the MCP server (explicit)

ticktick-sdk server --host HOST

Use specific API host (ticktick.com or dida365.com)

ticktick-sdk server --enabledModules MODULES

Enable only specific tool modules (comma-separated)

ticktick-sdk server --enabledTools TOOLS

Enable only specific tools (comma-separated)

ticktick-sdk auth

Get OAuth2 access token (opens browser)

ticktick-sdk auth --manual

Get OAuth2 access token (SSH-friendly)

ticktick-sdk --version

Show version information

ticktick-sdk --help

Show help message

Tool Filtering (reduces context window usage for AI assistants):

# Enable only task and project tools
ticktick-sdk server --enabledModules tasks,projects

# Enable specific tools only
ticktick-sdk server --enabledTools ticktick_create_tasks,ticktick_list_tasks

# Available modules: tasks, projects, folders, columns, tags, habits, user, focus

Example Conversations

Once configured, you can ask Claude things like:

  • "What tasks do I have due today?"

  • "Create a task to call John tomorrow at 2pm"

  • "Show me my high priority tasks"

  • "Mark the grocery shopping task as complete"

  • "What's my current streak for the Exercise habit?"

  • "Check in my meditation habit for today"

  • "Create a new habit to drink 8 glasses of water daily"

Available MCP Tools (43 Total)

All mutation tools accept lists for batch operations (1-100 items).

Task Tools (Batch-Capable)

Tool

Description

ticktick_create_tasks

Create 1-50 tasks with titles, dates, tags, etc.

ticktick_get_task

Get task details by ID

ticktick_list_tasks

List tasks (active/completed/abandoned/deleted via status filter)

ticktick_update_tasks

Update 1-100 tasks (includes column assignment)

ticktick_complete_tasks

Complete 1-100 tasks

ticktick_delete_tasks

Delete 1-100 tasks (moves to trash)

ticktick_move_tasks

Move 1-50 tasks between projects

ticktick_set_task_parents

Set parent-child relationships for 1-50 tasks

ticktick_unparent_tasks

Remove parent relationships from 1-50 tasks

ticktick_search_tasks

Search tasks by text

ticktick_pin_tasks

Pin or unpin 1-100 tasks

Project Tools

Tool

Description

ticktick_list_projects

List all projects

ticktick_get_project

Get project details with tasks

ticktick_create_project

Create a new project

ticktick_update_project

Update project properties

ticktick_delete_project

Delete a project

Folder Tools

Tool

Description

ticktick_list_folders

List all folders

ticktick_create_folder

Create a folder

ticktick_rename_folder

Rename a folder

ticktick_delete_folder

Delete a folder

Kanban Column Tools

Tool

Description

ticktick_list_columns

List columns for a kanban project

ticktick_create_column

Create a kanban column

ticktick_update_column

Update column name or order

ticktick_delete_column

Delete a kanban column

Tag Tools

Tool

Description

ticktick_list_tags

List all tags

ticktick_create_tag

Create a tag with color

ticktick_update_tag

Update tag properties (includes rename via label)

ticktick_delete_tag

Delete a tag

ticktick_merge_tags

Merge two tags

Habit Tools (Batch-Capable)

Tool

Description

ticktick_habits

List all habits

ticktick_habit

Get habit details

ticktick_habit_sections

List sections (morning/afternoon/night)

ticktick_create_habit

Create a new habit

ticktick_update_habit

Update habit properties (includes archive/unarchive)

ticktick_delete_habit

Delete a habit

ticktick_checkin_habits

Check in 1-50 habits (supports backdating)

ticktick_habit_checkins

Get check-in history

User & Analytics Tools

Tool

Description

ticktick_get_profile

Get user profile

ticktick_get_status

Get account status

ticktick_get_statistics

Get productivity stats

ticktick_get_preferences

Get user preferences

ticktick_focus_heatmap

Get focus heatmap data

ticktick_focus_by_tag

Get focus time by tag


Python Library Setup & Usage

Use TickTick programmatically in your Python applications.

Setup

Step 1: Register Your App

Same as MCP setup - go to the TickTick Developer Portal and create an app.

Step 2: Create Your .env File

Create a .env file in your project directory:

# V1 API (OAuth2)
TICKTICK_CLIENT_ID=your_client_id_here
TICKTICK_CLIENT_SECRET=your_client_secret_here
TICKTICK_REDIRECT_URI=http://127.0.0.1:8080/callback
TICKTICK_ACCESS_TOKEN=  # Will be filled in Step 3

# V2 API (Session)
TICKTICK_USERNAME=your_ticktick_email@example.com
TICKTICK_PASSWORD=your_ticktick_password

# Optional
TICKTICK_TIMEOUT=30

Step 3: Get OAuth2 Access Token

# Source your .env file first, or export the variables
ticktick-sdk auth

Copy the access token to your .env file.

Step 4: Verify Setup

import asyncio
from ticktick_sdk import TickTickClient

async def test():
    async with TickTickClient.from_settings() as client:
        profile = await client.get_profile()
        print(f'Connected as: {profile.display_name}')

asyncio.run(test())

Quick Start

import asyncio
from ticktick_sdk import TickTickClient

async def main():
    async with TickTickClient.from_settings() as client:
        # Create a task
        task = await client.create_task(
            title="Learn ticktick-sdk",
            tags=["python", "productivity"],
        )
        print(f"Created: {task.title} (ID: {task.id})")

        # List all tasks
        tasks = await client.get_all_tasks()
        print(f"You have {len(tasks)} active tasks")

        # Complete the task
        await client.complete_task(task.id, task.project_id)
        print("Task completed!")

asyncio.run(main())

Tasks

Creating Tasks

from datetime import datetime, timedelta
from ticktick_sdk import TickTickClient

async with TickTickClient.from_settings() as client:
    # Simple task
    task = await client.create_task(title="Buy groceries")

    # Task with due date and priority
    task = await client.create_task(
        title="Submit report",
        due_date=datetime.now() + timedelta(days=1),
        priority="high",  # none, low, medium, high
    )

    # Task with tags and content
    task = await client.create_task(
        title="Review PR #123",
        content="Check for:\n- Code style\n- Tests\n- Documentation",
        tags=["work", "code-review"],
    )

    # Recurring task (MUST include start_date!)
    task = await client.create_task(
        title="Daily standup",
        start_date=datetime(2025, 1, 20, 9, 0),
        recurrence="RRULE:FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR",
    )

    # Task with reminder
    task = await client.create_task(
        title="Meeting with team",
        due_date=datetime(2025, 1, 20, 14, 0),
        reminders=["TRIGGER:-PT15M"],  # 15 minutes before
    )

    # All-day task
    task = await client.create_task(
        title="Project deadline",
        due_date=datetime(2025, 1, 31),
        all_day=True,
    )

Managing Tasks

async with TickTickClient.from_settings() as client:
    # Get a specific task
    task = await client.get_task(task_id="...")

    # Update a task
    task.title = "Updated title"
    task.priority = 5  # high priority
    await client.update_task(task)

    # Complete a task
    await client.complete_task(task_id="...", project_id="...")

    # Delete a task (moves to trash)
    await client.delete_task(task_id="...", project_id="...")

    # Move task to another project
    await client.move_task(
        task_id="...",
        from_project_id="...",
        to_project_id="...",
    )

Subtasks

async with TickTickClient.from_settings() as client:
    # Create parent task
    parent = await client.create_task(title="Main task")

    # Create child task
    child = await client.create_task(title="Subtask")

    # Make it a subtask (parent_id in create is ignored by API)
    await client.make_subtask(
        task_id=child.id,
        parent_id=parent.id,
        project_id=child.project_id,
    )

    # Remove parent relationship
    await client.unparent_subtask(
        task_id=child.id,
        project_id=child.project_id,
    )

Querying Tasks

async with TickTickClient.from_settings() as client:
    # All active tasks
    all_tasks = await client.get_all_tasks()

    # Tasks due today
    today = await client.get_today_tasks()

    # Overdue tasks
    overdue = await client.get_overdue_tasks()

    # Tasks by tag
    work_tasks = await client.get_tasks_by_tag("work")

    # Tasks by priority
    urgent = await client.get_tasks_by_priority("high")

    # Search tasks
    results = await client.search_tasks("meeting")

    # Recently completed
    completed = await client.get_completed_tasks(days=7, limit=50)

    # Abandoned tasks ("won't do")
    abandoned = await client.get_abandoned_tasks(days=30)

    # Deleted tasks (in trash)
    deleted = await client.get_deleted_tasks(limit=50)

Projects & Folders

Projects

async with TickTickClient.from_settings() as client:
    # List all projects
    projects = await client.get_all_projects()
    for project in projects:
        print(f"{project.name} ({project.id})")

    # Get project with all its tasks
    project_data = await client.get_project_tasks(project_id="...")
    print(f"Project: {project_data.project.name}")
    print(f"Tasks: {len(project_data.tasks)}")

    # Create a project
    project = await client.create_project(
        name="Q1 Goals",
        color="#4A90D9",
        view_mode="kanban",  # list, kanban, timeline
    )

    # Update a project
    await client.update_project(
        project_id=project.id,
        name="Q1 Goals 2025",
        color="#FF5500",
    )

    # Delete a project
    await client.delete_project(project_id="...")

Folders (Project Groups)

async with TickTickClient.from_settings() as client:
    # List all folders
    folders = await client.get_all_folders()

    # Create a folder
    folder = await client.create_folder(name="Work Projects")

    # Create project in folder
    project = await client.create_project(
        name="Client A",
        folder_id=folder.id,
    )

    # Rename a folder
    await client.rename_folder(folder_id=folder.id, name="Work")

    # Delete a folder
    await client.delete_folder(folder_id="...")

Tags

Tags in TickTick support hierarchy (parent-child relationships) and custom colors.

async with TickTickClient.from_settings() as client:
    # List all tags
    tags = await client.get_all_tags()
    for tag in tags:
        print(f"{tag.label} ({tag.name}) - {tag.color}")

    # Create a tag
    tag = await client.create_tag(
        name="urgent",
        color="#FF0000",
    )

    # Create nested tag
    child_tag = await client.create_tag(
        name="critical",
        parent="urgent",  # Parent tag name
    )

    # Rename a tag
    await client.rename_tag(old_name="urgent", new_name="priority")

    # Update tag color or parent
    await client.update_tag(
        name="priority",
        color="#FF5500",
    )

    # Merge tags (move all tasks from source to target)
    await client.merge_tags(source="old-tag", target="new-tag")

    # Delete a tag
    await client.delete_tag(name="obsolete")

Habits

TickTick habits are recurring activities you want to track daily.

Habit Types

Type

Description

Example

Boolean

Simple yes/no

"Did you exercise today?"

Real

Numeric counter

"How many pages did you read?"

Creating and Managing Habits

async with TickTickClient.from_settings() as client:
    # List all habits
    habits = await client.get_all_habits()

    # Boolean habit (yes/no)
    exercise = await client.create_habit(
        name="Exercise",
        color="#4A90D9",
        reminders=["07:00", "19:00"],
        target_days=30,
        encouragement="Stay strong!",
    )

    # Numeric habit
    reading = await client.create_habit(
        name="Read",
        habit_type="Real",
        goal=30,           # 30 pages per day
        step=5,            # +5 button increment
        unit="Pages",
    )

    # Check in a habit (today)
    habit = await client.checkin_habit("habit_id")
    print(f"Streak: {habit.current_streak} days!")

    # Check in for a past date (backdate)
    from datetime import date
    habit = await client.checkin_habit("habit_id", checkin_date=date(2025, 12, 15))

    # Archive/unarchive
    await client.archive_habit("habit_id")
    await client.unarchive_habit("habit_id")

Habit Repeat Rules (RRULE Format)

Schedule

RRULE

Daily (every day)

RRULE:FREQ=WEEKLY;BYDAY=SU,MO,TU,WE,TH,FR,SA

Weekdays only

RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR

Weekends only

RRULE:FREQ=WEEKLY;BYDAY=SA,SU

X times per week

RRULE:FREQ=WEEKLY;TT_TIMES=5

Specific days

RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR

Focus/Pomodoro

from datetime import date, timedelta

async with TickTickClient.from_settings() as client:
    # Focus heatmap (like GitHub contribution graph)
    heatmap = await client.get_focus_heatmap(
        start_date=date.today() - timedelta(days=90),
        end_date=date.today(),
    )

    # Focus time by tag
    by_tag = await client.get_focus_by_tag(days=30)
    for tag, seconds in sorted(by_tag.items(), key=lambda x: -x[1]):
        hours = seconds / 3600
        print(f"  {tag}: {hours:.1f} hours")

User & Statistics

async with TickTickClient.from_settings() as client:
    # User profile
    profile = await client.get_profile()
    print(f"Username: {profile.username}")

    # Account status
    status = await client.get_status()
    print(f"Pro User: {status.is_pro}")
    print(f"Inbox ID: {status.inbox_id}")

    # Productivity statistics
    stats = await client.get_statistics()
    print(f"Level: {stats.level}")
    print(f"Score: {stats.score}")
    print(f"Tasks completed today: {stats.today_completed}")

Error Handling

from ticktick_sdk import (
    TickTickClient,
    TickTickError,
    TickTickNotFoundError,
    TickTickAuthenticationError,
    TickTickRateLimitError,
    TickTickValidationError,
)

async with TickTickClient.from_settings() as client:
    try:
        task = await client.get_task("nonexistent-id")
    except TickTickNotFoundError as e:
        print(f"Task not found: {e}")
    except TickTickAuthenticationError:
        print("Authentication failed - check credentials")
    except TickTickRateLimitError:
        print("Rate limited - wait and retry")
    except TickTickValidationError as e:
        print(f"Invalid input: {e}")
    except TickTickError as e:
        print(f"TickTick error: {e}")

Architecture

┌─────────────────────────────────────────────────────────────┐
│                    Your Application                         │
│              (or MCP Server for AI Assistants)              │
└─────────────────────────┬───────────────────────────────────┘
                          │
┌─────────────────────────▼───────────────────────────────────┐
│                    TickTickClient                           │
│            High-level, user-friendly async API              │
│   (tasks, projects, tags, habits, focus, user methods)      │
└─────────────────────────┬───────────────────────────────────┘
                          │
┌─────────────────────────▼───────────────────────────────────┐
│                  UnifiedTickTickAPI                         │
│        Routes calls to V1 or V2, converts responses         │
│              to unified Pydantic models                     │
└─────────────────────────┬───────────────────────────────────┘
                          │
           ┌──────────────┴──────────────┐
           ▼                             ▼
┌──────────────────────┐      ┌──────────────────────┐
│      V1 API          │      │      V2 API          │
│     (OAuth2)         │      │     (Session)        │
│                      │      │                      │
│ • Official API       │      │ • Unofficial API     │
│ • Project with tasks │      │ • Tags, folders      │
│ • Limited features   │      │ • Habits, focus      │
│                      │      │ • Full subtasks      │
└──────────────────────┘      └──────────────────────┘

Key Design Decisions

  1. V2-First: Most operations use V2 API (more features), falling back to V1 only when needed

  2. Unified Models: Single set of Pydantic models regardless of which API provides the data

  3. Async Throughout: All I/O operations are async for performance

  4. Type Safety: Full type hints and Pydantic validation


API Reference

Models

Model

Description

Task

Task with title, dates, priority, tags, subtasks, recurrence, etc.

Project

Project/list container for tasks

ProjectGroup

Folder for organizing projects

ProjectData

Project with its tasks (from get_project_tasks)

Column

Kanban column for organizing tasks in boards

Tag

Tag with name, label, color, and optional parent

Habit

Recurring habit with type, goals, streaks, and check-ins

HabitSection

Time-of-day grouping (morning/afternoon/night)

HabitCheckin

Individual habit check-in record

HabitPreferences

User habit settings

User

User profile information

UserStatus

Account status (Pro, inbox ID, etc.)

UserStatistics

Productivity statistics (level, score, counts)

ChecklistItem

Subtask/checklist item within a task

Enums

Enum

Values

TaskStatus

ABANDONED (-1), ACTIVE (0), COMPLETED (2)

TaskPriority

NONE (0), LOW (1), MEDIUM (3), HIGH (5)

TaskKind

TEXT, NOTE, CHECKLIST

ProjectKind

TASK, NOTE

ViewMode

LIST, KANBAN, TIMELINE

Exceptions

Exception

Description

TickTickError

Base exception for all errors

TickTickAuthenticationError

Authentication failed

TickTickNotFoundError

Resource not found

TickTickValidationError

Invalid input data

TickTickRateLimitError

Rate limit exceeded

TickTickConfigurationError

Missing configuration

TickTickForbiddenError

Access denied

TickTickServerError

Server-side error


Important: TickTick API Quirks

TickTick's API has several unique behaviors you should know about:

1. Recurrence Requires start_date

If you create a recurring task without a start_date, TickTick silently ignores the recurrence rule.

# WRONG - recurrence will be ignored!
task = await client.create_task(
    title="Daily standup",
    recurrence="RRULE:FREQ=DAILY",
)

# CORRECT
task = await client.create_task(
    title="Daily standup",
    start_date=datetime(2025, 1, 20, 9, 0),
    recurrence="RRULE:FREQ=DAILY",
)

2. Subtasks Require Separate Call

Setting parent_id during task creation is ignored by the API:

# Create the child task first
child = await client.create_task(title="Subtask")

# Then make it a subtask
await client.make_subtask(
    task_id=child.id,
    parent_id="parent_task_id",
    project_id=child.project_id,
)

3. Soft Delete

Deleting tasks moves them to trash (deleted=1) rather than permanently removing them.

4. Date Clearing

To clear a task's due_date, you must also clear start_date:

task.due_date = None
task.start_date = None
await client.update_task(task)

5. Tag Order Not Preserved

The API does not preserve tag order - tags may be returned in any order.

6. Inbox is Special

The inbox is a special project that cannot be deleted. Get its ID via await client.get_status().


Environment Variables

Variable

Required

Description

TICKTICK_CLIENT_ID

Yes

OAuth2 client ID from developer portal

TICKTICK_CLIENT_SECRET

Yes

OAuth2 client secret

TICKTICK_ACCESS_TOKEN

Yes

OAuth2 access token (from auth command)

TICKTICK_USERNAME

Yes

Your TickTick email

TICKTICK_PASSWORD

Yes

Your TickTick password

TICKTICK_REDIRECT_URI

No

OAuth2 redirect URI (default: http://127.0.0.1:8080/callback)

TICKTICK_HOST

No

API host: ticktick.com (default) or dida365.com (Chinese)

TICKTICK_TIMEOUT

No

Request timeout in seconds (default: 30)

TICKTICK_DEVICE_ID

No

Device ID for V2 API (auto-generated)


Running Tests

# Install dev dependencies
pip install -e ".[dev]"

# All tests (mock mode - no API calls)
pytest

# With verbose output
pytest -v

# Live tests (requires credentials)
pytest --live

# With coverage
pytest --cov=ticktick_sdk --cov-report=term-missing

Test Markers

Marker

Description

unit

Unit tests (fast, isolated)

tasks

Task-related tests

projects

Project-related tests

tags

Tag-related tests

habits

Habit-related tests

focus

Focus/Pomodoro tests

pinning

Task pinning tests

columns

Kanban column tests

mock_only

Tests that only work with mocks

live_only

Tests that only run with --live


Troubleshooting

"Token exchange failed"

  • Verify your Client ID and Client Secret are correct

  • Ensure the Redirect URI matches exactly (including trailing slashes)

  • Check that you're using the correct TickTick developer portal

"Authentication failed"

  • Check your TickTick username (email) and password

  • Try logging into ticktick.com to verify credentials

"V2 initialization failed"

  • Your password may contain special characters - try changing it

  • Check for 2FA/MFA (not currently supported)

"Rate limit exceeded"

  • Wait 30-60 seconds before retrying

  • Reduce the frequency of API calls


Contributing

Contributions are welcome! Please:

  1. Fork the repository

  2. Create a feature branch (git checkout -b feature/amazing-feature)

  3. Write tests for new functionality

  4. Ensure all tests pass (pytest)

  5. Run type checking (mypy src/)

  6. Submit a pull request

Development Setup

git clone https://github.com/dev-mirzabicer/ticktick-sdk.git
cd ticktick-sdk
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

License

MIT License - see LICENSE for details.


Acknowledgments

A
license - permissive license
-
quality - not tested
B
maintenance

Maintenance

Maintainers
11hResponse time
3dRelease cycle
9Releases (12mo)
Commit activity
Issues opened vs closed

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dev-mirzabicer/ticktick-sdk'

If you have feedback or need assistance with the MCP directory API, please join our Discord server