Search projects by criteria
Lists every non-template project the caller may see, filtered by up to 15 include/exclude ID lists and a text search, with hour aggregates.
Returns all matching non-template projects in ONE unpaged TAF list, ordered by project number then name, with list Properties: ProjectsCount, TotalBudgetWorkHours, TotalProjectActualHours, TotalTasksBudgetHours, TotalAllocationsBudgetHours (numbers as en-GB strings) and IsShowingProjectStage / IsShowingProjectCategory (account features, booleans). Every filter is an optional comma-separated ID list with an is…Inclusive switch (true = keep projects whose value is in the list, false = drop them); an empty list means no filter; filters AND together. searchText is a LIKE '%text%' over customer number and name, project number, name and description. A caller without project privileges gets an empty list.
Use it to:
- Project overview / portfolio list with the same filters as the TimeLog project list
- Find the projectId values to pass to the bulk status / stage / resource-planner / delete operations
Not for:
- Paging through large accounts — there is no paging; use
GET /v1/project/get-all - A project picker for time registration →
GET /v1/project-header/search-for-time-tracking-order-by-recent-registration
Rules across fields:
- R1: Each is…Inclusive switch is used only when its ID list is non-empty.
Behaviour:
- Idempotent.
- Safe to retry.
- Changes are visible to the next read immediately.
Call first: GET /v2/filters/list/{pageIdentifier}
Next: GET /v2/projects/actions, GET /v2/projects/{projectId}/time-registrations
See also: GET /v1/project/get-all, GET /v2/filters/project-status/content
Not yet documented: request → query params → contractStatusIds; response → empty result.
curl -X GET "https://app3.timelog.com/your-account/api/v2/projects?searchText=example_string&customerIds=example_string&isCustomerInclusive=true&projectDepartmentIds=example_string&isProjectDepartmentInclusive=true&legalEntityProjectIds=example_string&isLegalEntityProjectInclusive=true&accountManagerIds=example_string&isAccountManagerInclusive=true&contractModelIds=example_string&isContractModelInclusive=true&contractStatusIds=example_string&isContractStatusInclusive=true&projectStatusIds=123&isProjectStatusInclusive=true&projectManagerIds=example_string&isProjectManagerInclusive=true&projectTypeIds=example_string&isProjectTypeInclusive=true&projectCategoryIds=example_string&isProjectCategoryInclusive=true&projectStageIds=example_string&isProjectStageInclusive=true&customerOwnerIds=example_string&isCustomerOwnerInclusive=true&contactIds=example_string&isContactInclusive=true&partnerIds=example_string&isPartnerInclusive=true&projectInactivityPeriodIds=example_string" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN"
import requests
import json
url = "https://app3.timelog.com/your-account/api/v2/projects?searchText=example_string&customerIds=example_string&isCustomerInclusive=true&projectDepartmentIds=example_string&isProjectDepartmentInclusive=true&legalEntityProjectIds=example_string&isLegalEntityProjectInclusive=true&accountManagerIds=example_string&isAccountManagerInclusive=true&contractModelIds=example_string&isContractModelInclusive=true&contractStatusIds=example_string&isContractStatusInclusive=true&projectStatusIds=123&isProjectStatusInclusive=true&projectManagerIds=example_string&isProjectManagerInclusive=true&projectTypeIds=example_string&isProjectTypeInclusive=true&projectCategoryIds=example_string&isProjectCategoryInclusive=true&projectStageIds=example_string&isProjectStageInclusive=true&customerOwnerIds=example_string&isCustomerOwnerInclusive=true&contactIds=example_string&isContactInclusive=true&partnerIds=example_string&isPartnerInclusive=true&projectInactivityPeriodIds=example_string"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
}
response = requests.get(url, headers=headers)
print(response.json())
const response = await fetch("https://app3.timelog.com/your-account/api/v2/projects?searchText=example_string&customerIds=example_string&isCustomerInclusive=true&projectDepartmentIds=example_string&isProjectDepartmentInclusive=true&legalEntityProjectIds=example_string&isLegalEntityProjectInclusive=true&accountManagerIds=example_string&isAccountManagerInclusive=true&contractModelIds=example_string&isContractModelInclusive=true&contractStatusIds=example_string&isContractStatusInclusive=true&projectStatusIds=123&isProjectStatusInclusive=true&projectManagerIds=example_string&isProjectManagerInclusive=true&projectTypeIds=example_string&isProjectTypeInclusive=true&projectCategoryIds=example_string&isProjectCategoryInclusive=true&projectStageIds=example_string&isProjectStageInclusive=true&customerOwnerIds=example_string&isCustomerOwnerInclusive=true&contactIds=example_string&isContactInclusive=true&partnerIds=example_string&isPartnerInclusive=true&projectInactivityPeriodIds=example_string", {
method: "GET",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
}
});
const data = await response.json();
console.log(data);
package main
import (
"fmt"
"net/http"
)
func main() {
req, err := http.NewRequest("GET", "https://app3.timelog.com/your-account/api/v2/projects?searchText=example_string&customerIds=example_string&isCustomerInclusive=true&projectDepartmentIds=example_string&isProjectDepartmentInclusive=true&legalEntityProjectIds=example_string&isLegalEntityProjectInclusive=true&accountManagerIds=example_string&isAccountManagerInclusive=true&contractModelIds=example_string&isContractModelInclusive=true&contractStatusIds=example_string&isContractStatusInclusive=true&projectStatusIds=123&isProjectStatusInclusive=true&projectManagerIds=example_string&isProjectManagerInclusive=true&projectTypeIds=example_string&isProjectTypeInclusive=true&projectCategoryIds=example_string&isProjectCategoryInclusive=true&projectStageIds=example_string&isProjectStageInclusive=true&customerOwnerIds=example_string&isCustomerOwnerInclusive=true&contactIds=example_string&isContactInclusive=true&partnerIds=example_string&isPartnerInclusive=true&projectInactivityPeriodIds=example_string", nil)
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
fmt.Println("Response Status:", resp.Status)
}
require 'net/http'
require 'json'
uri = URI('https://app3.timelog.com/your-account/api/v2/projects?searchText=example_string&customerIds=example_string&isCustomerInclusive=true&projectDepartmentIds=example_string&isProjectDepartmentInclusive=true&legalEntityProjectIds=example_string&isLegalEntityProjectInclusive=true&accountManagerIds=example_string&isAccountManagerInclusive=true&contractModelIds=example_string&isContractModelInclusive=true&contractStatusIds=example_string&isContractStatusInclusive=true&projectStatusIds=123&isProjectStatusInclusive=true&projectManagerIds=example_string&isProjectManagerInclusive=true&projectTypeIds=example_string&isProjectTypeInclusive=true&projectCategoryIds=example_string&isProjectCategoryInclusive=true&projectStageIds=example_string&isProjectStageInclusive=true&customerOwnerIds=example_string&isCustomerOwnerInclusive=true&contactIds=example_string&isContactInclusive=true&partnerIds=example_string&isPartnerInclusive=true&projectInactivityPeriodIds=example_string')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Content-Type'] = 'application/json'
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
response = http.request(request)
puts response.body
{
"Entities": [
{
"Properties": {
"Id": "123e4567-e89b-12d3-a456-426614174000",
"ProjectId": 123,
"Name": "John Doe",
"No": "example_string",
"CustomerId": 123,
"CustomerName": "John Doe",
"CustomerOwnerId": 123,
"LegalEntityId": 123,
"ContactId": 123,
"Description": "example_string",
"DepartmentId": 123,
"ProjectManagerId": 123,
"ProjectManagerName": "John Doe",
"ProjectManagerInitials": "example_string",
"AccountManagerId": 123,
"AccountManagerName": "John Doe",
"AccountManagerInitials": "example_string",
"PartnerId": 123,
"ProjectTypeId": 123,
"ProjectTypeName": "John Doe",
"ProjectCategoryId": 123,
"ProjectCategoryName": "John Doe",
"ProjectStageId": 123,
"ProjectStageName": "John Doe",
"BudgetWorkHours": 3.14,
"ProjectActualHours": 3.14,
"TasksBudgetHours": 3.14,
"AllocationsBudgetHours": 3.14,
"StatusId": 123,
"StatusName": "John Doe",
"HasAccountingIssues": true,
"Completion": 3.14,
"ContractModels": [
"null"
]
},
"Links": [
{
"Href": "example_string",
"Rel": "example_string"
}
],
"Actions": [
{}
]
}
],
"Links": [
{
"Href": "example_string",
"Rel": "example_string"
}
]
}
{
"error": "Unauthorized",
"message": "Authentication required. Please provide a valid API token",
"code": 401
}
{
"error": "Internal Server Error",
"message": "An unexpected error occurred on the server",
"code": 500,
"requestId": "req_1234567890"
}
/v2/projects
Target server for requests. Edit to use your own host.
OAuth 2.0 bearer token, sent as Authorization: Bearer <token>. JWT and reference tokens are both accepted. The token must carry the required scope (default tlp).
The token's sub claim is the acting user. A token WITHOUT sub (client-credentials) acts as the system service user. If the sub user cannot log in (inactive, locked, deleted), the request is rejected with 401 — it is no longer let through without a user.
> Not yet documented: how to obtain a token.
> Not yet documented: token lifetime.
Authorization: Bearer <token>. JWT and reference tokens are both accepted. The token must carry the required scope (default tlp).
The token's sub claim is the acting user. A token WITHOUT sub (client-credentials) acts as the system service user. If the sub user cannot log in (inactive, locked, deleted), the request is rejected with 401 — it is no longer let through without a user.
> Not yet documented: how to obtain a token.
> Not yet documented: token lifetime.LIKE '%text%' on customer number, customer name, project number, project name and project description (OR). Wildcards % and _ are not escaped. Empty = no text filter.
Filters on SearchProjectRead.No, Name, Description, CustomerName (+ customer number) (contains).
Customer IDs. Each entry must parse as an integer.
Several values: one string, separated by ,.
true = only these customers; false = all but these.
Filters on customerIds (in).
Project (handling) department IDs, matched exactly (0 = projects without department); no subtree expansion.
Several values: one string, separated by ,.
Include / exclude switch.
Filters on projectDepartmentIds (in).
Project legal-entity IDs.
Several values: one string, separated by ,.
Include / exclude switch.
Filters on legalEntityProjectIds (in).
Account manager EMPLOYEE IDs.
Several values: one string, separated by ,.
Include / exclude switch.
Filters on accountManagerIds (in).
ContractModelType values. Matched per CONTRACT row: with isContractModelInclusive=false a project still matches when any other contract has another model.
Several values: one string, separated by ,.
Include / exclude switch.
Filters on contractModelIds (in).
Contract status values; per contract row like contractModelIds.
Several values: one string, separated by ,.
Filters (in).
Valid values come from GET /v2/filters/contract-status/content.
Include / exclude switch.
Filters on contractStatusIds (in).
PAGE-FILTER status codes, not StatusId: 1–7 map to StatusId 0–6 (1 = Quote … 7 = Cancelled); -1 = active group (Quote, Approved, InProgress, OnHold), -2 = inactive group (Completed, Archived, Cancelled), -3 = all. Any other integer passes through unchanged (so 0 also means Quote). When a group code is present the individual codes are ignored, and only one group applies: -3 over -2 over -1. Same codes as GET /v2/filters/project-status/content.
Several values: one string, separated by ,.
Filters on SearchProjectRead.StatusId (in).
Allowed values:
| Value | Name | Meaning |
|---|---|---|
| -3 | All | Every status 0–6. |
| -2 | Inactive | Completed, Archived, Cancelled (4, 5, 6). |
| -1 | Active | Quote, Approved, InProgress, OnHold (0–3). |
| 1 | Quote | StatusId 0. |
| 2 | Approved | StatusId 1. |
| 3 | InProgress | StatusId 2. |
| 4 | OnHold | StatusId 3. |
| 5 | Completed | StatusId 4. |
| 6 | Archived | StatusId 5. |
| 7 | Cancelled | StatusId 6. |
Include / exclude switch.
Filters on projectStatusIds (in).
Project manager EMPLOYEE IDs.
Several values: one string, separated by ,.
Include / exclude switch.
Filters on projectManagerIds (in).
Project type IDs.
Several values: one string, separated by ,.
Include / exclude switch.
Filters on projectTypeIds (in).
Project category IDs.
Several values: one string, separated by ,.
Include / exclude switch.
Filters on projectCategoryIds (in).
Project stage IDs (current stage). Projects without a stage never match an inclusive list and always survive an exclusive one.
Several values: one string, separated by ,.
Include / exclude switch.
Filters on projectStageIds (in).
Customer-owner EMPLOYEE IDs. Not echoed in the response.
Several values: one string, separated by ,.
Filters (in).
Valid values come from GET /v2/filters/customer-owner/content.
Include / exclude switch.
Filters on customerOwnerIds (in).
Customer contact-person IDs.
Several values: one string, separated by ,.
Include / exclude switch.
Filters on contactIds (in).
Partner IDs.
Several values: one string, separated by ,.
Include / exclude switch.
Filters on partnerIds (in).
ONE number of days N (despite the plural name): keep only projects whose newest registration LastModified — or, without registrations, whose creation — is older than N days. Parsed with TryParse: a list or a non-number silently means 0 = no filter.
Filters on date of the latest time registration (or project creation when none) (lt).
Valid values come from GET /v2/filters/project-inactivity-period/content.
Request Preview
Response
Response will appear here after sending the request
Authentication
Bearer token. OAuth 2.0 bearer token, sent as Authorization: Bearer <token>. JWT and reference tokens are both accepted. The token must carry the required scope (default tlp).
The token's sub claim is the acting user. A token WITHOUT sub (client-credentials) acts as the system service user. If the sub user cannot log in (inactive, locked, deleted), the request is rejected with 401 — it is no longer let through without a user.
Not yet documented: how to obtain a token.
Not yet documented: token lifetime.
Query Parameters
LIKE '%text%' on customer number, customer name, project number, project name and project description (OR). Wildcards % and _ are not escaped. Empty = no text filter.
Filters on SearchProjectRead.No, Name, Description, CustomerName (+ customer number) (contains).
Customer IDs. Each entry must parse as an integer.
Several values: one string, separated by ,.
true = only these customers; false = all but these.
Filters on customerIds (in).
Project (handling) department IDs, matched exactly (0 = projects without department); no subtree expansion.
Several values: one string, separated by ,.
Include / exclude switch.
Filters on projectDepartmentIds (in).
Project legal-entity IDs.
Several values: one string, separated by ,.
Include / exclude switch.
Filters on legalEntityProjectIds (in).
Account manager EMPLOYEE IDs.
Several values: one string, separated by ,.
Include / exclude switch.
Filters on accountManagerIds (in).
ContractModelType values. Matched per CONTRACT row: with isContractModelInclusive=false a project still matches when any other contract has another model.
Several values: one string, separated by ,.
Include / exclude switch.
Filters on contractModelIds (in).
Contract status values; per contract row like contractModelIds.
Several values: one string, separated by ,.
Filters (in).
Valid values come from GET /v2/filters/contract-status/content.
Include / exclude switch.
Filters on contractStatusIds (in).
PAGE-FILTER status codes, not StatusId: 1–7 map to StatusId 0–6 (1 = Quote … 7 = Cancelled); -1 = active group (Quote, Approved, InProgress, OnHold), -2 = inactive group (Completed, Archived, Cancelled), -3 = all. Any other integer passes through unchanged (so 0 also means Quote). When a group code is present the individual codes are ignored, and only one group applies: -3 over -2 over -1. Same codes as GET /v2/filters/project-status/content.
Several values: one string, separated by ,.
Filters on SearchProjectRead.StatusId (in).
Allowed values:
| Value | Name | Meaning |
|---|---|---|
-3 | All | Every status 0–6. |
-2 | Inactive | Completed, Archived, Cancelled (4, 5, 6). |
-1 | Active | Quote, Approved, InProgress, OnHold (0–3). |
1 | Quote | StatusId 0. |
2 | Approved | StatusId 1. |
3 | InProgress | StatusId 2. |
4 | OnHold | StatusId 3. |
5 | Completed | StatusId 4. |
6 | Archived | StatusId 5. |
7 | Cancelled | StatusId 6. |
-3-2-11234567Include / exclude switch.
Filters on projectStatusIds (in).
Project manager EMPLOYEE IDs.
Several values: one string, separated by ,.
Include / exclude switch.
Filters on projectManagerIds (in).
Project type IDs.
Several values: one string, separated by ,.
Include / exclude switch.
Filters on projectTypeIds (in).
Project category IDs.
Several values: one string, separated by ,.
Include / exclude switch.
Filters on projectCategoryIds (in).
Project stage IDs (current stage). Projects without a stage never match an inclusive list and always survive an exclusive one.
Several values: one string, separated by ,.
Include / exclude switch.
Filters on projectStageIds (in).
Customer-owner EMPLOYEE IDs. Not echoed in the response.
Several values: one string, separated by ,.
Filters (in).
Valid values come from GET /v2/filters/customer-owner/content.
Include / exclude switch.
Filters on customerOwnerIds (in).
Customer contact-person IDs.
Several values: one string, separated by ,.
Include / exclude switch.
Filters on contactIds (in).
Partner IDs.
Several values: one string, separated by ,.
Include / exclude switch.
Filters on partnerIds (in).
ONE number of days N (despite the plural name): keep only projects whose newest registration LastModified — or, without registrations, whose creation — is older than N days. Parsed with TryParse: a list or a non-number silently means 0 = no filter.
Filters on date of the latest time registration (or project creation when none) (lt).
Valid values come from GET /v2/filters/project-inactivity-period/content.
Responses
Omitted (not []) when the list is empty.