zeiten.io API
Programs can read the entries of a tracker as JSON, for example to attach a timesheet to an invoice. The API has one request and needs no account: the address of a tracker gives access to it, as its page does.
Request
GET https://new.zeiten.io/api/trackers/ID/timesheet
Replace ID
with the last part of the tracker's address, for example AbCdE
for https://new.zeiten.io/AbCdE. The path may end with a slash, and zeiten answers
HEAD
with the same status and without a body. All parameters are optional and go into the query:
| Parameter | Meaning |
|---|---|
start |
The first day of the period, as a date or an expression. Without it, the period has no start. |
end |
The last day of the period, in the same forms. Without it, the period has no end. |
tag |
Selects the entries that carry this tag. zeiten compares whole tags and ignores case and the spaces around the value:
work
selects an entry tagged Work
and not one tagged homework.
|
comment |
Selects the entries whose comment contains this text, ignoring case. |
tz |
The time zone that gives today's date for expressions, such as Europe/Zurich. Without it, or when it is empty, today is the date in UTC.
|
The parameters narrow the selection together, and zeiten ignores other parameters. This request asks for the entries of September 2026 with the tag SynergyPlus:
https://new.zeiten.io/api/trackers/ID/timesheet?start=2026-09-01&end=2026-09-30&tag=SynergyPlus
Dates and expressions
start
and end
take the same forms as a custom filter. An expression counts from today in the time zone tz. zeiten ignores the case of the letters and the spaces around the value. The examples assume that today is Wednesday, 2026-09-16.
| Form | Meaning | Example |
|---|---|---|
2026-09-01 |
The date itself, from year 1 to 9999. | |
-6d, +2w, -1m, +1y |
Today, moved by days, weeks, months or years. A number without sign moves forward. A month keeps the day, or takes the last day of a shorter month. | -6d is 2026-09-10, +2w is 2026-09-30. |
ws, we, ms, me, ys,
ye
|
The first (s) or last (e) day of the week, month or year of today. A week runs from Monday to Sunday. A sign and one digit move by whole weeks, months or years.
|
ws
is 2026-09-14, -1ms
is 2026-08-01, -1me
is 2026-08-31.
|
The filters of the menu use these pairs: Today -0d
to +0d, Last 7 days -6d
to +0d, Last month -1ms
to -1me, This week +0ws
to +0we, This month +0ms
to +0me
and This year +0ys
to +0ye.
Answer
zeiten answers with status 200 and a JSON document in UTF-8:
{
"tracker": {"id": "AbCdE", "title": "Roche 2026"},
"start": "2026-09-01",
"end": "2026-09-30",
"total_seconds": 55800,
"entries": [
{
"id": 27579,
"day": "2026-09-29",
"duration": "8:00 - 12:00, 13:00 - 16:30",
"duration_seconds": 27000,
"tags": ["SynergyPlus"],
"comment": "- Solution train sync"
},
{
"id": 27583,
"day": "2026-09-30",
"duration": "8h",
"duration_seconds": 28800,
"tags": ["SynergyPlus", "PDT"],
"comment": "- Order checks\n- PDT working slot"
}
]
}
| Field | Meaning |
|---|---|
tracker.id |
The ID from the address. |
tracker.title |
The title of the tracker, empty for an untitled tracker. |
start, end |
The first and the last day of the period as YYYY-MM-DD, which shows the dates of an expression, or
null
without the parameter.
|
total_seconds |
The sum of duration_seconds of the listed entries. |
entries |
The selected entries, from the oldest day to the newest, and within a day in the order they were saved. |
id |
A number that identifies the entry. |
day |
The date of the entry as YYYY-MM-DD. |
duration |
The normalized duration, such as 8h
or 8:00 - 12:00, 13:00 - 17:00.
|
duration_seconds |
The length of the entry in seconds. Programs compute with this field. |
tags |
The tags of the entry in the order the user typed them, each once. |
comment |
The comment as plain text, which may contain line breaks. |
Errors
| Status | Reason | Body |
|---|---|---|
| 400 |
A parameter is invalid: a date or an expression that zeiten cannot read, an unknown time zone, or a list such as
tag[]=a
in place of a text.
|
The messages per parameter, see below. |
| 400 | The query is not valid UTF-8. | No JSON. |
| 404 | No tracker has this ID. | {"errors": {"detail": "Not Found"}} |
| 406 |
The request does not accept JSON: its Accept
header names other types only.
|
No JSON. |
| 429 |
This client sent more than 20 requests to the API in the current minute. The header
Retry-After
gives the seconds until the minute ends.
|
No JSON. |
| 503 |
All clients together sent more than 100 requests to the API in the current minute. The header
Retry-After
gives the seconds until the minute ends.
|
No JSON. |
An answer with status 400 names each invalid parameter with a list of messages:
{
"errors": {
"start": ["use a date like 2026-09-27 or an expression like -6d or +0ws"],
"tz": ["use a time zone like Europe/Zurich"],
"tag": ["is invalid"]
}
}
start
and end
get the first message, tz
the second, and a list in place of a text gets is invalid.
Access
- The address of a tracker gives access to it. Anyone who knows it can read the entries through the API and read and change them on the page, so keep it private.
- zeiten sets no cookies for the API and does not count the request as a visit.
- zeiten sends no CORS headers, so scripts of other websites cannot read the answer in a browser. Call the API from a program or a server.
- zeiten.io answers over HTTPS. This request reads last month in the time zone of Zurich:
curl 'https://new.zeiten.io/api/trackers/ID/timesheet?start=-1ms&end=-1me&tz=Europe/Zurich'