Your API returned 200 OK. Check the value and unit before displaying it
Your first Python app asks a weather service for a temperature. The request succeeds, a number appears and the project feels finished. Then someone changes the unit setting, or a response arrives without the field your code expects.
An application programming interface, or API, lets one piece of software exchange information with another. Receiving a successful response is one useful check. Your app also needs to decide whether the returned data fits the promise it makes on screen.
This guide takes one small display rule and turns it into a runnable Python exercise. You will practise accepting a genuine zero, rejecting missing or unsuitable values and checking the unit before adding a Celsius label.
Separate the request from the display decision
freeCodeCamp’s weather-app tutorial, published 28 October 2026 is a timely example of beginners connecting Python to a real service. This article explores a narrower next step: the checks between receiving data and displaying it. This is an evergreen practice exercise prompted by that tutorial.
HTTP is the protocol commonly used for web requests. The HTTP standard defines 200 OK as a successful request. That status does not, by itself, establish that a particular field has the type or unit your display expects.
JSON is a text format for exchanging data. Parsing it converts that text into Python values. Python’s JSON documentation describes decoding errors for invalid JSON. A successfully parsed object can still omit the field your app needs.
| Check | Question it answers |
|---|---|
| Request status | Did the HTTP request succeed under the response’s status? |
| JSON decoding | Can the response text be read as JSON? |
| Display rule | Does the parsed value have the shape, number type and unit this screen accepts? |
An example: one deliberately small data contract
Illustrative example: a practice weather display accepts a Python dictionary with two fields: temperature, containing a built-in integer or float, and unit, containing exactly "C". A dictionary stores values under named keys. This is a simplified practice format, not a response format promised by a particular provider.
A data contract is an explicit agreement about the fields and values a consumer expects. Here the agreement is intentionally narrow: display finite Celsius numbers, including zero, and explain when the input does not qualify.
Units need checking because they change what a number means. For example, Open-Meteo’s API documentation offers Celsius and Fahrenheit temperature options. A value cannot acquire a different unit just because the display appends a different label. Adapt this exercise to a real provider’s documented fields before connecting it to that service.
| Practice input | Expected decision |
|---|---|
| temperature: 0; unit: C | Display 0 °C. Zero is a valid number. |
| temperature missing; unit: C | Reject: Temperature must be a number. |
| temperature: “0”; unit: C | Reject the text value; do not silently convert it. |
| temperature: False; unit: C | Reject the Boolean value; do not treat it as zero. |
| temperature: 68; unit: F | Reject: Expected Celsius (C). Do not display 68 °C. |
| temperature: 12; unit missing | Reject: Expected Celsius (C). |
Run the check locally
Save the code below as temperature_check.py. Run it with python3 temperature_check.py, or the Python command used in your environment. It uses Python’s standard library and does not need an API key, network connection or additional package.
On a narrow screen, scroll the code panel sideways to keep each line intact.
import math
def temperature_label(payload):
if not isinstance(payload, dict):
raise ValueError("Expected an object.")
value = payload.get("temperature")
if type(value) not in (int, float):
raise ValueError("Temperature must be a number.")
if type(value) is float and not math.isfinite(value):
raise ValueError("Temperature must be finite.")
if payload.get("unit") != "C":
raise ValueError("Expected Celsius (C).")
return f"{value} °C"
if __name__ == "__main__":
example = {"temperature": 0, "unit": "C"}
try:
print(temperature_label(example))
except ValueError as error:
print(f"Cannot display temperature: {error}")
The supplied example prints 0 °C. Replace its dictionary with {"temperature": 68, "unit": "F"} and run it again. The output should be Cannot display temperature: Expected Celsius (C). The function raises a ValueError to reject unsuitable data; the caller catches it and chooses the message.
Why these small checks matter
1. Keep missing separate from zero
Purpose: Avoid inventing a measurement.
Using payload.get("temperature", 0) would supply zero when the key is absent. That could make missing data look like a real reading. Likewise, a test such as if not value rejects a genuine zero because zero is false in a Boolean condition.
The example checks the value’s type instead. Both an absent key and a supplied None are rejected here, while the integer zero is accepted. A larger application may need separate error categories for those two failures.
2. Choose the types you actually accept
Purpose: Prevent a plausible-looking substitute from slipping through.
Python documents Boolean values as a subclass of integers. That is why this small contract checks the exact built-in type instead of accepting everything that behaves like an integer. The text "0" also remains text.
A float can represent infinity or a value called NaN, meaning “not a number”. Python’s math.isfinite check rejects those float values here. This is not a complete definition of a plausible weather measurement; an application may need additional limits.
3. Explain the limit of the result
Purpose: Keep a valid display label separate from a trustworthy forecast.
Passing this function establishes only the small contract described above. It does not check the location, observation time, source quality or whether the reading is recent. Those checks need additional fields and rules.
When validation fails, show an unavailable-data message that the application can support. Do not replace the reading with zero or imply that a current reading is available. If you show an older saved reading, label its time and status clearly.
Try one useful extension
Before editing the function, predict what it should do with a negative temperature, a supplied None, a list instead of a dictionary and a float containing infinity. Run each case and compare the result with your prediction.
Then write a small display agreement for your own API project:
Choose one field your app already displays and replace an unstated assumption with an explicit rule. Keep the rule, one valid example and one rejected example beside the code so another developer can understand the decision.


