Back to Blog

Product Management 6 min read

From JSON to a web page: how an API's answer becomes what you see

At the end of Part 2, the weather API had answered with JSON: a block of labelled values, such as a temperature of 11.6 and a weather code of 2. That's not what anyone sees on the weather page. They see "12°", "Partly cloudy" and a little sun behind a cloud. This part is about the gap between the two, because it's where a lot of product decisions quietly get made.

Three moves

The JSON that comes back is just text. Turning it into the page you see takes three moves, and they're the same on almost any site that uses an API.

Three moves from JSON to a web page

LikeUnpacking a delivery note, so each value sits in its own labelled tray

Read it

The answer arrives as text. response.json() turns it into labelled values the code can pick out, such as data.current.temperature_2m.

LikeRewriting a supplier's codes into words your customers understand

Translate it

Raw values aren't ready for people. 11.6 becomes "12°", a date becomes "Thu" and weather code 2 becomes "Partly cloudy".

LikeA mail merge filling the blanks in a letter that was written once

Place it

The page's HTML already has a labelled spot for each value. The code finds each spot and writes the value in.

The browser redraws the page as each spot is filled, usually faster than you can notice. Nothing is reloaded: only the blanks change.

The best comparison I know is a mail merge. The letter (the page) is written once, with blanks where the details go. The spreadsheet (the JSON) holds the details. The merge (the code) fills each blank. Every visitor gets the same page, filled in with fresh weather.

Here's where each value in the weather answer ends up:

Where each value ends up

In the JSONWhat the code doesOn the page
"temperature_2m": 11.6Rounds it and adds a degree sign12° the big number
"apparent_temperature": 9.2Rounds it and adds °CFeels like 9°C
"wind_speed_10m": 9.8Rounds it and adds the unit it asked forWind 10 mph
"weather_code": 2Looks it up in the legend (below) Partly cloudy
"is_day": 1Picks the day icon, not the night oneA sun, not a moon
"time": "2026-11-04T14:15"Keeps just the clock timeUpdated 14:15
"daily": { 5 days }Fills one day tile, then repeats it for each dayToday, Thu, Fri, Sat, Sun
Before the answer arrives, each spot shows a placeholder ("--" and "Loading the latest weather"). What a spot should say while it waits, or if its value never comes, is a product decision worth writing into the acceptance criteria.

From a number to a picture

Translating is where most of the product decisions hide, and one translation deserves a closer look. The API never sends a picture, or even the words "partly cloudy". It sends a number from a standard list of weather codes, and the page does the rest. Try any of the 28 codes:

The weather code is a map legend

LikeThe key on a map: the map only prints a small symbol, and the legend tells you it means "church" or "car park"

The API sends only the number. The page's lookup table matches each of the 28 codes to some words and one of twelve icons, so the same data could look completely different in another product.

This split is the same in most products. The API sends compact, raw data, and the product decides how to present it. It's also why the answer is so small (around a kilobyte here) and so quick to arrive.

One example of that split: Open-Meteo has no code for sleet, so the page treats "freezing rain" and "freezing drizzle" as sleet. That's a presentation decision, made in the product, not in the API.

The code, simplified

You don't need to read code to follow this series, but if you're curious, here are the three moves in a few lines of JavaScript:

const response = await fetch(address);       // ask, and wait for the answer
const data = await response.json();          // 1. read: text into labelled values

const temp = Math.round(data.current.temperature_2m) + "°";   // 2. translate: 11.6 into "12°"
const sky = legend[data.current.weather_code];                // 2. translate: 2 into "Partly cloudy"

document.querySelector("[data-wx-temp]").textContent = temp;  // 3. place: into its spot
document.querySelector("[data-wx-condition]").textContent = sky.label;

fetch is built into every modern browser. [data-wx-temp] is the label on the spot in the page's HTML where the temperature goes, the blank in the mail merge.

What this means for product managers

  • Every translation is a decision. Rounding 11.5 up or down, mph or km/h, "Thu" or "Thursday", what counts as sleet: none of it comes from the API. Someone chooses, so make sure it's written down.
  • Design the empty states. What does each spot show while it waits, if a value is missing, or if the whole answer fails? Put it in the acceptance criteria, or the team will guess.
  • Test with awkward values. Minus temperatures, a 0, a missing field, a very long place name. Real answers will contain all of them eventually.
  • Treat the field names as a contract. The page looks for temperature_2m by name. If the provider renames it in a new version, the page shows "--" until someone updates the code, so version changes belong on the roadmap.

Part 4 moves from the data to the small print: API keys, keeping them secret, and the limits and costs that come with them.

  • APIs
  • Product Management
  • JSON
  • Learning

Published 05 Nov 2026 Updated 05 Nov 2026