A successful socket read does not mean the API request succeeded. A server can return 503, a proxy can return an HTML page, JSON can be malformed, and valid JSON can have the wrong fields.
Validate each boundary in order. This endpoint promises status 200 and a JSON page containing items and next. A generic successful 204 response is not a page for this application, even though it can be correct for another endpoint.
HTTP header names are case-insensitive. Split Content-Type at its first semicolon, strip surrounding whitespace and lowercase the media type. Require one value rather than guessing between duplicates.
Decode UTF-8 explicitly. The supplied JSON hooks reject duplicate object members and nonstandard NaN/Infinity constants. Ambiguous JSON should not depend on which duplicate a parser happened to keep.
headers = [("cOnTeNt-TyPe","Application/JSON; charset=utf-8")]
media = [v.split(";",1)[0].strip().lower() for k,v in headers if k.lower()=="content-type"]
print(media)
print("unambiguous", media==["application/json"])The schema is deliberately specific to this lab’s order API: exact items/next fields; item id, category and amount_cents; bounded arrays and strings; nonnegative integer cents. Categories are lower-case identifiers. This is an application contract, not a claim that every API uses these fields.
Use type(value) is int for cents, because isinstance(True,int) is true. Keep money in integer cents so grouping does not accumulate floating-point currency error. Copy validated item dictionaries so later mutations do not rewrite an earlier accepted page.
A cursor is opaque. Validate its bounded string shape, but never infer the server’s pagination math or treat it as a new URL. The request builder will encode it as one query value.
for value in [True, 125, 125.0, "125"]:
valid_cents = type(value) is int and value >= 0
print(repr(value), valid_cents)Complete decode_page and test status failure before JSON, wrong/duplicate media types, malformed UTF-8, duplicate keys, nonstandard constants and schema mistakes. An empty items list with next None is a valid empty page.
Next we consume multiple validated pages while bounding cursors, duplicates and total work.