To integrate Replit with Todoist, get your API token from Todoist Settings > Integrations, store it in Replit Secrets (lock icon π), and use the Todoist REST API v2 from a simple Express or Flask server to create tasks, read projects, add comments, and build personal task automation β the simplest task API available for beginners.
Why Integrate Todoist with Replit?
Todoist is one of the most accessible task management APIs for beginners: it uses simple Bearer token authentication, has clean and consistent REST endpoints, returns straightforward JSON, and is thoroughly documented. If you are learning to build API integrations in Replit, Todoist is an excellent starting point that teaches the core patterns β secrets management, HTTP requests, JSON handling, error checking β without the complexity of OAuth flows or intricate data models.
For practical automation, a Todoist + Replit integration lets you create tasks from any event: when a form is submitted, when a webhook fires from another service, when a scheduled job runs, or when a user takes an action in your app. Common workflows include creating a Todoist task for every new customer support inquiry, adding daily habit tasks on a schedule, syncing tasks from a project management tool, or building a personal assistant bot that adds tasks based on natural language input.
Todoist's API covers the complete task lifecycle: creating tasks with due dates, priorities, labels, and project assignments; reading tasks filtered by project or label; updating task content; and marking tasks complete. The API also supports comments (for adding notes to tasks), reminders (on premium plans), and sections (for organizing tasks within a project). With less than 10 lines of code for a basic task creation call, Todoist is proof that powerful automation does not need to be complex.
Integration method
Replit connects to Todoist via the Todoist REST API v2 using a personal API token stored in Replit Secrets. Your Express or Flask server makes simple HTTP requests to create tasks, read projects, add labels, and manage task completion. Todoist's API uses straightforward Bearer token authentication with no OAuth flows, making it one of the most beginner-friendly task APIs to integrate.
Prerequisites
- A Todoist account (free tier is sufficient for basic API access)
- Your Todoist API token (from Settings > Integrations > Developer in the Todoist web app)
- A Replit account with a Node.js or Python Repl created
- Basic understanding of HTTP requests and JSON β no prior API experience required
Step-by-step guide
Get your Todoist API token
Get your Todoist API token
Getting your Todoist API token takes less than a minute. Log in to the Todoist web app at todoist.com. Click your avatar or profile picture in the top right corner, then click 'Settings'. In the Settings menu, navigate to the 'Integrations' section (sometimes listed as 'Developer'). Scroll to the bottom of the Integrations page and you will see your API token β a 40-character alphanumeric string. Click 'Copy to clipboard'. This is your personal API token that provides full access to your Todoist account. Unlike OAuth, there is no token expiry or refresh to manage β this token stays valid until you explicitly generate a new one from the same settings page. If you ever need to invalidate the token (for example, if it was accidentally exposed), click 'Copy' again to cycle to a new token β the old one becomes invalid immediately. Todoist does not currently support multiple API tokens or scoped tokens, so the same token is used for all API operations. Keep it safe β store it in Replit Secrets and never put it in your source code.
1# Python β quick test of Todoist API token2# Run this in Replit after adding TODOIST_API_TOKEN to Secrets3import os4try:5 import requests6except ImportError:7 print('Run: pip install requests')8 exit()910token = os.environ.get('TODOIST_API_TOKEN', '')11if not token:12 print('Add TODOIST_API_TOKEN to Replit Secrets (lock icon π)')13 exit()1415response = requests.get(16 'https://api.todoist.com/rest/v2/projects',17 headers={'Authorization': f'Bearer {token}'}18)19if response.status_code == 200:20 projects = response.json()21 print(f'Connected! Found {len(projects)} projects:')22 for p in projects[:5]:23 print(f' {p["name"]} (ID: {p["id"]})')24else:25 print(f'Error: {response.status_code} β check your token')Pro tip: Your Todoist API token is visible to anyone who can see your Replit Secrets, so keep access to your Repl private. Regenerate the token immediately if you accidentally paste it in a code file or commit it to a public repository.
Expected result: The test script prints 'Connected!' and lists your Todoist projects. The token is confirmed working and stored correctly in Replit Secrets.
Store credentials in Replit Secrets
Store credentials in Replit Secrets
Open your Repl in Replit and click the lock icon (π) in the left sidebar to open the Secrets panel. Click 'New Secret', enter TODOIST_API_TOKEN as the key, and paste your Todoist API token as the value. Click 'Add Secret'. That is the only secret you need for basic Todoist API access. If you plan to always create tasks in the same project, also add TODOIST_PROJECT_ID β you can find project IDs by running the test script from Step 1, which lists all your projects with their IDs. Replit Secrets are stored encrypted and injected as environment variables when your Repl runs β they are not visible in your code files or git history. In your Node.js code, access the token as process.env.TODOIST_API_TOKEN. In Python, use os.environ['TODOIST_API_TOKEN']. Install required packages: for Node.js run npm install express axios in the Shell; for Python, the requests library is usually pre-installed but add it to requirements.txt if needed.
1// Node.js β verify Todoist secrets (check-secrets.js)2const token = process.env.TODOIST_API_TOKEN;3const projectId = process.env.TODOIST_PROJECT_ID;45if (!token) {6 console.error('MISSING: TODOIST_API_TOKEN β add via Replit Secrets (lock icon π)');7 process.exit(1);8}9console.log(`OK: TODOIST_API_TOKEN loaded (${token.length} chars)`);10if (projectId) {11 console.log(`OK: TODOIST_PROJECT_ID = ${projectId}`);12} else {13 console.log('INFO: TODOIST_PROJECT_ID not set β tasks will go to Inbox');14}Pro tip: Tasks created without a project_id go to your Todoist Inbox by default. This is fine for testing, but for production integrations, always specify a project_id so automated tasks are kept separate from your personal inbox.
Expected result: TODOIST_API_TOKEN appears in the Replit Secrets panel. The check script confirms it is loaded. If TODOIST_PROJECT_ID is also set, it is confirmed too.
Create tasks with the Todoist REST API
Create tasks with the Todoist REST API
Creating a task in Todoist is a single POST request to https://api.todoist.com/rest/v2/tasks with a JSON body. The only required field is content (the task title). Optional fields include description (longer text, supports Markdown), due_string (natural language date like 'today', 'tomorrow at 3pm', 'every Monday'), due_date (ISO 8601 date), priority (1=normal to 4=urgent), project_id, labels (array of label names), and assignee_id. Todoist's due_string field is particularly powerful β it uses the same natural language parser that the Todoist app uses, so you can write 'next Friday at 2pm' and Todoist will correctly parse the date. The API returns the created task object with its id, url (link to open the task in Todoist), and all other fields. Build a simple Express or Flask server with a task creation endpoint that accepts parameters and proxies them to the Todoist API. This pattern β a Replit server acting as a thin wrapper around the Todoist API β is the foundation for all task automation workflows. Once the endpoint works, you can connect it to any trigger: form submissions, scheduled jobs, webhooks from other services.
1// Node.js β Todoist task creation server (index.js)2const express = require('express');3const axios = require('axios');45const app = express();6app.use(express.json());78const TODOIST_TOKEN = process.env.TODOIST_API_TOKEN;9const DEFAULT_PROJECT_ID = process.env.TODOIST_PROJECT_ID;1011if (!TODOIST_TOKEN) {12 console.error('TODOIST_API_TOKEN not found. Add it in Replit Secrets (lock icon π).');13 process.exit(1);14}1516const todoistApi = axios.create({17 baseURL: 'https://api.todoist.com/rest/v2',18 headers: { Authorization: `Bearer ${TODOIST_TOKEN}`, 'Content-Type': 'application/json' }19});2021// Create a task22app.post('/tasks', async (req, res) => {23 const { content, description, due_string, priority, project_id } = req.body;24 if (!content) return res.status(400).json({ error: 'content is required' });2526 try {27 const task = await todoistApi.post('/tasks', {28 content,29 description: description || '',30 due_string: due_string || 'today',31 priority: priority || 1,32 project_id: project_id || DEFAULT_PROJECT_ID || undefined33 });34 console.log(`Task created: ${task.data.id} β ${content}`);35 res.json({ success: true, task: task.data });36 } catch (err) {37 console.error('Todoist error:', err.response?.data || err.message);38 res.status(500).json({ error: 'Failed to create task' });39 }40});4142// Get all tasks in a project43app.get('/tasks', async (req, res) => {44 try {45 const params = req.query.project_id ? { project_id: req.query.project_id } : {};46 const tasks = await todoistApi.get('/tasks', { params });47 res.json(tasks.data);48 } catch (err) {49 res.status(500).json({ error: err.message });50 }51});5253// Mark a task complete54app.post('/tasks/:id/close', async (req, res) => {55 try {56 await todoistApi.post(`/tasks/${req.params.id}/close`);57 res.json({ success: true });58 } catch (err) {59 res.status(500).json({ error: err.message });60 }61});6263app.listen(3000, '0.0.0.0', () => console.log('Todoist server running on port 3000'));Pro tip: Todoist's due_string understands natural language: 'tomorrow', 'next monday at 10am', 'every weekday', 'in 3 days'. Use this instead of calculating ISO dates manually β it is more readable and handles time zones automatically based on the user's Todoist timezone setting.
Expected result: The server starts on port 3000. A POST to /tasks with a JSON body containing content creates a new task in Todoist. A GET to /tasks returns the list of active tasks. The new task appears in the Todoist app.
Read projects and list tasks with filters
Read projects and list tasks with filters
Beyond creating tasks, the Todoist API lets you read your project structure and query tasks with filters. Use GET /projects to list all projects β each project has an id, name, color, and parent_id (for nested projects). Use GET /tasks to list active tasks, optionally filtered by project_id, label, or filter (Todoist's query language). The filter parameter supports the same queries as the Todoist app's filter feature: 'today', 'overdue', 'p1' (priority 1), '@work' (label 'work'), '#Inbox' (project Inbox), or compound filters like 'today & @work'. Use GET /tasks/{id} to retrieve a single task by its ID. For a complete workflow, build an endpoint that reads tasks from a specific project and returns them formatted for display in your app β this turns your Replit server into a personal task API that any frontend or other service can consume. For deployed integrations, use Autoscale for event-triggered task creation endpoints and Scheduled deployments for periodic tasks like the daily habit generator.
1# Python β read projects and tasks (read_tasks.py)2import requests3import os45TOKEN = os.environ['TODOIST_API_TOKEN']6headers = {'Authorization': f'Bearer {TOKEN}'}78def get_projects():9 resp = requests.get('https://api.todoist.com/rest/v2/projects', headers=headers)10 resp.raise_for_status()11 return resp.json()1213def get_tasks(project_id=None, label=None, filter_query=None):14 params = {}15 if project_id:16 params['project_id'] = project_id17 if label:18 params['label'] = label19 if filter_query:20 params['filter'] = filter_query21 resp = requests.get('https://api.todoist.com/rest/v2/tasks', headers=headers, params=params)22 resp.raise_for_status()23 return resp.json()2425def create_task(content, due_string='today', priority=1, project_id=None):26 payload = {'content': content, 'due_string': due_string, 'priority': priority}27 if project_id:28 payload['project_id'] = project_id29 resp = requests.post('https://api.todoist.com/rest/v2/tasks', json=payload, headers=headers)30 resp.raise_for_status()31 return resp.json()3233# Example usage34projects = get_projects()35print('Projects:')36for p in projects:37 print(f' {p["name"]} (ID: {p["id"]})')3839overdue = get_tasks(filter_query='overdue')40print(f'\nOverdue tasks: {len(overdue)}')41for task in overdue[:5]:42 print(f' [{task.get("priority", 1)}] {task["content"]}')4344# Create a test task45new_task = create_task('Test task from Replit', due_string='tomorrow', priority=2)46print(f'\nCreated: {new_task["id"]} β {new_task["content"]}')Pro tip: When paginating through large task lists, use the cursor-based pagination that the Todoist API supports. For most personal Todoist accounts with fewer than 200 active tasks, a single API call returns all tasks without pagination.
Expected result: The script prints a list of your Todoist projects with their IDs, shows any overdue tasks, and creates a test task that appears in your Todoist app due tomorrow.
Common use cases
Automatic Task Creation from Form Submissions
When a user submits a contact form or support request on your website, your Replit backend creates a Todoist task with the user's name, message, and email as the task content, assigns it to your 'Inbox' or a dedicated 'Support' project, and sets it due today so it appears at the top of your task list.
Build an Express server with a POST /task-from-form endpoint that accepts form data (name, email, message), creates a Todoist task with the message as the content and name/email in the description, assigns it to a specific project ID, marks it as high priority, and returns the created task ID. Store TODOIST_API_TOKEN and TODOIST_PROJECT_ID in Replit Secrets.
Copy this prompt to try it in Replit
Daily Habit Task Generator
A scheduled Replit job runs every morning and creates a set of recurring habit tasks in Todoist β exercise, meditation, reading, journaling β each with the correct due date set to today and assigned to a 'Habits' project. This automates the repetitive manual creation of daily tasks and ensures they always appear in your today view.
Build a Python script that reads a predefined list of daily habits (strings), creates a Todoist task for each with today's date as the due date and priority 2, all assigned to a 'Daily Habits' project. Schedule it to run at 7am using Replit's scheduled deployments. Store TODOIST_API_TOKEN in Replit Secrets.
Copy this prompt to try it in Replit
GitHub Issue to Todoist Task Sync
When a new issue is opened in your GitHub repository, a webhook fires to your Replit server. The server creates a corresponding Todoist task with the issue title, URL, and repository name, labels it 'bug' or 'feature' based on the GitHub issue labels, and assigns it to your development project. All GitHub issues become visible in your personal task manager without manual copying.
Build an Express webhook handler at /github-webhook that accepts GitHub issue webhook payloads, extracts the issue title, number, and URL when the action is 'opened', creates a Todoist task with '[GitHub #number] title' as the content and the issue URL in the description, and returns a 200 confirmation.
Copy this prompt to try it in Replit
Troubleshooting
API returns 401 Unauthorized on every request
Cause: The TODOIST_API_TOKEN is missing from Replit Secrets, or it is being passed incorrectly in the Authorization header.
Solution: Open Replit Secrets (lock icon π) and confirm TODOIST_API_TOKEN is present and correct. Verify the Authorization header format is exactly 'Bearer YOUR_TOKEN' with a capital B and a space. The token should be 40 characters β if it is shorter, you may have copied a partial value.
1// Correct Authorization header format2const headers = { Authorization: `Bearer ${process.env.TODOIST_API_TOKEN}` };Tasks are created but do not appear in the expected project
Cause: The project_id was not specified in the API call, so tasks default to the Inbox, or the project_id value in Replit Secrets does not match the intended project.
Solution: Call GET /projects to list all projects and their IDs. Confirm the TODOIST_PROJECT_ID in Replit Secrets matches the ID of your intended project exactly. Always log the project_id being sent to confirm it is not undefined.
1// Debug: log the project_id being used2console.log('Using project_id:', process.env.TODOIST_PROJECT_ID || 'NOT SET (will use Inbox)');Task due dates are being set to wrong dates or timezone
Cause: When using due_date (ISO 8601 format) instead of due_string, Todoist uses UTC time. If your tasks are appearing on the wrong day, there may be a timezone offset issue.
Solution: Prefer due_string over due_date for human-readable dates ('today', 'tomorrow', 'next Monday'). Todoist uses the account's configured timezone for due_string parsing. If using due_date, be aware that it is treated as UTC midnight.
1// Prefer due_string for reliable date handling2{3 content: 'Task title',4 due_string: 'tomorrow at 9am' // Todoist parses this in the user's timezone5}GET /tasks returns an empty array even though tasks exist in the Todoist app
Cause: Completed tasks are not returned by the GET /tasks endpoint. Only active (not yet completed) tasks are listed. If all tasks in the project were recently completed, the list will be empty.
Solution: The Todoist REST API does not provide a simple way to list completed tasks β that requires the Sync API. For active tasks, verify tasks are not completed in the app. Use the filter parameter (e.g., filter='today') to narrow the query and confirm you are looking in the right project.
Best practices
- Store TODOIST_API_TOKEN in Replit Secrets (lock icon π) β it provides full account access and must never appear in source code or git history
- Always specify a project_id when creating tasks via automation so they do not clutter your personal Inbox
- Use due_string with natural language ('today', 'tomorrow at 9am', 'every Monday') rather than calculating ISO dates β it is more readable and handles timezones automatically
- Include a descriptive description field in automated tasks with enough context for you to understand the origin (e.g., 'Created from form submission by user@example.com via Replit webhook')
- Set meaningful priorities (1-4) on automated tasks β priority 4 (urgent) is the highest and will appear at the top of your task list
- Deploy as Autoscale for form submission handlers and webhook receivers; use Scheduled deployments for daily habit task generators
- Log the task ID returned from every creation call β this lets you update or close specific tasks later without needing to search by title
Alternatives
Asana is better suited for team project management with task dependencies, timelines, and team workspaces, while Todoist is optimized for personal productivity and small teams.
Trello uses a visual kanban board model with a simple REST API, making it a good alternative if you prefer cards and boards over a list-based task interface.
ClickUp provides a more feature-rich project management platform with a broader API, suitable for teams that need more than personal task lists.
Notion's database feature can function as a task manager with richer content per item, better suited if you want tasks embedded in a larger knowledge management system.
Frequently asked questions
How do I connect Replit to Todoist?
Get your API token from Todoist Settings > Integrations > Developer, store it as TODOIST_API_TOKEN in Replit Secrets (lock icon π), and make HTTP requests to https://api.todoist.com/rest/v2 with the token in an Authorization: Bearer header from your Node.js or Python server.
Does Replit work with Todoist for free?
Yes. Todoist's free tier includes full REST API access for basic task management (creating, reading, and completing tasks). Some features like reminders and comments require a Todoist Pro subscription. Replit's free tier is sufficient for development and testing.
How do I store the Todoist API key in Replit?
Click the lock icon (π) in the Replit sidebar to open Secrets. Add a new secret with key TODOIST_API_TOKEN and your 40-character API token as the value. Access it in Node.js with process.env.TODOIST_API_TOKEN or in Python with os.environ['TODOIST_API_TOKEN']. This is the only credential you need for Todoist.
Can I create recurring tasks via the Todoist API?
Yes. Use the due_string field with a recurring pattern like 'every day', 'every Monday', 'every month on the 1st', or 'every weekday at 9am'. Todoist's natural language parser understands these patterns the same way the app does. The API also accepts due_lang to specify the language for date parsing.
How do I find my Todoist project ID for the API?
Call GET https://api.todoist.com/rest/v2/projects with your Bearer token. The response is a JSON array of all your projects, each with an id, name, and other properties. Copy the id of the project you want to use and store it as TODOIST_PROJECT_ID in Replit Secrets.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation