Small QR-first Python client for fetching your own WebUntis timetable.
This project uses the Untis Mobile QR secret as the only supported authentication flow. It does not use the browser password login path.
- Imports a full
untis://setschool?...QR string or equivalent manual values. - Derives a 6-digit TOTP from the QR secret.
- Authenticates against the Untis Mobile JSON-RPC endpoint.
- Mints a bearer token from
/WebUntis/api/token/new. - Fetches your timetable from the WebUntis REST endpoint.
- Keeps the last successful timetable payload in
.untis-cache/.
- Python 3.11+
- A WebUntis account with a valid Untis Mobile QR secret
No third-party runtime dependencies are required.
pip install .Or run it directly from the repo:
python -m untis_simple_api.cli --helpThe normalized .env contract is:
UNTIS_BASE_URL=https://scholl-gymnasium.webuntis.com
UNTIS_SCHOOL=scholl-gymnasium
UNTIS_USERNAME=ihor.oleksiuk
UNTIS_SECRET=BASE32SECRETYou can populate that file from a full QR string:
untis-simple-api import-qr --qr "untis://setschool?url=...&school=...&user=...&key=..." --write-envOr from manual values:
untis-simple-api import-qr \
--base-url "https://scholl-gymnasium.webuntis.com" \
--school "scholl-gymnasium" \
--user "ihor.oleksiuk" \
--secret "BASE32SECRET" \
--write-envimport-qr --write-env removes legacy USERNAME and PASSWORD entries from the target .env and writes the normalized UNTIS_* keys instead.
untis-simple-api import-qr --qr "untis://setschool?..."untis-simple-api day
untis-simple-api day --date 2026-05-20
untis-simple-api day --json
untis-simple-api day --use-cache-on-failureUses the week containing the supplied date, or today if no date is provided.
untis-simple-api week
untis-simple-api week --date 2026-05-20untis-simple-api range --start 2026-05-19 --end 2026-05-23
untis-simple-api range --start 2026-05-19 --end 2026-05-23 --jsonuntis-simple-api inspect-auth
untis-simple-api inspect-auth --jsonDefault terminal output is concise:
DAY 2026-05-20 | 5 lessons | live
2026-05-20 08:00-08:45 | Mathematics | Smith | A201
...
--json returns a machine-readable payload with:
rawresolvedmeta
The meta object includes:
freshnesscache_usedwarningfetched_atjwt_iatjwt_expresource_idresource_typetenant_idcurrent_schoolyear_idrange_startrange_end
Successful timetable responses are written to .untis-cache/.
Cached data is only returned when you explicitly opt in with --use-cache-on-failure or use_cache_on_failure=True.
When cached data is returned after an auth failure:
meta.freshnessis set to"stale_cache"meta.cache_usedistruemeta.warningexplains why cached data was used- the original
meta.fetched_atis preserved
Live and cached data are not silently mixed.
from untis_simple_api import UntisClient
client = UntisClient.from_env()
today = client.fetch_day()
week = client.fetch_week("2026-05-20")
custom = client.fetch_range(start="2026-05-19", end="2026-05-23")
auth = client.inspect_auth()You can also construct the client directly from a QR string:
client = UntisClient.from_qr_string(
"untis://setschool?url=...&school=...&user=...&key=..."
)The current flow is:
- Parse a QR string or normalized
.envvalues. - Generate a 6-digit TOTP from
UNTIS_SECRET. - Call
POST /WebUntis/jsonrpc_intern.do?m=getUserData2017&school=...&v=i2.2. - Keep the returned school/session cookies.
- Call
GET /WebUntis/api/token/new. - Call the timetable REST endpoint with cookies and bearer token.
Run tests with:
python -m unittest discover -s tests -v- This client is intentionally limited to fetching the authenticated user's own timetable.
- Password-based WebUntis login is not supported.
- The QR secret should be treated like a long-lived credential.