# Recipe: Hourly Forecast

import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "zudoku/ui/Accordion";

export const StepHeader = ({ n, children }) => (
  <span style={{ display: "inline-flex", alignItems: "center", gap: "0.75rem" }}>
    <span style={{
      display: "inline-flex",
      alignItems: "center",
      justifyContent: "center",
      width: "1.75rem",
      height: "1.75rem",
      minWidth: "1.75rem",
      borderRadius: "9999px",
      backgroundColor: "#F05514",
      color: "#FFFFFF",
      fontSize: "0.875rem",
      fontWeight: 600,
      flexShrink: 0,
      lineHeight: 1,
    }}>
      {n}
    </span>
    <span>{children}</span>
  </span>
);

This recipe shows how to fetch a sequence of hourly forecasts — ideal for weather apps, commute planners, event scheduling tools, or any feature that needs short-range weather data with per-hour precision.

:::note
**These recipes are teaching examples — not production code.**

- **Host:** examples use the development host **apidev.accuweather.com**. Switch to **api.accuweather.com** for production.
- **Production hardening:** GZIP compression, caching, retries with exponential backoff, and error handling are not included. See the [Best Practices](/developers/best-practices) guide.
:::

## Scenario

> "I want to show users whether it will rain in the next 12 hours, and what the temperature will be each hour."

:::note

You'll need a **LocationKey** before starting. If you don't have one, follow the [Location Search recipe](/recipes/location-search) first.
For this example we use LocationKey `349727` (New York City).

:::

---

<Accordion type="multiple" defaultValue={["step-1"]} className="my-6">

<AccordionItem value="step-1">
  <AccordionTrigger><StepHeader n={1}>Choose a forecast window</StepHeader></AccordionTrigger>
  <AccordionContent>

The Hourly Forecast API supports six durations:

| Duration param | Hours of data | Use case |
|----------------|--------------|----------|
| `1hour` | 1 | Next single hour |
| `12hour` | 12 | Half-day planning |
| `24hour` | 24 | Full-day planning |
| `72hour` | 72 | 3-day hour-by-hour |
| `120hour` | 120 | 5-day hour-by-hour |
| `240hour` | 240 | 10-day hour-by-hour |

  </AccordionContent>
</AccordionItem>

<AccordionItem value="step-2">
  <AccordionTrigger><StepHeader n={2}>Make the request</StepHeader></AccordionTrigger>
  <AccordionContent>

**Endpoint pattern:**
```
GET https://apidev.accuweather.com/forecasts/v1/hourly/{duration}/{locationKey}.json?apikey={YOUR_API_KEY}
```

<CodeTabs syncKey="language">

```ts title="TypeScript"
const apiKey = "{YOUR_API_KEY}";
const locationKey = "349727";
const duration = "12hour";

const response = await fetch(
  `https://apidev.accuweather.com/forecasts/v1/hourly/${duration}/${locationKey}.json?apikey=${apiKey}`
);
const hours: Record<string, any>[] = await response.json();
```

```python title="Python"
import requests

api_key = "{YOUR_API_KEY}"
location_key = "349727"
duration = "12hour"

response = requests.get(
    f"https://apidev.accuweather.com/forecasts/v1/hourly/{duration}/{location_key}.json",
    params={"apikey": api_key},
)
hours = response.json()
```

```csharp title="C#"
using var client = new HttpClient();

var apiKey = "{YOUR_API_KEY}";
var locationKey = "349727";
var duration = "12hour";
var url = $"https://apidev.accuweather.com/forecasts/v1/hourly/{duration}/{locationKey}.json?apikey={apiKey}";
var response = await client.GetAsync(url);
var data = await response.Content.ReadAsStringAsync();
```

```java title="Java"
HttpClient client = HttpClient.newHttpClient();
String apiKey = "{YOUR_API_KEY}";
String locationKey = "349727";
String duration = "12hour";
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://apidev.accuweather.com/forecasts/v1/hourly/"
        + duration + "/" + locationKey + ".json?apikey=" + apiKey))
    .build();

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
String data = response.body();
```

```bash title="bash"
curl "https://apidev.accuweather.com/forecasts/v1/hourly/12hour/349727.json?apikey={YOUR_API_KEY}"
```

</CodeTabs>

  </AccordionContent>
</AccordionItem>

<AccordionItem value="step-3">
  <AccordionTrigger><StepHeader n={3}>Inspect the response</StepHeader></AccordionTrigger>
  <AccordionContent>

Each element in the array represents one hour, starting from the next upcoming hour.

```json
[
  {
    "DateTime": "2024-06-15T15:00:00-04:00",
    "EpochDateTime": 1718478000,
    "WeatherIcon": 3,
    "IconPhrase": "Partly sunny",
    "HasPrecipitation": false,
    "PrecipitationType": null,
    "PrecipitationIntensity": null,
    "IsDaylight": true,
    "Temperature": {
      "Value": 77,
      "Unit": "F",
      "UnitType": 18
    },
    "PrecipitationProbability": 10
  },
  {
    "DateTime": "2024-06-15T16:00:00-04:00",
    "EpochDateTime": 1718481600,
    "WeatherIcon": 18,
    "IconPhrase": "Rain",
    "HasPrecipitation": true,
    "PrecipitationType": "Rain",
    "PrecipitationIntensity": "Light",
    "IsDaylight": true,
    "Temperature": {
      "Value": 74,
      "Unit": "F",
      "UnitType": 18
    },
    "PrecipitationProbability": 72
  }
]
```

:::note

By default, temperature is returned in **Imperial (°F)**. Append `&metric=true` to get Celsius instead.

:::

  </AccordionContent>
</AccordionItem>

<AccordionItem value="step-4">
  <AccordionTrigger><StepHeader n={4}>Find when rain is likely</StepHeader></AccordionTrigger>
  <AccordionContent>

Filter the array for hours where precipitation probability exceeds your threshold.

<CodeTabs syncKey="language">

```ts title="TypeScript"
const rainyHours = hours.filter(
  (h: any) => h.PrecipitationProbability > 50
);

rainyHours.forEach((h: any) => {
  const time = new Date(h.DateTime).toLocaleTimeString("en-US", {
    hour: "2-digit",
    minute: "2-digit",
  });
  console.log(
    `${time}: ${h.IconPhrase} — ${h.PrecipitationProbability}% chance of ${h.PrecipitationType}`
  );
});
// → "4:00 PM: Rain — 72% chance of Rain"
```

```python title="Python"
from datetime import datetime

for h in hours:
    if h["PrecipitationProbability"] > 50:
        time = datetime.fromisoformat(h["DateTime"]).strftime("%I:%M %p")
        ptype = h.get("PrecipitationType", "precipitation")
        prob = h["PrecipitationProbability"]
        print(f"{time}: {h['IconPhrase']} — {prob}% chance of {ptype}")
# → "04:00 PM: Rain — 72% chance of Rain"
```

```csharp title="C#"
using System.Text.Json;

var hours = JsonDocument.Parse(data).RootElement;

foreach (var h in hours.EnumerateArray())
{
    var prob = h.GetProperty("PrecipitationProbability").GetInt32();
    if (prob > 50)
    {
        var time = DateTime.Parse(h.GetProperty("DateTime").GetString()!).ToString("h:mm tt");
        var phrase = h.GetProperty("IconPhrase").GetString();
        var ptype = h.GetProperty("PrecipitationType").GetString();
        Console.WriteLine($"{time}: {phrase} — {prob}% chance of {ptype}");
    }
}
// → "4:00 PM: Rain — 72% chance of Rain"
```

```java title="Java"
import org.json.JSONArray;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;

var hours = new JSONArray(data);
var fmt = DateTimeFormatter.ofPattern("h:mm a");

for (int i = 0; i < hours.length(); i++) {
    var h = hours.getJSONObject(i);
    var prob = h.getInt("PrecipitationProbability");
    if (prob > 50) {
        var time = OffsetDateTime.parse(h.getString("DateTime")).format(fmt);
        var phrase = h.getString("IconPhrase");
        var ptype = h.optString("PrecipitationType", "precipitation");
        System.out.println(time + ": " + phrase + " — " + prob + "% chance of " + ptype);
    }
}
// → "4:00 PM: Rain — 72% chance of Rain"
```

```bash title="bash"
echo "$DATA" | jq -r '.[] | select(.PrecipitationProbability > 50) |
  "\(.DateTime[11:16])  \(.IconPhrase) — \(.PrecipitationProbability)% chance of \(.PrecipitationType // "precipitation")"'
```

</CodeTabs>

  </AccordionContent>
</AccordionItem>

</Accordion>

---

## Extended details

Add `details=true` to unlock additional fields: RealFeel® temperature, wind, humidity, UV index, cloud cover, and more.

```bash
curl "https://apidev.accuweather.com/forecasts/v1/hourly/12hour/349727.json?apikey={YOUR_API_KEY}&details=true"
```

Selected additional fields in the detailed response:

```json
{
  "RealFeelTemperature": {
    "Value": 79,
    "Unit": "F",
    "UnitType": 18,
    "Phrase": "Pleasantly Warm"
  },
  "Wind": {
    "Speed": { "Value": 9, "Unit": "mi/h", "UnitType": 9 },
    "Direction": { "Degrees": 200, "Localized": "SSW", "English": "SSW" }
  },
  "RelativeHumidity": 47,
  "UVIndex": 5,
  "UVIndexText": "Moderate",
  "ThunderstormProbability": 5,
  "RainProbability": 72,
  "SnowProbability": 0,
  "TotalLiquid": { "Value": 0.08, "Unit": "in", "UnitType": 1 },
  "CloudCover": 85
}
```

---

## Key response fields

| Field | Type | Description |
|-------|------|-------------|
| `DateTime` | String | ISO 8601 timestamp for this forecast hour |
| `WeatherIcon` | Integer | Icon code (1–44) |
| `IconPhrase` | String | Human-readable weather description |
| `HasPrecipitation` | Boolean | Whether precipitation is forecast for this hour |
| `PrecipitationType` | String \| null | `"Rain"`, `"Snow"`, `"Ice"`, `"Mixed"`, or `null` |
| `PrecipitationIntensity` | String \| null | `"Light"`, `"Moderate"`, or `"Heavy"` |
| `IsDaylight` | Boolean | Whether this hour is during daylight |
| `Temperature.Value` | Number | Forecast temperature (°F by default) |
| `PrecipitationProbability` | Integer | Percentage chance of any precipitation (0–100) |
| `RealFeelTemperature` *(details)* | Object | Perceived temperature with a comfort phrase |
| `Wind` *(details)* | Object | Wind speed and direction |
| `ThunderstormProbability` *(details)* | Integer | Percentage chance of thunderstorm |
| `CloudCover` *(details)* | Integer | Cloud coverage percentage (0–100) |

---

## Common pitfalls

:::warning

**Units** — The `Temperature` object has a single `Value` field (unlike Current Conditions which has both `Metric` and `Imperial` sub-objects). Control the unit with the `metric` query parameter (`true` = Celsius, `false`/omitted = Fahrenheit).

:::

---

## Complete code sample

Copy a full, runnable example that fetches a 12-hour forecast and lists hours with a high chance of rain.

<CodeTabs syncKey="language">

```ts title="TypeScript"
const API_KEY = "{YOUR_API_KEY}";
const locationKey = "349727";
const duration = "12hour";

// Step 1: Fetch the 12-hour hourly forecast
const res = await fetch(
  `https://apidev.accuweather.com/forecasts/v1/hourly/${duration}/${locationKey}.json?apikey=${API_KEY}`
);
const hours: Record<string, any>[] = await res.json();

console.log("\nHourly Forecast — Next 12 Hours");
console.log("━".repeat(42) + "\n");

// Step 2: Find rainy hours (PrecipitationProbability > 50)
const rainyHours = hours.filter((h) => h.PrecipitationProbability > 50);

if (rainyHours.length > 0) {
  console.log("Hours with a high chance of rain:");
  for (const h of rainyHours) {
    const time = new Date(h.DateTime).toLocaleTimeString("en-US", {
      hour: "2-digit",
      minute: "2-digit",
    });
    const ptype = h.PrecipitationType ?? "precipitation";
    console.log(`  ${time}: ${h.IconPhrase} — ${h.PrecipitationProbability}% chance of ${ptype}`);
  }
} else {
  console.log("No hours with >50% chance of rain in the next 12 hours.");
}
```

```python title="Python"
from datetime import datetime
import requests

API_KEY = "{YOUR_API_KEY}"
location_key = "349727"
duration = "12hour"

# Step 1: Fetch the 12-hour hourly forecast
resp = requests.get(
    f"https://apidev.accuweather.com/forecasts/v1/hourly/{duration}/{location_key}.json",
    params={"apikey": API_KEY},
)
hours = resp.json()

print("\nHourly Forecast — Next 12 Hours")
print("━" * 42 + "\n")

# Step 2: Find rainy hours (PrecipitationProbability > 50)
rainy = [h for h in hours if h["PrecipitationProbability"] > 50]

if rainy:
    print("Hours with a high chance of rain:")
    for h in rainy:
        time = datetime.fromisoformat(h["DateTime"]).strftime("%I:%M %p")
        ptype = h.get("PrecipitationType") or "precipitation"
        print(f"  {time}: {h['IconPhrase']} — {h['PrecipitationProbability']}% chance of {ptype}")
else:
    print("No hours with >50% chance of rain in the next 12 hours.")
```

```csharp title="C#"
using System.Text.Json;

var apiKey = "{YOUR_API_KEY}";
var locationKey = "349727";
var duration = "12hour";

// Step 1: Fetch the 12-hour hourly forecast
using var client = new HttpClient();
var response = await client.GetStringAsync(
    $"https://apidev.accuweather.com/forecasts/v1/hourly/{duration}/{locationKey}.json?apikey={apiKey}");

using var doc = JsonDocument.Parse(response);

Console.WriteLine("\nHourly Forecast — Next 12 Hours");
Console.WriteLine(new string('━', 42) + "\n");

// Step 2: Find rainy hours (PrecipitationProbability > 50)
var rainyFound = false;
foreach (var h in doc.RootElement.EnumerateArray())
{
    var prob = h.GetProperty("PrecipitationProbability").GetInt32();
    if (prob > 50)
    {
        if (!rainyFound)
        {
            Console.WriteLine("Hours with a high chance of rain:");
            rainyFound = true;
        }
        var time = DateTime.Parse(h.GetProperty("DateTime").GetString()!).ToString("h:mm tt");
        var phrase = h.GetProperty("IconPhrase").GetString();
        var ptypeEl = h.GetProperty("PrecipitationType");
        var ptype = ptypeEl.ValueKind == JsonValueKind.Null ? "precipitation" : ptypeEl.GetString();
        Console.WriteLine($"  {time}: {phrase} — {prob}% chance of {ptype}");
    }
}

if (!rainyFound)
{
    Console.WriteLine("No hours with >50% chance of rain in the next 12 hours.");
}
```

```java title="Java"
import org.json.JSONArray;
import java.net.URI;
import java.net.http.*;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;

public class HourlyForecast {
    public static void main(String[] args) throws Exception {
        String apiKey = "{YOUR_API_KEY}";
        String locationKey = "349727";
        String duration = "12hour";

        // Step 1: Fetch the 12-hour hourly forecast
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(
                "https://apidev.accuweather.com/forecasts/v1/hourly/"
                + duration + "/" + locationKey + ".json?apikey=" + apiKey))
            .build();

        HttpResponse<String> response =
            client.send(request, HttpResponse.BodyHandlers.ofString());

        var hours = new JSONArray(response.body());
        var fmt = DateTimeFormatter.ofPattern("h:mm a");

        System.out.println("\nHourly Forecast — Next 12 Hours");
        System.out.println("━".repeat(42) + "\n");

        // Step 2: Find rainy hours (PrecipitationProbability > 50)
        boolean rainyFound = false;
        for (int i = 0; i < hours.length(); i++) {
            var h = hours.getJSONObject(i);
            int prob = h.getInt("PrecipitationProbability");
            if (prob > 50) {
                if (!rainyFound) {
                    System.out.println("Hours with a high chance of rain:");
                    rainyFound = true;
                }
                var time = OffsetDateTime.parse(h.getString("DateTime")).format(fmt);
                var phrase = h.getString("IconPhrase");
                var ptype = h.optString("PrecipitationType", "precipitation");
                System.out.println("  " + time + ": " + phrase + " — " + prob + "% chance of " + ptype);
            }
        }

        if (!rainyFound) {
            System.out.println("No hours with >50% chance of rain in the next 12 hours.");
        }
    }
}
```

```bash title="bash"
#!/bin/bash
API_KEY="{YOUR_API_KEY}"
LOCATION_KEY="349727"
DURATION="12hour"

# Step 1: Fetch the 12-hour hourly forecast
DATA=$(curl -s "https://apidev.accuweather.com/forecasts/v1/hourly/${DURATION}/${LOCATION_KEY}.json?apikey=${API_KEY}")

echo ""
echo "Hourly Forecast — Next 12 Hours"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""

# Step 2: Find rainy hours (PrecipitationProbability > 50)
echo "Hours with a high chance of rain:"
echo "$DATA" | jq -r '.[] | select(.PrecipitationProbability > 50) | "  \(.DateTime[11:16])  \(.IconPhrase) — \(.PrecipitationProbability)% chance of \(.PrecipitationType // "precipitation")"'
```

</CodeTabs>

---

## Next steps

- [5-Day Daily Forecast](/recipes/daily-forecast) — high/low temperatures and day/night summaries for longer-range planning
- [Current Conditions](/recipes/current-conditions) — complement forecasts with a real-time snapshot
