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
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": [
{}
]
}
{
"Code": 200,
"Message": "Invalid model state error.",
"Details": [
"…"
],
"Parameters": "…",
"Url": "https://app{server-number}.timelog.com/{account}/api/v2/widget-data/billable-percentage"
}
{
"Message": "Authorization has been denied for this request."
}
{
"Message": "No HTTP resource was found that matches the request URI 'https://app{server-number}.timelog.com/{account}/api/v2/widget-data/billable-percentage'.",
"MessageDetail": "No action was found on the controller '…' that matches the request."
}
{
"Code": 100,
"Message": "Unknown error occur in system, please contact system administrator.",
"Details": [],
"Parameters": "…",
"Url": "https://app{server-number}.timelog.com/{account}/api/v2/widget-data/billable-percentage"
}
/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.
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.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.
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.