Summary and recommendation
Todoist exposes two distinct API surfaces: REST v2 (`https://api.todoist.com/rest/v2`) for resource-level operations and Sync v9 (`https://api.todoist.com/sync/v9/sync`) for incremental state sync.
Both surfaces are strictly user-scoped - all operations execute in the context of the authenticated token holder.
There is no admin or service-account API for managing, listing, or deprovisioning other workspace members.
Auth is Bearer token via personal API token or OAuth 2.0;
personal tokens do not expire and OAuth tokens do not expire unless explicitly revoked via `POST /oauth/revoke_token`.
For identity graph construction, the available user-level fields are `id`, `email`, `full_name`, `is_premium`, `premium_until`, `team_inbox_id`, `timezone`, and `lang`.
Cross-user identity resolution is limited: the `/projects/{project_id}/collaborators` endpoint returns only `id`, `name`, and `email` per collaborator - not the full user object - and only for projects where `is_shared: true`.
There is no endpoint to enumerate all workspace members or retrieve an arbitrary user profile by ID.
API quick reference
| Has user API | Yes |
| Auth method | Bearer token (personal API token or OAuth 2.0 access token) |
| Base URL | Official docs |
| SCIM available | No |
| SCIM plan required | Business |
Authentication
Auth method: Bearer token (personal API token or OAuth 2.0 access token)
Setup steps
- Personal token: Log in to Todoist → Settings → Integrations → Developer → copy the API token.
- OAuth 2.0: Register an application at https://developer.todoist.com/appconsole.html to obtain client_id and client_secret.
- OAuth flow: Redirect user to https://todoist.com/oauth/authorize with client_id, scope, and state parameters.
- Exchange the returned code at https://todoist.com/oauth/access_token for an access_token.
- Include the token in all requests as: Authorization: Bearer
Required scopes
| Scope | Description | Required for |
|---|---|---|
| data:read | Read-only access to all user data (tasks, projects, labels, etc.). | Reading user profile and task data |
| data:read_write | Read and write access to all user data. | Creating, updating, and deleting tasks, projects, and user settings |
| data:delete | Permission to delete user data. | Deleting tasks, projects, and other resources |
| project:delete | Permission to delete projects. | Project deletion operations |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | string | Unique user identifier. | system-assigned | immutable | Stable identifier across all API versions. |
| string | User's email address. | required | updatable | Used as login credential. | |
| full_name | string | User's display name. | required | updatable | |
| avatar_big | string (URL) | URL to the large avatar image. | system-assigned | read-only via API | Also available as avatar_medium, avatar_small, avatar_s640. |
| premium_until | string (ISO 8601) | null | Timestamp until which the user has premium access; null if free plan. | system-assigned | read-only | |
| is_premium | boolean | Whether the user currently has a premium (Pro or Business) subscription. | system-assigned | read-only | |
| inbox_project_id | string | ID of the user's Inbox project. | system-assigned | read-only | |
| team_inbox_id | string | null | ID of the team inbox project if user belongs to a team. | system-assigned | read-only | |
| lang | string | User's language setting (e.g., 'en'). | optional | updatable | |
| timezone | string | User's timezone (e.g., 'America/New_York'). | optional | updatable | |
| start_day | integer | Day the week starts for the user (1=Monday, 7=Sunday). | optional | updatable | |
| next_week | integer | Day used as 'next week' in date shortcuts. | optional | updatable | |
| date_format | integer | Date display format preference (0=DD-MM-YYYY, 1=MM-DD-YYYY). | optional | updatable | |
| time_format | integer | Time display format (0=24h, 1=12h). | optional | updatable | |
| sort_order | integer | Default task sort order preference. | optional | updatable | |
| daily_goal | integer | User's daily task completion goal. | optional | updatable | |
| weekly_goal | integer | User's weekly task completion goal. | optional | updatable | |
| karma | float | User's Todoist Karma score. | system-assigned | read-only | |
| karma_trend | string | Direction of karma trend ('up', 'down', or empty). | system-assigned | read-only | |
| token | string | User's personal API token (only returned in Sync API user resource). | system-assigned | read-only | Returned by Sync API; not exposed in REST API v2 user endpoint. |
Core endpoints
Get current user
- Method: GET
- URL:
https://api.todoist.com/rest/v2/user - Watch out for: Returns only the authenticated user's own profile. There is no admin endpoint to retrieve arbitrary users.
Request example
curl -X GET https://api.todoist.com/rest/v2/user \
-H "Authorization: Bearer $TOKEN"
Response example
{
"id": "2671355",
"email": "user@example.com",
"full_name": "Jane Doe",
"is_premium": true,
"timezone": "America/New_York",
"karma": 12345.0
}
Update current user
- Method: POST
- URL:
https://api.todoist.com/rest/v2/user/update - Watch out for: Uses POST, not PATCH. Only the authenticated user can update their own profile.
Request example
curl -X POST https://api.todoist.com/rest/v2/user/update \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"full_name": "Jane Smith", "lang": "en"}'
Response example
{
"id": "2671355",
"email": "user@example.com",
"full_name": "Jane Smith",
"lang": "en"
}
Get all projects
- Method: GET
- URL:
https://api.todoist.com/rest/v2/projects - Watch out for: Returns projects visible to the authenticated user only; no cross-user project listing.
Request example
curl -X GET https://api.todoist.com/rest/v2/projects \
-H "Authorization: Bearer $TOKEN"
Response example
[
{
"id": "220474322",
"name": "Inbox",
"is_shared": false,
"is_team_inbox": false
}
]
Get project collaborators
- Method: GET
- URL:
https://api.todoist.com/rest/v2/projects/{project_id}/collaborators - Watch out for: Only returns collaborators for shared projects. Returns empty array for personal projects.
Request example
curl -X GET https://api.todoist.com/rest/v2/projects/220474322/collaborators \
-H "Authorization: Bearer $TOKEN"
Response example
[
{
"id": "2671355",
"name": "Jane Doe",
"email": "user@example.com"
}
]
Sync user data (Sync API)
- Method: POST
- URL:
https://api.todoist.com/sync/v9/sync - Watch out for: Sync API returns the user's personal API token in the response. Use sync_token='*' for full sync; subsequent calls use the returned sync_token for incremental updates.
Request example
curl -X POST https://api.todoist.com/sync/v9/sync \
-H "Authorization: Bearer $TOKEN" \
-d 'sync_token=*&resource_types=["user"]'
Response example
{
"sync_token": "VHJhbnNhY3Rpb24...",
"user": {
"id": "2671355",
"email": "user@example.com",
"token": "abc123..."
}
}
Revoke OAuth access token
- Method: POST
- URL:
https://todoist.com/oauth/revoke_token - Watch out for: Revokes the specified OAuth access token. Does not revoke the user's personal API token.
Request example
curl -X POST https://todoist.com/oauth/revoke_token \
-d 'client_id=CLIENT_ID&client_secret=CLIENT_SECRET&access_token=TOKEN'
Response example
{}
Get tasks (for user activity auditing)
- Method: GET
- URL:
https://api.todoist.com/rest/v2/tasks - Watch out for: Scoped to the authenticated user. No admin-level cross-user task listing is available via REST API.
Request example
curl -X GET "https://api.todoist.com/rest/v2/tasks?project_id=220474322" \
-H "Authorization: Bearer $TOKEN"
Response example
[
{
"id": "2995104339",
"content": "Buy milk",
"creator_id": "2671355",
"assignee_id": null
}
]
Exchange OAuth authorization code for access token
- Method: POST
- URL:
https://todoist.com/oauth/access_token - Watch out for: Authorization code is single-use and short-lived. The returned access_token does not expire unless explicitly revoked.
Request example
curl -X POST https://todoist.com/oauth/access_token \
-d 'client_id=CLIENT_ID&client_secret=CLIENT_SECRET&code=AUTH_CODE'
Response example
{
"access_token": "0123456789abcdef...",
"token_type": "Bearer"
}
Rate limits, pagination, and events
- Rate limits: Todoist enforces a rate limit of 1000 requests per 15-minute window per user token. Exceeding the limit returns HTTP 429.
- Rate-limit headers: No
- Retry-After header: No
- Rate-limit notes: Official docs state the 1000/15-min limit. No explicit documentation of rate-limit response headers or Retry-After header behavior was found in current official docs.
- Pagination method: cursor
- Default page size: 0
- Max page size: 0
- Pagination pointer: cursor
| Plan | Limit | Concurrent |
|---|---|---|
| All plans | 1000 requests per 15 minutes per token | 0 |
- Webhooks available: Yes
- Webhook notes: Todoist supports webhooks via the app console. Webhooks deliver event payloads via HTTP POST to a configured endpoint URL when specified events occur.
- Alternative event strategy: Polling the Sync API with incremental sync_token is the alternative for detecting changes without webhooks.
- Webhook events: item:added, item:updated, item:deleted, item:completed, item:uncompleted, note:added, note:updated, note:deleted, project:added, project:updated, project:deleted, project:archived, project:unarchived, label:added, label:updated, label:deleted, filter:added, filter:updated, filter:deleted, reminder:fired
SCIM API status
- SCIM available: No
- SCIM version: Not documented
- Plan required: Business
- Endpoint: Not documented
Limitations:
- Todoist does not offer a native SCIM 2.0 API endpoint.
- SSO (SAML) is available on the Business plan via Okta and OneLogin, but automated user provisioning/deprovisioning via SCIM is not natively supported.
- User provisioning must be handled manually or via third-party integration platforms (e.g., Okta SCIM connector if available through the IdP's app catalog).
- Business plan is required for SSO; pricing increased in December 2025 to $10/user/mo (monthly) or $8/user/mo (annual).
Common scenarios
Three integration patterns are well-supported by the current API surface.
First, authenticated user profile retrieval: GET /rest/v2/user returns the full user object for the token holder;
parse id, email, full_name, is_premium, and timezone for downstream identity enrichment.
Second, collaborator enumeration across shared projects: chain GET /rest/v2/projects (filter on is_shared: true) with GET /rest/v2/projects/{project_id}/collaborators per shared project to build a partial identity graph of active collaborators;
note that returned objects are minimal (id, name, email only).
Third, OAuth 2.0 integration lifecycle: register at https://developer.todoist.com/appconsole.html, drive the standard authorization code flow to https://todoist.com/oauth/authorize, exchange at https://todoist.com/oauth/access_token, and revoke explicitly at https://todoist.com/oauth/revoke_token - validate the state parameter to prevent CSRF.
Rate limit is 1,000 requests per 15-minute window per token;
HTTP 429 is returned on breach, with no documented Retry-After header behavior.
Retrieve and display authenticated user profile
- Obtain a Bearer token (personal API token from Settings → Integrations, or OAuth 2.0 access token).
- GET https://api.todoist.com/rest/v2/user with Authorization: Bearer
. - Parse the returned JSON for id, email, full_name, is_premium, timezone, and karma fields.
- Display or store the profile data as needed.
Watch out for: Only the authenticated user's own profile is accessible. No endpoint exists to fetch another user's profile by ID.
List collaborators on a shared project
- Authenticate with a token that has data:read scope.
- GET https://api.todoist.com/rest/v2/projects to retrieve project IDs.
- Identify shared projects by checking is_shared: true in the response.
- GET https://api.todoist.com/rest/v2/projects/{project_id}/collaborators for each shared project.
- Collect collaborator objects (id, name, email) from the response array.
Watch out for: Returns an empty array for non-shared projects. Collaborator objects contain only id, name, and email - not the full user profile.
Implement OAuth 2.0 authorization for a third-party integration
- Register the application at https://developer.todoist.com/appconsole.html; obtain client_id and client_secret.
- Redirect the user to https://todoist.com/oauth/authorize?client_id=CLIENT_ID&scope=data:read_write&state=RANDOM_STATE.
- After user approves, receive the authorization code at the configured redirect_uri.
- POST to https://todoist.com/oauth/access_token with client_id, client_secret, and code to exchange for access_token.
- Store the access_token securely; include it as Authorization: Bearer
in all subsequent API requests. - To revoke access, POST to https://todoist.com/oauth/revoke_token with client_id, client_secret, and access_token.
Watch out for: OAuth access tokens do not expire automatically. Implement explicit revocation on user disconnect. The state parameter should be validated to prevent CSRF attacks.
Why building this yourself is a trap
The primary integration trap is assuming the REST API can support admin-level user management - it cannot. There is no endpoint to list workspace members, fetch another user's profile by ID, or programmatically remove a user from the workspace.
Developers building identity graph pipelines should expect collaborator coverage to be partial: only users who appear as collaborators on shared projects accessible to the token holder will surface, and only with minimal fields. SCIM 2.0 is not natively available; SSO via SAML (Okta, OneLogin) is Business-plan-only but does not include automated provisioning.
The Sync API (v9) and REST API (v2) have different base URLs and different response shapes, and the Sync API returns the user's personal API token in its response payload - a credential exposure risk if Sync API responses are logged or forwarded without scrubbing. OAuth scopes are coarse-grained (data:read, data:read_write, data:delete, project:delete);
there are no admin-scoped or user-management-scoped grants.
Automate Todoist workflows without one-off scripts
Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.