A data document beside a thermometer and magnifying glass, representing checks before displaying an API temperature.
Software Development

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.

Three questions before displaying a number
CheckQuestion it answers
Request statusDid the HTTP request succeed under the response’s status?
JSON decodingCan the response text be read as JSON?
Display ruleDoes the parsed value have the shape, number type and unit this screen accepts?
The function below covers the third check. It does not make a network request or replace HTTP and JSON error handling.

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.

Predict the display decision before running the code
Practice inputExpected decision
temperature: 0; unit: CDisplay 0 °C. Zero is a valid number.
temperature missing; unit: CReject: Temperature must be a number.
temperature: “0”; unit: CReject the text value; do not silently convert it.
temperature: False; unit: CReject the Boolean value; do not treat it as zero.
temperature: 68; unit: FReject: Expected Celsius (C). Do not display 68 °C.
temperature: 12; unit missingReject: Expected Celsius (C).
These are selected fields from constructed Python dictionaries. Missing information remains missing. The exercise rejects Fahrenheit input rather than performing a conversion.

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.

The simplified contract, code, comparison table and exercise are original material. The inputs are constructed and are not live weather observations. The function checks parsed data only; it is not a complete weather client.

Leave a Reply

Your email address will not be published. Required fields are marked *