What this tutorial covers: a line-by-line explanation of an Office Script that reads a city name from a worksheet, calls the OpenWeatherMap API, and writes temperature, description, humidity, and wind back into the sheet. While you may not need rain information in your spreadsheets any time soon, learning how to call APIs from Excel can be extremely useful. You will be able to use other APIs to pull stock market and goverment data or communicate with other applications.
YOUR_OPENWEATHERMAP_API_KEY in the script.The key is generated immediately, but it takes a couple of hours before they activate it. You will see your key on the 'API Keys' tab (it is a string of random letters). I covered it with a red rectangle on the screenshot to prevent others from using mine:

interface Weather { id?: number; main?: string; description?: string; icon?: string; }
interface Main { temp?: number; humidity?: number; }
interface Wind { speed?: number; deg?: number; }
interface OpenWeatherResponse {
weather?: Weather[];
main?: Main;
wind?: Wind;
name?: string;
cod?: number;
message?: string;
}
async function main(workbook: ExcelScript.Workbook) {
const sheet: ExcelScript.Worksheet = workbook.getActiveWorksheet();
const cityRange: ExcelScript.Range = sheet.getRange("B1");
const city: string = (cityRange.getText() || "").trim();
if (!city) {
sheet.getRange("B2").setValue("Enter city in B1");
return;
}
const apiKey: string = "YOUR_OPENWEATHERMAP_API_KEY";
const url: string = `https://api.openweathermap.org/data/2.5/weather?q=${encodeURIComponent(
city
)}&units=metric&appid=${apiKey}`;
try {
const response = await fetch(url);
if (!response.ok) {
sheet.getRange("B2").setValue(`API error ${response.status}`);
return;
}
const data = (await response.json()) as OpenWeatherResponse;
const temp: number | string = data.main?.temp ?? "N/A";
const desc: string = data.weather?.[0]?.description ?? "N/A";
const humidity: number | string = data.main?.humidity ?? "N/A";
const wind: number | string = data.wind?.speed ?? "N/A";
sheet.getRange("A2").setValue("Temperature C");
sheet.getRange("B2").setValue(temp);
sheet.getRange("A3").setValue("Description");
sheet.getRange("B3").setValue(desc);
sheet.getRange("A4").setValue("Humidity %");
sheet.getRange("B4").setValue(humidity);
sheet.getRange("A5").setValue("Wind m/s");
sheet.getRange("B5").setValue(wind);
} catch (err) {
sheet.getRange("B2").setValue("Fetch failed: " + String(err));
}
}
The top block defines TypeScript interfaces that model the JSON returned by OpenWeatherMap. These are optional-field shapes that help with type safety and autocompletion in the editor.
weather array (description, icon, etc.).temp and humidity.main functionOffice Scripts run the exported main function. It receives an ExcelScript.Workbook object that represents the current workbook.
const sheet = workbook.getActiveWorksheet();
const cityRange = sheet.getRange("B1");
const city = (cityRange.getText() || "").trim();
Behavior: the script reads the text in cell B1, trims whitespace, and stores it in city. If the cell is empty, the script writes a prompt into B2 and exits early.
const apiKey = "YOUR_OPENWEATHERMAP_API_KEY";
const url = `https://api.openweathermap.org/data/2.5/weather?q=${encodeURIComponent(city)}&units=metric&appid=${apiKey}`;
Notes:
imperial for Fahrenheit.YOUR_OPENWEATHERMAP_API_KEY with your real API key.const response = await fetch(url);
if (!response.ok) {
sheet.getRange("B2").setValue(`API error ${response.status}`);
return;
}
const data = (await response.json()) as OpenWeatherResponse;
Behavior: the script uses the global fetch API to call OpenWeatherMap. If the HTTP response status is not OK (200), it writes the status code to B2 and stops. Otherwise it parses JSON into data.
const temp = data.main?.temp ?? "N/A";
const desc = data.weather?.[0]?.description ?? "N/A";
const humidity = data.main?.humidity ?? "N/A";
const wind = data.wind?.speed ?? "N/A";
These lines use optional chaining (?.) and the nullish coalescing operator (??) to avoid runtime errors when fields are missing. If a value is absent, the script uses "N/A".
sheet.getRange("A2").setValue("Temperature C");
sheet.getRange("B2").setValue(temp);
sheet.getRange("A3").setValue("Description");
sheet.getRange("B3").setValue(desc);
sheet.getRange("A4").setValue("Humidity %");
sheet.getRange("B4").setValue(humidity);
sheet.getRange("A5").setValue("Wind m/s");
sheet.getRange("B5").setValue(wind);
The script writes labels in column A and the corresponding values in column B. You can change the target cells to suit your layout.
catch (err) {
sheet.getRange("B2").setValue("Fetch failed: " + String(err));
}
If the network request or JSON parsing throws an exception, the script writes a short error message to B2. This helps with basic troubleshooting.
B1 (for example, Paris).The screen will look like this: 
B2–B5 (with labels in column A).
API error 401 or similar, verify your API key and that it is active.Paris,FR).&units=metric to imperial for Fahrenheit or remove it to get Kelvin.&lang=es (for Spanish) to the URL.data.weather[0].icon to an image URL and inserting it into the sheet (requires additional steps).Math.round(temp).// change units to imperial for Fahrenheit
const url = `https://api.openweathermap.org/data/2.5/weather?q=${encodeURIComponent(city)}&units=imperial&appid=${apiKey}`;