MENU navbar-image

Introduction

Welcome to the Timing API reference.

If you prefer a more interactive presentation of the API, you can download our Postman Collection and open it in a tool like Postman or Paw.

We also offer three Siri shortcuts that demonstrate starting and stopping timers via the API:

The "Start Fixed Timing Timer" shortcut will ask you once for a combination of timer title and project name. From then on, it will always launch a shortcut with that combination of title and project. You can create several copies of this shortcut (e.g. by duplicating it), each with a different title/project combination.

Use Cases

Siri Shortcuts and Automation

Using the Timing API, you can quickly create Siri shortcuts to e.g. start and stop timers. During installation, the shortcuts will ask you for an API which you can generate in the 'API Keys' section of the web app. Once installed, simply run the corresponding shortcut to start or stop a timer.

The "Start Fixed Timing Timer" shortcut will ask you once for a combination of timer title and project name. From then on, it will always launch a shortcut with that combination of title and project. You can create several copies of this shortcut (e.g. by duplicating it), each with a different title/project combination.

Feel free to customize these shortcuts to your liking, e.g. by changing the "Start Timer" shortcut to let you select from a couple of preset titles instead. We are also interested in the shortcuts you create, so please let us know what you build with this!

You could also create scripts that start or stop a timer whenever you perform a specific action; see Start a new timer. and Stop the currently running timer. for the corresponding API calls.

Integrating with your billing system

The API also makes it possible to integrate with whatever billing system you are currently using. Simply retrieve your most recent time entries, then send them to your billing system in the desired format. You can also create a script to create a custom report in exactly the format you need, of course.

Make sure to have a look at the ?include_project_data=true query parameter to include the corresponding project's attributes in the response. This lets you retrieve project titles without having to do a second API call to the "Projects" collection.

GrandTotal integration

The GrandTotal plugin for Timing already uses the Timing Web API to import your team members' time entries. To use that functionality, please refer the corresponding section in the documentation.

Zapier integration

We also offer a Zapier integration. This lets you connect the API to thousands of other services with just a few clicks, solving the problems mentioned above without having to write any code. To start using this integration, see the Zapier section in the web app.

Example use cases include:

Contact us

We also recommend for you to reach out and let us know your desired use cases! This helps us prioritize which API features to build first.

API Usage

The API root is https://web.timingapp.com/api/v1. All endpoint URLs share this prefix. Sample code for each available API call can be found in the right-hand column. Note that query parameters need to be URL-encoded, as shown in the Bash example for the Return a list of time entries. call.

Authentication

The Timing API requires authentication with an API key. You can generate a key in the 'API Keys' section of the web app. Once generated, add an Authorization header to each request with value Bearer {{token}}, where {{token}} is your key.

Rate limiting

The API enforces a rate limit of 500 requests per hour. You can retrieve your current quota via the x-ratelimit-remaining header attached to every request. In addition, sending more than 200 requests per minute will also trigger a temporary rate limit.

Request and response data

The API expects requests and returns responses in the JSON format. The actual response payload can be found in the data field. Additional data might be provided in the links and meta fields described below. Refer to the descriptions of individual API calls for concrete examples.

Repeated fields

Query parameters ending with a [] can be passed repeatedly. For example, passing ?columns[]=title&columns[]=notes will show both the "Title" and the "Notes" columns. Optionally, ascending indices can be provided (e.g. ?columns[0]=title&columns[1]=notes), which makes it easier to build queries in e.g. PHP.

Date format

As Timing is a time-tracking application, its API has to work with many dates. Dates returned by the API will always be formatted as an ISO8601 string including microseconds as well as the time zone, for example 2019-01-01T00:00:00.000000+00:00.

When sending dates in your requests, we recommend providing dates in a format appropriate to the type of request:

Timezone

When a timezone is not provided, the default timezone for an unqualified date is assumed to be UTC. For example, 2019-01-01T00:00:00 would be interpreted as 2019-01-01T00:00:00+0000. This may be subject to change, however, so try to provide a timezone with your request whenever possible.

A default timezone may be provided, using the X-Time-Zone header. When this header is set to a valid timezone, any unqualified date is assumed to be in this timezone instead of UTC.

The header only affects input parameters, and all dates are currently returned in UTC. This however may change in the future, so your code should not make any assumptions about this, and always account for the timezone specified in the results.

References

The Timing API identifies entities via the self field, which contains a link relative to the API root, for example /projects/1. This avoids any ambiguity about the type of the linked resource and provides you with a convenient way of looking it up, without having to look up the correct API call in this documentation: Simply append the link's value to the API root, resulting in e.g. https://web.timingapp.com/api/v1/projects/1.

References should be treated as opaque strings; your code should not assumptions about their structure.

Shallow references

For API responses that contain related entities, these entities are usually referenced in a "shallow" fashion. Instead of including the full object, a placeholder containing only the self field is provided, e.g. as "parent": {"self": "/projects/1"}. For the Return a list of time entries. call, you can append the ?include_project_data=true query parameter to include the corresponding project's attributes in the response. This lets you retrieve project titles without having to do a second API call to the "Projects" collection.

Some responses include links to related queries or entities. This includes pagination and queries for related entries.

Pagination

By default, collection responses are paginated to 100 items per page. The links to retrieve the first, last, next and previous pages are part of the response's links field. Additional information about the paginated data can be found in the meta field.

Custom fields

Some entities (namely projects and time entries) can have custom fields. These are returned as the custom_fields collection, with an entry for each custom field on the entity. Custom fields are intended only for scripting purposes. They are exposed only via the API and are not visible anywhere in the Timing app on your Mac nor the web app.

Naming

Custom field names must be a non-empty string, and may only contain alphanumeric characters, dashes and underscores. Additionally, they must not start with an underscore or contain only digits.

Values

Custom field values may only be strings. The only exception to this is providing null, which can be provided to remove a custom field.

Usage

Custom fields may be added when creating or updating an entity. When updating an entity, any custom fields not provided will be left untouched. If you want to remove a custom field, you must explicitly set it to null.

Projects

Return the complete project hierarchy.

requires authentication


See Display the specified project. for the returned attributes.

Example request:
curl --request GET \
    --get "https://web.timingapp.com/api/v1/projects/hierarchy" \
    --header "Authorization: Bearer {{token}}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://web.timingapp.com/api/v1/projects/hierarchy"
);

const headers = {
    "Authorization": "Bearer {{token}}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://web.timingapp.com/api/v1/projects/hierarchy';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {{token}}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'https://web.timingapp.com/api/v1/projects/hierarchy'
headers = {
  'Authorization': 'Bearer {{token}}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):

Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 500
x-ratelimit-remaining: 499
access-control-allow-origin: *
 

{
    "data": [
        {
            "self": "/projects/1",
            "team_id": null,
            "title": "Project at root level",
            "title_chain": [
                "Project at root level"
            ],
            "color": "#FF0000",
            "productivity_score": 1,
            "is_archived": false,
            "notes": null,
            "children": [
                {
                    "self": "/projects/2",
                    "team_id": null,
                    "title": "Unproductive child project",
                    "title_chain": [
                        "Project at root level",
                        "Unproductive child project"
                    ],
                    "color": "#00FF00",
                    "productivity_score": -1,
                    "is_archived": false,
                    "notes": null,
                    "children": [],
                    "parent": {
                        "self": "/projects/1"
                    },
                    "custom_fields": {}
                }
            ],
            "parent": null,
            "custom_fields": {}
        }
    ]
}
 

Request      

GET api/v1/projects/hierarchy

Headers

Authorization      

Example: Bearer {{token}}

Content-Type      

Example: application/json

Accept      

Example: application/json

Query Parameters

team_id   integer  optional  

The ID of the team to list projects for. Can be omitted to list the user's private projects. See Return a list containing all the teams you are a member of. for obtaining a team ID to provide here.

Return a list containing all projects.

requires authentication


See Display the specified project. for the returned attributes.

Example request:
curl --request GET \
    --get "https://web.timingapp.com/api/v1/projects?title=root&hide_archived=true" \
    --header "Authorization: Bearer {{token}}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://web.timingapp.com/api/v1/projects"
);

const params = {
    "title": "root",
    "hide_archived": "true",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {{token}}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://web.timingapp.com/api/v1/projects';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {{token}}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'title' => 'root',
            'hide_archived' => 'true',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'https://web.timingapp.com/api/v1/projects'
params = {
  'title': 'root',
  'hide_archived': 'true',
}
headers = {
  'Authorization': 'Bearer {{token}}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers, params=params)
response.json()

Example response (200):

Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 500
x-ratelimit-remaining: 498
access-control-allow-origin: *
 

{
    "data": [
        {
            "self": "/projects/1",
            "team_id": null,
            "title": "Project at root level",
            "title_chain": [
                "Project at root level"
            ],
            "color": "#FF0000",
            "productivity_score": 1,
            "is_archived": false,
            "notes": null,
            "children": [
                {
                    "self": "/projects/2"
                }
            ],
            "parent": null,
            "custom_fields": {}
        }
    ]
}
 

Request      

GET api/v1/projects

Headers

Authorization      

Example: Bearer {{token}}

Content-Type      

Example: application/json

Accept      

Example: application/json

Query Parameters

title   string  optional  

Filter for projects whose title contains all words in this parameter. The search is case-insensitive but diacritic-sensitive. Example: root

hide_archived   string  optional  

If set to true, archived projects and their children will not be included in the result. Example: true

team_id   integer  optional  

The ID of the team to list projects for. Can be omitted to list the user's private projects. See Return a list containing all the teams you are a member of. for obtaining a team ID to provide here.

Create a new project.

requires authentication


See Display the specified project. for the returned attributes.

Example request:
curl --request POST \
    "https://web.timingapp.com/api/v1/projects" \
    --header "Authorization: Bearer {{token}}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"title\": \"Acme Inc.\",
    \"parent\": \"\\/projects\\/1\",
    \"color\": \"#FF0000\",
    \"productivity_score\": 1,
    \"is_archived\": false,
    \"notes\": \"Some more detailed notes\",
    \"custom_fields\": {
        \"field_name\": \"field_value\"
    }
}"
const url = new URL(
    "https://web.timingapp.com/api/v1/projects"
);

const headers = {
    "Authorization": "Bearer {{token}}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "title": "Acme Inc.",
    "parent": "\/projects\/1",
    "color": "#FF0000",
    "productivity_score": 1,
    "is_archived": false,
    "notes": "Some more detailed notes",
    "custom_fields": {
        "field_name": "field_value"
    }
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://web.timingapp.com/api/v1/projects';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {{token}}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'title' => 'Acme Inc.',
            'parent' => '/projects/1',
            'color' => '#FF0000',
            'productivity_score' => 1.0,
            'is_archived' => false,
            'notes' => 'Some more detailed notes',
            'custom_fields' => [
                'field_name' => 'field_value',
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'https://web.timingapp.com/api/v1/projects'
payload = {
    "title": "Acme Inc.",
    "parent": "\/projects\/1",
    "color": "#FF0000",
    "productivity_score": 1,
    "is_archived": false,
    "notes": "Some more detailed notes",
    "custom_fields": {
        "field_name": "field_value"
    }
}
headers = {
  'Authorization': 'Bearer {{token}}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (201):

Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 500
x-ratelimit-remaining: 497
access-control-allow-origin: *
 

{
    "data": {
        "self": "/projects/2",
        "team_id": null,
        "title": "Acme Inc.",
        "title_chain": [
            "Project at root level",
            "Acme Inc."
        ],
        "color": "#FF0000",
        "productivity_score": 1,
        "is_archived": false,
        "notes": "Some more detailed notes",
        "children": [],
        "parent": {
            "self": "/projects/1"
        },
        "custom_fields": {
            "field_name": "field_value"
        }
    },
    "links": {
        "time-entries": "https://web.timingapp.com/api/v1/time-entries?project[]=/projects/2"
    }
}
 

Request      

POST api/v1/projects

Headers

Authorization      

Example: Bearer {{token}}

Content-Type      

Example: application/json

Accept      

Example: application/json

Body Parameters

title   string   

The project's title. Example: Acme Inc.

parent   project  optional  

A reference to an existing project. The new project will be appended to the parent's children. Can be a project reference in the form "/projects/1", a project title (e.g. "Project at root level"), or an array with the project's entire title chain (e.g. ["Project at root level", "Unproductive child project"]). Example: /projects/1

color   color  optional  

The project's color, in hexadecimal format (#RRGGBB). If omitted, a color with random hue, 70% saturation and 100% value will be used. Example: #FF0000

productivity_score   number  optional  

The project's productivity rating, between -1 (very unproductive) and 1 (very productive). Defaults to 1. Example: 1

is_archived   boolean  optional  

Whether the project has been archived. Defaults to false. Example: false

team_id   integer  optional  

The ID of the team to add the project to. See Return a list containing all the teams you are a member of. for obtaining a team ID to provide here.

notes   string  optional  

The project's notes. Example: Some more detailed notes

custom_fields   object  optional  

A list of custom field name/value pairs to store. For more details, see Custom fields.

Display the specified project.

requires authentication


The following attributes will be returned:

Example request:
curl --request GET \
    --get "https://web.timingapp.com/api/v1/projects/1" \
    --header "Authorization: Bearer {{token}}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://web.timingapp.com/api/v1/projects/1"
);

const headers = {
    "Authorization": "Bearer {{token}}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://web.timingapp.com/api/v1/projects/1';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {{token}}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'https://web.timingapp.com/api/v1/projects/1'
headers = {
  'Authorization': 'Bearer {{token}}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):

Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 500
x-ratelimit-remaining: 496
access-control-allow-origin: *
 

{
    "data": {
        "self": "/projects/1",
        "team_id": null,
        "title": "Project at root level",
        "title_chain": [
            "Project at root level"
        ],
        "color": "#FF0000",
        "productivity_score": 1,
        "is_archived": false,
        "notes": null,
        "children": [
            {
                "self": "/projects/2"
            }
        ],
        "parent": null,
        "custom_fields": {}
    },
    "links": {
        "time-entries": "https://web.timingapp.com/api/v1/time-entries?project[]=/projects/1"
    }
}
 

Request      

GET api/v1/projects/{project_id}

Headers

Authorization      

Example: Bearer {{token}}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

project_id   integer   

The ID of the project to display. Example: 1

Update the specified project.

requires authentication


See Display the specified project. for the returned attributes.

Example request:
curl --request PUT \
    "https://web.timingapp.com/api/v1/projects/1" \
    --header "Authorization: Bearer {{token}}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"title\": \"Acme Inc.\",
    \"color\": \"#FF0000\",
    \"productivity_score\": 1,
    \"is_archived\": false,
    \"notes\": \"Some more detailed notes\",
    \"custom_fields\": {
        \"field_name\": \"field_value\"
    }
}"
const url = new URL(
    "https://web.timingapp.com/api/v1/projects/1"
);

const headers = {
    "Authorization": "Bearer {{token}}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "title": "Acme Inc.",
    "color": "#FF0000",
    "productivity_score": 1,
    "is_archived": false,
    "notes": "Some more detailed notes",
    "custom_fields": {
        "field_name": "field_value"
    }
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://web.timingapp.com/api/v1/projects/1';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {{token}}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'title' => 'Acme Inc.',
            'color' => '#FF0000',
            'productivity_score' => 1.0,
            'is_archived' => false,
            'notes' => 'Some more detailed notes',
            'custom_fields' => [
                'field_name' => 'field_value',
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'https://web.timingapp.com/api/v1/projects/1'
payload = {
    "title": "Acme Inc.",
    "color": "#FF0000",
    "productivity_score": 1,
    "is_archived": false,
    "notes": "Some more detailed notes",
    "custom_fields": {
        "field_name": "field_value"
    }
}
headers = {
  'Authorization': 'Bearer {{token}}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('PUT', url, headers=headers, json=payload)
response.json()

Example response (200):

Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 500
x-ratelimit-remaining: 495
access-control-allow-origin: *
 

{
    "data": {
        "self": "/projects/1",
        "team_id": null,
        "title": "Acme Inc.",
        "title_chain": [
            "Acme Inc."
        ],
        "color": "#FF0000",
        "productivity_score": 1,
        "is_archived": false,
        "notes": "Some more detailed notes",
        "children": [
            {
                "self": "/projects/2"
            }
        ],
        "parent": null,
        "custom_fields": {
            "field_name": "field_value"
        }
    },
    "links": {
        "time-entries": "https://web.timingapp.com/api/v1/time-entries?project[]=/projects/1"
    }
}
 

Request      

PUT api/v1/projects/{project_id}

PATCH api/v1/projects/{project_id}

Headers

Authorization      

Example: Bearer {{token}}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

project_id   integer   

The ID of the project to update. Example: 1

Body Parameters

title   string   

The project's title. Example: Acme Inc.

color   color  optional  

The project's color, in hexadecimal format (#RRGGBB). Example: #FF0000

productivity_score   number  optional  

The project's productivity rating, between -1 (very unproductive) and 1 (very productive). Example: 1

is_archived   boolean  optional  

Whether the project has been archived. Example: false

notes   string  optional  

The project's notes. Example: Some more detailed notes

custom_fields   object  optional  

A list of custom field name/value pairs to update. For more details, see Custom fields.

Delete the specified project and all of its children.

requires authentication

Example request:
curl --request DELETE \
    "https://web.timingapp.com/api/v1/projects/1" \
    --header "Authorization: Bearer {{token}}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://web.timingapp.com/api/v1/projects/1"
);

const headers = {
    "Authorization": "Bearer {{token}}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://web.timingapp.com/api/v1/projects/1';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {{token}}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'https://web.timingapp.com/api/v1/projects/1'
headers = {
  'Authorization': 'Bearer {{token}}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('DELETE', url, headers=headers)
response.json()

Example response (204):

Show headers
cache-control: no-cache, private
x-ratelimit-limit: 500
x-ratelimit-remaining: 494
access-control-allow-origin: *
 
Empty response
 

Request      

DELETE api/v1/projects/{project_id}

Headers

Authorization      

Example: Bearer {{token}}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

project_id   integer   

The ID of the project to delete. Example: 1

Reports

Generate a report that can contain both time entries and app usage.

requires authentication


Returns a JSON array with several rows; each row includes the total duration (in seconds) belonging to the corresponding other (configurable) columns.

The include_app_usage and include_team_members parameters govern whether to include app usage (otherwise, only time entries are returned) as well as data for other team members.
The start_date_min, start_date_max, projects(also see include_child_projects) and search_query parameters allow filtering the returned data.
The columns, project_grouping_level, include_project_data, timespan_grouping_mode, and sort parameters govern the presentation of the returned data.

Example request:
curl --request GET \
    --get "https://web.timingapp.com/api/v1/report?include_app_usage=0&include_team_members=0&team_members%5B%5D=%2Fusers%2F1&start_date_min=2019-01-01&start_date_max=2019-01-01&projects%5B%5D=%2Fprojects%2F1&include_child_projects=1&search_query=meeting&columns%5B%5D=project&project_grouping_level=0&include_project_data=1&timespan_grouping_mode=day&sort%5B%5D=-duration" \
    --header "Authorization: Bearer {{token}}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://web.timingapp.com/api/v1/report"
);

const params = {
    "include_app_usage": "0",
    "include_team_members": "0",
    "team_members[]": "/users/1",
    "start_date_min": "2019-01-01",
    "start_date_max": "2019-01-01",
    "projects[]": "/projects/1",
    "include_child_projects": "1",
    "search_query": "meeting",
    "columns[]": "project",
    "project_grouping_level": "0",
    "include_project_data": "1",
    "timespan_grouping_mode": "day",
    "sort[]": "-duration",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {{token}}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://web.timingapp.com/api/v1/report';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {{token}}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'include_app_usage' => '0',
            'include_team_members' => '0',
            'team_members[]' => '/users/1',
            'start_date_min' => '2019-01-01',
            'start_date_max' => '2019-01-01',
            'projects[]' => '/projects/1',
            'include_child_projects' => '1',
            'search_query' => 'meeting',
            'columns[]' => 'project',
            'project_grouping_level' => '0',
            'include_project_data' => '1',
            'timespan_grouping_mode' => 'day',
            'sort[]' => '-duration',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'https://web.timingapp.com/api/v1/report'
params = {
  'include_app_usage': '0',
  'include_team_members': '0',
  'team_members[]': '/users/1',
  'start_date_min': '2019-01-01',
  'start_date_max': '2019-01-01',
  'projects[]': '/projects/1',
  'include_child_projects': '1',
  'search_query': 'meeting',
  'columns[]': 'project',
  'project_grouping_level': '0',
  'include_project_data': '1',
  'timespan_grouping_mode': 'day',
  'sort[]': '-duration',
}
headers = {
  'Authorization': 'Bearer {{token}}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers, params=params)
response.json()

Example response (200):

Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 500
x-ratelimit-remaining: 489
access-control-allow-origin: *
 

{
    "data": [
        {
            "duration": 3600,
            "project": {
                "self": "/projects/1",
                "team_id": null,
                "title": "Project at root level",
                "title_chain": [
                    "Project at root level"
                ],
                "color": "#FF0000",
                "productivity_score": 1,
                "is_archived": false,
                "notes": null,
                "parent": null,
                "custom_fields": {}
            },
            "title": "Client Meeting"
        }
    ]
}
 

Request      

GET api/v1/report

Headers

Authorization      

Example: Bearer {{token}}

Content-Type      

Example: application/json

Accept      

Example: application/json

Query Parameters

include_app_usage   integer  optional  

Whether to include app usage in the report. If false, only time entries are returned. Default: 0 Example: 0

include_team_members   integer  optional  

If true, the response will also contain time entries that belong to other team members, provided the current user has permission to view them. Default: 0 Example: 0

team_members[]   user; can be repeated to provide multiple users  optional  

Restricts the query to data associated with the given user. Can be repeated to include time entries from several users. Example: /users/1

start_date_min   date  optional  

Restricts the query to data whose start date is equal to or later than this parameter. Example: 2019-01-01

start_date_max   date  optional  

Restricts the query to data whose start date is equal to or earlier than this parameter. Example: 2019-01-01

projects[]   project; can be repeated to provide multiple projects  optional  

Restricts the query to data associated with the given project. Can be repeated to include time entries from several projects. If you would like to include time entries that are not assigned to any project, you can provide an empty string, i.e. projects[]= Example: /projects/1

include_child_projects   integer  optional  

If true, the response will also contain time entries that belong to any child projects of the ones provided in projects[]. Default: 0 Example: 1

search_query   string  optional  

Restricts the query to time entries whose title and/or notes contain all words in this parameter. The search is case-insensitive but diacritic-sensitive. Note: this parameter can not be used when app usage is included. Example: meeting

columns[]   column; can be repeated to provide multiple columns  optional  

Which columns to show. The user column is ignored if include_team_members is false. Possible values: project, title, notes, timespan, user. Default: user, project, title. start_date and end_date is shown when timespan column is sent. Example: project

project_grouping_level   integer  optional  

When this argument is provided, report lines for projects below the given level will be aggregated by their parent project on the given level. For example, when project_grouping_level is 0, all times in sub-projects will be counted towards the corresponding project on the "root" (i.e. highest) level in the project tree. Can be a non-negative integer or -1. The default is -1, which indicates no grouping (i.e. all projects will be returned, regardless of how deep they are in the hierarchy). Requires columns[] to contain project. Example: 0

include_project_data   integer  optional  

If true, the properties of each line's project will be included in the response. Requires columns[] to contain project. Example: 1

timespan_grouping_mode   string  optional  

When this argument is provided, report lines will be aggregated according to the given calendar unit. Possible values: exact, day, week, month, year. Default: exact Example: day

sort[]   column with +/- prefix; can be repeated to provide multiple sort columns  optional  

Sort the results by the given column. Prepend column name with a dash (-) to sort descending. Default: -duration. Examples: sort[]=-duration -> Sort descending by duration. sort[]=user&sort[]=-duration -> Sort ascending by user, then descending by duration. Example: -duration

Teams

Return a list containing all active members of the given team.

requires authentication


Members with pending invitations will be excluded.

The following attributes will be returned:

Example request:
curl --request GET \
    --get "https://web.timingapp.com/api/v1/teams/1/members" \
    --header "Authorization: Bearer {{token}}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://web.timingapp.com/api/v1/teams/1/members"
);

const headers = {
    "Authorization": "Bearer {{token}}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://web.timingapp.com/api/v1/teams/1/members';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {{token}}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'https://web.timingapp.com/api/v1/teams/1/members'
headers = {
  'Authorization': 'Bearer {{token}}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):

Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 500
x-ratelimit-remaining: 491
access-control-allow-origin: *
 

{
    "data": [
        {
            "self": "/users/1",
            "email": "johnny@appleseed.net",
            "name": "Johnny Appleseed"
        }
    ]
}
 

Request      

GET api/v1/teams/{team_id}/members

Headers

Authorization      

Example: Bearer {{token}}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

team_id   integer   

The ID of the team to list members for. Example: 1

Return a list containing all the teams you are a member of.

requires authentication

Example request:
curl --request GET \
    --get "https://web.timingapp.com/api/v1/teams" \
    --header "Authorization: Bearer {{token}}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://web.timingapp.com/api/v1/teams"
);

const headers = {
    "Authorization": "Bearer {{token}}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://web.timingapp.com/api/v1/teams';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {{token}}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'https://web.timingapp.com/api/v1/teams'
headers = {
  'Authorization': 'Bearer {{token}}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):

Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 500
x-ratelimit-remaining: 490
access-control-allow-origin: *
 

{
    "data": [
        {
            "id": "/teams/1",
            "name": "Demo Team"
        }
    ]
}
 

Request      

GET api/v1/teams

Headers

Authorization      

Example: Bearer {{token}}

Content-Type      

Example: application/json

Accept      

Example: application/json

Time Entries

Start a new timer.

requires authentication


This also stops the currently running timer if there is one.

See Display the specified time entry. for the returned attributes.

Example request:
curl --request POST \
    "https://web.timingapp.com/api/v1/time-entries/start" \
    --header "Authorization: Bearer {{token}}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"start_date\": \"2019-01-01T00:00:00+00:00\",
    \"project\": \"Unproductive child project\",
    \"title\": \"Client Meeting\",
    \"notes\": \"Some more detailed notes\",
    \"replace_existing\": false,
    \"custom_fields\": {
        \"field_name\": \"field_value\"
    }
}"
const url = new URL(
    "https://web.timingapp.com/api/v1/time-entries/start"
);

const headers = {
    "Authorization": "Bearer {{token}}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "start_date": "2019-01-01T00:00:00+00:00",
    "project": "Unproductive child project",
    "title": "Client Meeting",
    "notes": "Some more detailed notes",
    "replace_existing": false,
    "custom_fields": {
        "field_name": "field_value"
    }
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://web.timingapp.com/api/v1/time-entries/start';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {{token}}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'start_date' => '2019-01-01T00:00:00+00:00',
            'project' => 'Unproductive child project',
            'title' => 'Client Meeting',
            'notes' => 'Some more detailed notes',
            'replace_existing' => false,
            'custom_fields' => [
                'field_name' => 'field_value',
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'https://web.timingapp.com/api/v1/time-entries/start'
payload = {
    "start_date": "2019-01-01T00:00:00+00:00",
    "project": "Unproductive child project",
    "title": "Client Meeting",
    "notes": "Some more detailed notes",
    "replace_existing": false,
    "custom_fields": {
        "field_name": "field_value"
    }
}
headers = {
  'Authorization': 'Bearer {{token}}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (201):

Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 500
x-ratelimit-remaining: 493
access-control-allow-origin: *
 

{
    "data": {
        "self": "/time-entries/2",
        "start_date": "2019-01-01T00:00:00.000000+00:00",
        "end_date": "2019-01-01T00:00:00.000000+00:00",
        "duration": 0,
        "project": {
            "self": "/projects/2"
        },
        "title": "Client Meeting",
        "notes": "Some more detailed notes",
        "is_running": true,
        "creator_name": "Johnny Appleseed",
        "custom_fields": {
            "field_name": "field_value"
        }
    },
    "message": "Timer 'Client Meeting' started."
}
 

Request      

POST api/v1/time-entries/start

Headers

Authorization      

Example: Bearer {{token}}

Content-Type      

Example: application/json

Accept      

Example: application/json

Body Parameters

start_date   date  optional  

The date this timer should have started at. Defaults to "now". Example: 2019-01-01T00:00:00+00:00

project   project  optional  

The project this timer is associated with. Can be a project reference in the form "/projects/1", a project title (e.g. "Project at root level"), or an array with the project's entire title chain (e.g. ["Project at root level", "Unproductive child project"]). Example: Unproductive child project

title   string  optional  

The timer's title. Example: Client Meeting

notes   string  optional  

The timer's notes. Example: Some more detailed notes

replace_existing   boolean  optional  

If true, any existing time entries that overlap with the new time entry will be adjusted to avoid overlap, or deleted altogether. Defaults to false. Example: false

custom_fields   object  optional  

A list of custom field name/value pairs to store. For more details, see Custom fields.

Stop the currently running timer.

requires authentication


Returns the stopped timer's attributes as listed under Display the specified time entry..

Example request:
curl --request PUT \
    "https://web.timingapp.com/api/v1/time-entries/stop" \
    --header "Authorization: Bearer {{token}}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://web.timingapp.com/api/v1/time-entries/stop"
);

const headers = {
    "Authorization": "Bearer {{token}}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://web.timingapp.com/api/v1/time-entries/stop';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {{token}}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'https://web.timingapp.com/api/v1/time-entries/stop'
headers = {
  'Authorization': 'Bearer {{token}}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('PUT', url, headers=headers)
response.json()

Example response (200):

Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 500
x-ratelimit-remaining: 499
access-control-allow-origin: *
 

{
    "data": {
        "self": "/time-entries/1",
        "start_date": "2019-01-01T00:00:00.000000+00:00",
        "end_date": "2019-01-01T01:00:00.000000+00:00",
        "duration": 3600,
        "project": {
            "self": "/projects/1"
        },
        "title": "Client Meeting",
        "notes": "Some more detailed notes",
        "is_running": false,
        "creator_name": "Johnny Appleseed",
        "custom_fields": {}
    },
    "message": "Timer 'Client Meeting' stopped."
}
 

Request      

PUT api/v1/time-entries/stop

Headers

Authorization      

Example: Bearer {{token}}

Content-Type      

Example: application/json

Accept      

Example: application/json

Redirect to the latest time entry.

requires authentication


See Display the specified time entry. for the route the redirect points to.

Example request:
curl --request GET \
    --get "https://web.timingapp.com/api/v1/time-entries/latest" \
    --header "Authorization: Bearer {{token}}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://web.timingapp.com/api/v1/time-entries/latest"
);

const headers = {
    "Authorization": "Bearer {{token}}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://web.timingapp.com/api/v1/time-entries/latest';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {{token}}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'https://web.timingapp.com/api/v1/time-entries/latest'
headers = {
  'Authorization': 'Bearer {{token}}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (302):

Show headers
cache-control: no-cache, private
location: https://web.timingapp.com/api/v1/time-entries/1
content-type: application/json
x-ratelimit-limit: 500
x-ratelimit-remaining: 498
access-control-allow-origin: *
 

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="refresh" content="0;url='https://web.timingapp.com/api/v1/time-entries/1'" />

        <title>Redirecting to https://web.timingapp.com/api/v1/time-entries/1</title>
    </head>
    <body>
        Redirecting to <a href="https://web.timingapp.com/api/v1/time-entries/1">https://web.timingapp.com/api/v1/time-entries/1</a>.
    </body>
</html>
 

Request      

GET api/v1/time-entries/latest

Headers

Authorization      

Example: Bearer {{token}}

Content-Type      

Example: application/json

Accept      

Example: application/json

Redirect to the currently running timer.


See Display the specified time entry. for the route the redirect points to.

Example request:
curl --request GET \
    --get "https://web.timingapp.com/api/v1/time-entries/running" \
    --header "Authorization: Bearer {{token}}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://web.timingapp.com/api/v1/time-entries/running"
);

const headers = {
    "Authorization": "Bearer {{token}}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://web.timingapp.com/api/v1/time-entries/running';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {{token}}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'https://web.timingapp.com/api/v1/time-entries/running'
headers = {
  'Authorization': 'Bearer {{token}}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (404):

Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 500
x-ratelimit-remaining: 497
access-control-allow-origin: *
 

{
    "message": "No running timer found."
}
 

Request      

GET api/v1/time-entries/running

Headers

Authorization      

Example: Bearer {{token}}

Content-Type      

Example: application/json

Accept      

Example: application/json

Return a list of time entries.

requires authentication


See Display the specified time entry. for the returned attributes.

Items are ordered descending by their start_date field.

Example request:
curl --request GET \
    --get "https://web.timingapp.com/api/v1/time-entries?start_date_min=2019-01-01&start_date_max=2019-01-01&projects%5B%5D=%2Fprojects%2F1&include_child_projects=1&search_query=meeting&is_running=0&include_project_data=1&include_team_members=0&team_members%5B%5D=%2Fusers%2F1" \
    --header "Authorization: Bearer {{token}}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://web.timingapp.com/api/v1/time-entries"
);

const params = {
    "start_date_min": "2019-01-01",
    "start_date_max": "2019-01-01",
    "projects[]": "/projects/1",
    "include_child_projects": "1",
    "search_query": "meeting",
    "is_running": "0",
    "include_project_data": "1",
    "include_team_members": "0",
    "team_members[]": "/users/1",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {{token}}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://web.timingapp.com/api/v1/time-entries';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {{token}}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'start_date_min' => '2019-01-01',
            'start_date_max' => '2019-01-01',
            'projects[]' => '/projects/1',
            'include_child_projects' => '1',
            'search_query' => 'meeting',
            'is_running' => '0',
            'include_project_data' => '1',
            'include_team_members' => '0',
            'team_members[]' => '/users/1',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'https://web.timingapp.com/api/v1/time-entries'
params = {
  'start_date_min': '2019-01-01',
  'start_date_max': '2019-01-01',
  'projects[]': '/projects/1',
  'include_child_projects': '1',
  'search_query': 'meeting',
  'is_running': '0',
  'include_project_data': '1',
  'include_team_members': '0',
  'team_members[]': '/users/1',
}
headers = {
  'Authorization': 'Bearer {{token}}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers, params=params)
response.json()

Example response (200):

Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 500
x-ratelimit-remaining: 496
access-control-allow-origin: *
 

{
    "data": [
        {
            "self": "/time-entries/1",
            "start_date": "2019-01-01T00:00:00.000000+00:00",
            "end_date": "2019-01-01T01:00:00.000000+00:00",
            "duration": 3600,
            "project": {
                "self": "/projects/1",
                "team_id": null,
                "title": "Project at root level",
                "title_chain": [
                    "Project at root level"
                ],
                "color": "#FF0000",
                "productivity_score": 1,
                "is_archived": false,
                "notes": null,
                "parent": null,
                "custom_fields": {}
            },
            "title": "Client Meeting",
            "notes": "Some more detailed notes",
            "is_running": false,
            "creator_name": "Johnny Appleseed",
            "custom_fields": {}
        }
    ],
    "links": {
        "first": "http://timing-web.test/api/v1/time-entries?start_date_min=2019-01-01&start_date_max=2019-01-01&projects%5B%5D=%2Fprojects%2F1&include_child_projects=1&search_query=meeting&is_running=0&include_project_data=1&include_team_members=0&team_members%5B%5D=%2Fusers%2F1&page=1",
        "last": "http://timing-web.test/api/v1/time-entries?start_date_min=2019-01-01&start_date_max=2019-01-01&projects%5B%5D=%2Fprojects%2F1&include_child_projects=1&search_query=meeting&is_running=0&include_project_data=1&include_team_members=0&team_members%5B%5D=%2Fusers%2F1&page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "links": [
            {
                "url": null,
                "label": "&laquo; Previous",
                "active": false
            },
            {
                "url": "http://timing-web.test/api/v1/time-entries?start_date_min=2019-01-01&start_date_max=2019-01-01&projects%5B%5D=%2Fprojects%2F1&include_child_projects=1&search_query=meeting&is_running=0&include_project_data=1&include_team_members=0&team_members%5B%5D=%2Fusers%2F1&page=1",
                "label": "1",
                "active": true
            },
            {
                "url": null,
                "label": "Next &raquo;",
                "active": false
            }
        ],
        "path": "http://timing-web.test/api/v1/time-entries",
        "per_page": 1000,
        "to": 1,
        "total": 1
    }
}
 

Request      

GET api/v1/time-entries

Headers

Authorization      

Example: Bearer {{token}}

Content-Type      

Example: application/json

Accept      

Example: application/json

Query Parameters

start_date_min   date  optional  

Restricts the query to time entries whose start date is equal to or later than this parameter. Example: 2019-01-01

start_date_max   date  optional  

Restricts the query to time entries whose start date is equal to or earlier than this parameter. Example: 2019-01-01

projects[]   project; can be repeated to provide multiple projects  optional  

Restricts the query to time entries associated with the given project. Can be repeated to include time entries from several projects. If you would like to include time entries that are not assigned to any project, you can provide an empty string, i.e. projects[]= Example: /projects/1

include_child_projects   integer  optional  

If true, the response will also contain time entries that belong to any child projects of the ones provided in projects[]. Default: 0 Example: 1

search_query   string  optional  

Restricts the query to time entries whose title and/or notes contain all words in this parameter. The search is case-insensitive but diacritic-sensitive. Example: meeting

is_running   integer  optional  

If provided, returns only time entries that are either running or not running. Example: 0

include_project_data   integer  optional  

If true, the properties of the time entry's project will be included in the response. Default: 0 Example: 1

include_team_members   integer  optional  

If true, the response will also contain time entries that belong to other team members, provided the current user has permission to view them. Default: 0 Example: 0

team_members[]   user; can be repeated to provide multiple users  optional  

Restricts the query to data associated with the given user. Can be repeated to include time entries from several users. Example: /users/1

Create a new time entry.

requires authentication


See Display the specified time entry. for the returned attributes.

Example request:
curl --request POST \
    "https://web.timingapp.com/api/v1/time-entries" \
    --header "Authorization: Bearer {{token}}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"start_date\": \"2019-01-01T00:00:00+00:00\",
    \"end_date\": \"2019-01-01T01:00:00+00:00\",
    \"project\": \"Unproductive child project\",
    \"title\": \"Client Meeting\",
    \"notes\": \"Some more detailed notes\",
    \"replace_existing\": false,
    \"custom_fields\": {
        \"field_name\": \"field_value\"
    }
}"
const url = new URL(
    "https://web.timingapp.com/api/v1/time-entries"
);

const headers = {
    "Authorization": "Bearer {{token}}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "start_date": "2019-01-01T00:00:00+00:00",
    "end_date": "2019-01-01T01:00:00+00:00",
    "project": "Unproductive child project",
    "title": "Client Meeting",
    "notes": "Some more detailed notes",
    "replace_existing": false,
    "custom_fields": {
        "field_name": "field_value"
    }
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://web.timingapp.com/api/v1/time-entries';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {{token}}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'start_date' => '2019-01-01T00:00:00+00:00',
            'end_date' => '2019-01-01T01:00:00+00:00',
            'project' => 'Unproductive child project',
            'title' => 'Client Meeting',
            'notes' => 'Some more detailed notes',
            'replace_existing' => false,
            'custom_fields' => [
                'field_name' => 'field_value',
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'https://web.timingapp.com/api/v1/time-entries'
payload = {
    "start_date": "2019-01-01T00:00:00+00:00",
    "end_date": "2019-01-01T01:00:00+00:00",
    "project": "Unproductive child project",
    "title": "Client Meeting",
    "notes": "Some more detailed notes",
    "replace_existing": false,
    "custom_fields": {
        "field_name": "field_value"
    }
}
headers = {
  'Authorization': 'Bearer {{token}}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (201):

Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 500
x-ratelimit-remaining: 495
access-control-allow-origin: *
 

{
    "data": {
        "self": "/time-entries/2",
        "start_date": "2019-01-01T00:00:00.000000+00:00",
        "end_date": "2019-01-01T01:00:00.000000+00:00",
        "duration": 3600,
        "project": {
            "self": "/projects/2"
        },
        "title": "Client Meeting",
        "notes": "Some more detailed notes",
        "is_running": false,
        "creator_name": "Johnny Appleseed",
        "custom_fields": {
            "field_name": "field_value"
        }
    }
}
 

Request      

POST api/v1/time-entries

Headers

Authorization      

Example: Bearer {{token}}

Content-Type      

Example: application/json

Accept      

Example: application/json

Body Parameters

start_date   date   

The time entry's start date and time. Example: 2019-01-01T00:00:00+00:00

end_date   date   

The time entry's end date and time. Example: 2019-01-01T01:00:00+00:00

project   project  optional  

The project this time entry is associated with. Can be a project reference in the form "/projects/1", a project title (e.g. "Project at root level"), or an array with the project's entire title chain (e.g. ["Project at root level", "Unproductive child project"]). Example: Unproductive child project

title   string  optional  

The time entry's title. Example: Client Meeting

notes   string  optional  

The time entry's notes. Example: Some more detailed notes

replace_existing   boolean  optional  

If true, any existing time entries that overlap with the new time entry will be adjusted to avoid overlap, or deleted altogether. Defaults to false. Example: false

custom_fields   object  optional  

A list of custom field name/value pairs to store. For more details, see Custom fields.

Display the specified time entry.

requires authentication


The following attributes will be returned:

Example request:
curl --request GET \
    --get "https://web.timingapp.com/api/v1/time-entries/1" \
    --header "Authorization: Bearer {{token}}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://web.timingapp.com/api/v1/time-entries/1"
);

const headers = {
    "Authorization": "Bearer {{token}}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://web.timingapp.com/api/v1/time-entries/1';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {{token}}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'https://web.timingapp.com/api/v1/time-entries/1'
headers = {
  'Authorization': 'Bearer {{token}}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):

Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 500
x-ratelimit-remaining: 494
access-control-allow-origin: *
 

{
    "data": {
        "self": "/time-entries/1",
        "start_date": "2019-01-01T00:00:00.000000+00:00",
        "end_date": "2019-01-01T01:00:00.000000+00:00",
        "duration": 3600,
        "project": {
            "self": "/projects/1"
        },
        "title": "Client Meeting",
        "notes": "Some more detailed notes",
        "is_running": false,
        "creator_name": "Johnny Appleseed",
        "custom_fields": {}
    }
}
 

Request      

GET api/v1/time-entries/{activity_id}

Headers

Authorization      

Example: Bearer {{token}}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

activity_id   string   

The ID of the activity to display. Example: 1

Update the specified time entry.

requires authentication


See Display the specified time entry. for the returned attributes.

Example request:
curl --request PUT \
    "https://web.timingapp.com/api/v1/time-entries/1" \
    --header "Authorization: Bearer {{token}}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"start_date\": \"2019-01-01T00:00:00+00:00\",
    \"end_date\": \"2019-01-01T01:00:00+00:00\",
    \"project\": \"Unproductive child project\",
    \"title\": \"Client Meeting\",
    \"notes\": \"Some more detailed notes\",
    \"replace_existing\": false,
    \"custom_fields\": {
        \"field_name\": \"field_value\"
    }
}"
const url = new URL(
    "https://web.timingapp.com/api/v1/time-entries/1"
);

const headers = {
    "Authorization": "Bearer {{token}}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "start_date": "2019-01-01T00:00:00+00:00",
    "end_date": "2019-01-01T01:00:00+00:00",
    "project": "Unproductive child project",
    "title": "Client Meeting",
    "notes": "Some more detailed notes",
    "replace_existing": false,
    "custom_fields": {
        "field_name": "field_value"
    }
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://web.timingapp.com/api/v1/time-entries/1';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {{token}}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'start_date' => '2019-01-01T00:00:00+00:00',
            'end_date' => '2019-01-01T01:00:00+00:00',
            'project' => 'Unproductive child project',
            'title' => 'Client Meeting',
            'notes' => 'Some more detailed notes',
            'replace_existing' => false,
            'custom_fields' => [
                'field_name' => 'field_value',
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'https://web.timingapp.com/api/v1/time-entries/1'
payload = {
    "start_date": "2019-01-01T00:00:00+00:00",
    "end_date": "2019-01-01T01:00:00+00:00",
    "project": "Unproductive child project",
    "title": "Client Meeting",
    "notes": "Some more detailed notes",
    "replace_existing": false,
    "custom_fields": {
        "field_name": "field_value"
    }
}
headers = {
  'Authorization': 'Bearer {{token}}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('PUT', url, headers=headers, json=payload)
response.json()

Example response (200):

Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 500
x-ratelimit-remaining: 493
access-control-allow-origin: *
 

{
    "data": {
        "self": "/time-entries/1",
        "start_date": "2019-01-01T00:00:00.000000+00:00",
        "end_date": "2019-01-01T01:00:00.000000+00:00",
        "duration": 3600,
        "project": {
            "self": "/projects/2"
        },
        "title": "Client Meeting",
        "notes": "Some more detailed notes",
        "is_running": false,
        "creator_name": "Johnny Appleseed",
        "custom_fields": {
            "field_name": "field_value"
        }
    }
}
 

Request      

PUT api/v1/time-entries/{activity_id}

PATCH api/v1/time-entries/{activity_id}

Headers

Authorization      

Example: Bearer {{token}}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

activity_id   integer   

The ID of the activity to update. Example: 1

Body Parameters

start_date   date  optional  

The time entry's start date and time. Example: 2019-01-01T00:00:00+00:00

end_date   date  optional  

The time entry's start date and time. Example: 2019-01-01T01:00:00+00:00

project   project  optional  

The project this time entry is associated with. Can be a project reference in the form "/projects/1", a project title (e.g. "Project at root level"), or an array with the project's entire title chain (e.g. ["Project at root level", "Unproductive child project"]). Example: Unproductive child project

title   string  optional  

The time entry's title. Example: Client Meeting

notes   string  optional  

The time entry's notes. Example: Some more detailed notes

replace_existing   boolean  optional  

If true and the entry's start or end date has changed, any existing time entries that overlap with the updated time entry will be adjusted to avoid overlap, or deleted altogether. Defaults to false. Example: false

custom_fields   object  optional  

A list of custom field name/value pairs to update. For more details, see Custom fields.

Delete the specified time entry.

requires authentication

Example request:
curl --request DELETE \
    "https://web.timingapp.com/api/v1/time-entries/1" \
    --header "Authorization: Bearer {{token}}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://web.timingapp.com/api/v1/time-entries/1"
);

const headers = {
    "Authorization": "Bearer {{token}}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://web.timingapp.com/api/v1/time-entries/1';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {{token}}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'https://web.timingapp.com/api/v1/time-entries/1'
headers = {
  'Authorization': 'Bearer {{token}}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('DELETE', url, headers=headers)
response.json()

Example response (204):

Show headers
cache-control: no-cache, private
x-ratelimit-limit: 500
x-ratelimit-remaining: 492
access-control-allow-origin: *
 
Empty response
 

Request      

DELETE api/v1/time-entries/{activity_id}

Headers

Authorization      

Example: Bearer {{token}}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

activity_id   integer   

The ID of the activity to delete. Example: 1