-
Notifications
You must be signed in to change notification settings - Fork 0
API Internals
How the Strava.cz web app talks to its backend, reverse engineered from browser traffic
and verified against a live account. The Czech-language original of these notes lives in
notes/README.md,
and complete recorded responses are in
tests/fixtures/.
This is unofficial. Nothing here is documented or guaranteed by Strava.cz.
- Base URL
https://app.strava.cz, endpoints under/api/<name>. - Everything is a POST with a JSON body.
- The web app sends
Content-Type: text/plain;charset=UTF-8even though the body is JSON. The server accepts it, and this library sends the same thing. - The
sidfrom login must be included in every authenticated request afterwards. - Money and prices come back as strings (
"512.50"), not numbers. -
ignoreCertis sent as the string"false". - Field naming is inconsistent between endpoints:
objednavkywantss5url, whilepridejJidloS5,saveOrdersandlogOutwant the same value asurl.
Failure is signalled with a non-standard HTTP 555 and this envelope:
{
"state": "error",
"number": 20,
"message": "The client was not found in the client register or the wrong nickname or password was entered...",
"backend": { "...": "..." }
}The status alone is not enough — check state. Known numbers:
number |
meaning | library exception |
|---|---|---|
| 20 | Unknown user, wrong password, or blocked account | AuthenticationError |
| 35 | Not enough money on the account | InsufficientBalanceError |
| 43 | Server overloaded, try again in a few minutes | StravaAPIError |
Code 43 turned up while logging in repeatedly from a script — "The service is currently
overloaded, please try again in a few minutes" — so it looks more like rate limiting
than genuine load. It is not mapped to its own exception yet; it arrives as a plain
StravaAPIError with .code == 43.
{
"cislo": "3753",
"jmeno": "vojtech.nerad",
"heslo": "...",
"zustatPrihlasen": false,
"environment": "W",
"lang": "EN"
}| key | meaning | type |
|---|---|---|
cislo |
canteen number | string |
jmeno |
username | string |
heslo |
plaintext password | string |
zustatPrihlasen |
stay logged in | boolean |
environment |
the web app sends W
|
string |
lang |
interface language | string |
Response — the interesting parts:
| key | meaning |
|---|---|
sid |
session identifier, required by every later request |
s5url |
the Stravné 5 web service endpoint for this canteen |
uzivatel.konto |
account balance, as a string |
uzivatel.jmeno |
the person's full name |
uzivatel.email |
e-mail address |
uzivatel.mena |
currency |
uzivatel.nazevJidelny |
canteen name |
Fetches the menu.
{
"cislo": "3753",
"sid": "01C6...",
"s5url": "https://wss5.strava.cz/WSStravne5_15/WSStravne5.svc",
"lang": "EN",
"konto": 0,
"podminka": "",
"ignoreCert": "false"
}The response is an object whose keys are table0, table1, … — one table per day,
each a list of that day's meals.
{
"table0": [ { "...one meal..." }, { "...another..." } ],
"table1": [ ... ]
}Fields that matter on a meal:
| key | meaning | example |
|---|---|---|
datum |
date, dd.mm.yyyy
|
"15.09.2026" |
druh |
short type code, stable across languages |
"PO", "O1"
|
druh_popis |
display label for the type |
"Polévka", "Oběd 1"
|
nazev |
the meal's name | "Vývar s kuskusem" |
cena |
price, as a string | "50.00" |
alergeny |
[code, name] pairs |
[["04","Ryby"]] |
pocet |
0 not ordered, 1 ordered | 0 |
veta |
the id used for ordering | "7" |
casKonec |
when ordering closes | "2026-09-14T15:00:00" |
omezeniObj |
what you may do with it — see below |
Older builds of the app used a different date separator ("15-09.2025"), so parse
tolerantly.
This object decides what you may do, and it is easy to read wrongly. It has four fields:
| field | scope | meaning |
|---|---|---|
obj |
this meal | may it be ordered |
zm |
this meal | may a saved order be changed or cancelled |
bur |
this meal | is it available on the canteen's exchange (burza) |
den |
the whole day | a day-level code |
An empty string in obj or zm means "allowed". Any other value is a single letter
naming the reason:
| value | meaning |
|---|---|
"" |
no restriction |
"C" |
the ordering deadline has passed (casKonec is in the past) |
"I" |
cannot be ordered on its own — typically soup, included with the main dish |
"V" |
no cooking that day |
Verified against a live account (32 days, 118 meals), and it lines up exactly with
casKonec:
| situation | obj |
zm |
bur |
den |
|---|---|---|---|---|
| deadline still open | "" |
"" |
"C" |
"" |
| deadline passed | "C" |
"C" |
"!" |
"" or "CO"
|
| not ordered automatically | "" |
"" |
"C" |
"T" |
| holiday / no cooking | "V" |
"" |
"V" |
"VP" |
| soup (always) | "I" |
"I" |
"I" |
— |
den describes the day, not the meal, and cannot be used to decide whether
something can be ordered:
| value | meaning | DayStatus |
|---|---|---|
"" |
ordinary day | NORMAL |
"T" |
the canteen does not order this day automatically | NOT_AUTO_ORDERED |
"VP" |
holiday or closure | NO_SCHOOL |
"CO" |
the day's deadline has passed | CLOSED |
-
"VP"— holiday or closure. That day's rows are placeholders named"Nevaří se"or"Státní svátek", not real meals. -
"T"— the canteen's standing order does not cover this day. Its meals carryobj: ""and can be ordered by hand like any other; they simply never appear on their own. In practice this is the weekday the school does not teach. -
"CO"— a day past its deadline, whose meals carryobj: "C". Builds differ: 5.13 and some 5.15 canteens send"CO", others leavedenempty and say it only inobj, soobjis the only reliable source.
Evidence for "T", from a live account over 32 days and 118 meals:
-
"T"appeared on 6 days and every one of them was a Friday; never on any other weekday. - Every one of those days had mains with
obj: ""— orderable. - Across the window where the standing order had run, each Monday-to-Thursday day had
exactly one meal ordered and every
"T"Friday had none. - A
"T"row differs from an ordinary row inomezeniObj.denand in nothing else — same price, same deadline, same fields.
Versions of this library up to 0.2.0 classified meals from
denalone. That hid orderable days carrying"T", and on a 5.13-style menu it returned an empty menu. Since 0.3.0 orderability comes fromobjandzm, whiledenis parsed intoDay.status(and kept raw inDay.day_code).
Canteens publish the menu in stages. Until a meal is announced its row carries a generic
label, recognisable because nazev equals druh_popis (both "Oběd 2"), or because the
name is itself a generic label such as "Oběd 3" or "Oběd 2 Rad 1". The library skips
those rows.
Testing for delsiPopis == "" && alergeny == [] — which this library used to do — is
unreliable: real meals exist with neither a long description nor declared allergens.
Ordering takes two steps, with the change only provisional in between:
-
pridejJidloS5— tick or untick one meal -
saveOrders— commit everything ticked
nactiVlastnostiPA reloads state from the server and discards anything provisional,
which is what this library uses to roll back a failed transaction.
{
"cislo": "3753",
"sid": "01C6...",
"url": "https://wss5.strava.cz/WSStravne5_15/WSStravne5.svc",
"veta": "7",
"pocet": "1",
"lang": "EN",
"ignoreCert": "false"
}pocet is "1" to order and "0" to cancel. The response reports the state of every
meal that day plus the new balance in konto.
{
"cislo": "3753",
"sid": "01C6...",
"url": "https://wss5.strava.cz/WSStravne5_15/WSStravne5.svc",
"xml": null,
"lang": "EN",
"ignoreCert": "false"
}Returns the current orders and the balance. It is not proof the order stuck — this
library re-fetches the menu afterwards and checks pocet.
{
"sid": "01C6...",
"url": "https://wss5.strava.cz/WSStravne5_15/WSStravne5.svc",
"cislo": "3753",
"ignoreCert": "false",
"lang": "EN",
"getText": true,
"checkVersion": true,
"resetTables": true,
"frontendFunction": "refreshInformations"
}{
"sid": "01C6...",
"cislo": "3753",
"url": "https://wss5.strava.cz/WSStravne5_15/WSStravne5.svc",
"lang": "EN",
"ignoreCert": "false"
}The response body is the bare JSON literal true — not an object. Code that does
response.json()["something"] breaks here.
- The
burzafields and the whole meal-exchange feature. -
environment,podminka,ignoreCertandxml— accepted, but effect unknown. - Ordering more than one portion (
pocetgreater than 1).
Contributions welcome — see Contributing. If you can record a menu from a canteen that behaves differently from the ones above, that is genuinely useful.