Get my billable percentage
Returns the caller's billable hours as a percentage of norm minus absence (month to date and last month) and their billable amount per registered hour over the last 29 days.
For the CALLER only: CurrentMonthToDateBillablePercentage and LastMonthBillablePercentage = round(billable hours ÷ (norm hours − absence hours) × 100) as integers, and AverageHourlyRate = billable amount ÷ ALL registered hours over currentDate−29 … currentDate−1, in the system base currency (CurrencyAbb), 2 decimals. Only registrations that already have financial data are counted (written asynchronously after a registration is saved — a just-saved registration may not be reflected yet). The norm and absence definitions differ from the external-percentage route (all absence codes here, holiday only there; no employment-period clipping here). Full definitions on WidgetDataBillablePercentageRead.
Use it to:
- Show a consultant their billable utilisation this month vs last month
Not for:
- Utilisation of another employee or a team — always the caller
- Billable rate per billable hour — AverageHourlyRate divides by all registered hours
Behaviour:
- Idempotent.
- Safe to retry.
- Consistency: eventual
See also: GET /v2/widget-data/external-percentage, GET /v1/time-registration-financial-data/get-by-date-range
Not yet documented: examples; purpose → description.
curl -X GET "https://app3.timelog.com/your-account/api/v2/widget-data/billable-percentage?currentDate=2024-12-25T10%3A00%3A00Z" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN"
import requests
import json
url = "https://app3.timelog.com/your-account/api/v2/widget-data/billable-percentage?currentDate=2024-12-25T10%3A00%3A00Z"
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/widget-data/billable-percentage?currentDate=2024-12-25T10%3A00%3A00Z", {
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/widget-data/billable-percentage?currentDate=2024-12-25T10%3A00%3A00Z", 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/widget-data/billable-percentage?currentDate=2024-12-25T10%3A00%3A00Z')
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
{
"Properties": {
"UserId": 123,
"CurrentMonthToDateBillablePercentage": 25,
"LastMonthBillablePercentage": 25,
"AverageHourlyRate": 3.14,
"CurrencyName": "null",
"CurrencyAbb": "null"
},
"Links": [
{
"Href": "example_string",
"Rel": "example_string"
}
],
"Actions": [
{}
]
}
{
"error": "Bad Request",
"message": "The request contains invalid parameters or malformed data",
"code": 400,
"details": [
{
"field": "email",
"message": "Invalid email format"
}
]
}
{
"error": "Unauthorized",
"message": "Authentication required. Please provide a valid API token",
"code": 401
}
{
"error": "Not Found",
"message": "The requested resource was not found",
"code": 404
}
{
"error": "Internal Server Error",
"message": "An unexpected error occurred on the server",
"code": 500,
"requestId": "req_1234567890"
}
/v2/widget-data/billable-percentage
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.The reference day ('today'). Windows: month start … currentDate; previous calendar month; currentDate−29 … currentDate−1 for the rate. Time part dropped. Omitted → 404 action not matched (captured). Date only — no time component.
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
The reference day ('today'). Windows: month start … currentDate; previous calendar month; currentDate−29 … currentDate−1 for the rate. Time part dropped. Omitted → 404 action not matched (captured).
Date only — no time component.
Responses
"Billable percentage" of ONE user (the caller): billable hours as a percentage of the user's norm hours minus absence hours, month to date and for the previous month, plus the caller's average billable amount per registered hour over the last 29 days, in the system base currency. Formula, per window: round(BillableHours / (NormHours − AbsenceHours) × 100) as an integer; 0 when the denominator is 0. - BillableHours = SUM of the caller's registrations dated in the window. Only registrations that already have a financial-data row count (inner join; that row is written asynchronously — see TimeRegistrationFinancialDataRead). - NormHours = SUM for the caller in the window (not clipped to the employment period, unlike the external widget). - AbsenceHours = SUM(ActualHours) of the caller's registrations with EntityType 149 (= any absence code, not only holiday) in the window that have a financial-data row. - The denominator can be negative or tiny; the percentage is not capped.
Not yet documented: description.