// Get golden hour times for New York on June 21
const response = await fetch(
"https://worlddataapi.com/v1/sun/40.71,-74.01?date=2026-06-21",
{ headers: { "X-API-Key": "YOUR_API_KEY" } },
);
const data = await response.json();
console.log(data.golden_hour);
// {
// "morning": { "start": "2026-06-21T05:25:00-04:00", "end": "2026-06-21T06:05:00-04:00" },
// "evening": { "start": "2026-06-21T19:51:00-04:00", "end": "2026-06-21T20:31:00-04:00" }
// }
Golden hour is the window of soft, warm light that photographers prize. This guide covers building a golden hour calculator that provides accurate times for any location on Earth.
The Challenge#
Calculating golden hour times is harder than it looks. The naive approach of adding a fixed offset to sunrise/sunset times fails because:
Golden hour duration varies by latitude — Near the equator, golden hour lasts about 20-30 minutes. In northern latitudes during summer, it can last over an hour.
Seasonal variation is significant — The same location has different golden hour durations in winter vs summer.
The math is complex — Calculating when the sun reaches a specific altitude (around 6 degrees) requires spherical trigonometry involving declination, hour angle, and atmospheric refraction.
Edge cases break simple formulas — Polar regions during midnight sun have no traditional golden hour at all.
The World Data API handles these calculations for you, returning accurate golden hour times for any coordinates and date.
Prerequisites#
Before starting, you will need:
A World Data API key (get one free at worlddataapi.com)
Basic knowledge of JavaScript or Python
An HTTP client for making API requests
The sun endpoint is a Premium endpoint, available on Starter ($9/month) and higher plans.
What Is Golden Hour?#
Golden hour occurs when the sun is low in the sky — roughly 6 degrees above the horizon to just below it. The light has several qualities photographers seek:
Warm color temperature — Sunlight travels through more atmosphere, filtering blue wavelengths
Soft shadows — Low sun angle creates gentle, directional light
Even exposure — Less contrast between highlights and shadows
There are two golden hours per day:
Morning golden hour: From sunrise until the sun rises above ~6 degrees
Evening golden hour: From when the sun drops below ~6 degrees until sunset
Accuracy note: Golden hour times are calculated using standard astronomical algorithms. Actual observed times may differ by several minutes due to atmospheric conditions, local elevation, and horizon obstructions (mountains, buildings). The API does not account for these local factors.
API Response Structure#
The sun endpoint returns golden hour alongside other solar events:
{
"location": { "latitude": 40.71, "longitude": -74.01 },
"date": "2026-06-21",
"timezone": "America/New_York",
"sunrise": "2026-06-21T05:25:00-04:00",
"sunset": "2026-06-21T20:31:00-04:00",
"solar_noon": "2026-06-21T12:58:00-04:00",
"day_length": "15:06",
"golden_hour": {
"morning": {
"start": "2026-06-21T05:25:00-04:00",
"end": "2026-06-21T06:05:00-04:00"
},
"evening": {
"start": "2026-06-21T19:51:00-04:00",
"end": "2026-06-21T20:31:00-04:00"
}
},
"blue_hour": {
"morning": {
"start": "2026-06-21T04:53:00-04:00",
"end": "2026-06-21T05:25:00-04:00"
},
"evening": {
"start": "2026-06-21T20:31:00-04:00",
"end": "2026-06-21T21:03:00-04:00"
}
}
}
Basic Implementation#
curl#
curl -X GET "https://worlddataapi.com/v1/sun/40.71,-74.01?date=2026-06-21" \
-H "X-API-Key: YOUR_API_KEY"
Python#
import requests
def get_golden_hour(latitude: float, longitude: float, date: str, api_key: str) -> dict:
url = f"https://worlddataapi.com/v1/sun/{latitude},{longitude}"
headers = {"X-API-Key": api_key}
params = {"date": date}
response = requests.get(url, headers=headers, params=params)
response.raise_for_status()
data = response.json()
return {
"date": data["date"],
"timezone": data["timezone"],
"morning": data["golden_hour"]["morning"],
"evening": data["golden_hour"]["evening"],
"sunrise": data["sunrise"],
"sunset": data["sunset"],
}
# Get golden hour for Central Park, NYC
golden_hour = get_golden_hour(40.7829, -73.9654, "2026-06-21", API_KEY)
print(f"Morning golden hour: {golden_hour['morning']['start']} - {golden_hour['morning']['end']}")
JavaScript#
async function getGoldenHour(latitude, longitude, date) {
const response = await fetch(
`https://worlddataapi.com/v1/sun/${latitude},${longitude}?date=${date}`,
{ headers: { "X-API-Key": API_KEY } },
);
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const data = await response.json();
return {
date: data.date,
timezone: data.timezone,
morning: data.golden_hour.morning,
evening: data.golden_hour.evening,
sunrise: data.sunrise,
sunset: data.sunset,
};
}
// Get golden hour for Central Park, NYC
const goldenHour = await getGoldenHour(40.7829, -73.9654, "2026-06-21");
console.log(
`Morning golden hour: ${goldenHour.morning.start} - ${goldenHour.morning.end}`,
);
Using Location Names#
You can also query by city GeoNames ID or airport IATA code:
// By airport code
const jfkData = await fetch(
"https://worlddataapi.com/v1/sun/JFK?date=2026-06-21",
{ headers: { "X-API-Key": API_KEY } },
);
// By GeoNames city ID (Berlin = 2950159)
const berlinData = await fetch(
"https://worlddataapi.com/v1/sun/2950159?date=2026-06-21",
{ headers: { "X-API-Key": API_KEY } },
);
Building the UI#
Time Display Component#
function GoldenHourDisplay({ goldenHour, timezone }) {
const formatTime = (isoString) => {
return new Date(isoString).toLocaleTimeString("en-US", {
hour: "numeric",
minute: "2-digit",
timeZone: timezone,
});
};
const getDuration = (start, end) => {
const ms = new Date(end) - new Date(start);
return Math.round(ms / 60000); // minutes
};
return (
<div className="golden-hour-display">
<section className="time-block morning">
<h3>Morning Golden Hour</h3>
<p className="time-range">
{formatTime(goldenHour.morning.start)} –{" "}
{formatTime(goldenHour.morning.end)}
</p>
<p className="duration">
{getDuration(
goldenHour.morning.start,
goldenHour.morning.end,
)}{" "}
minutes
</p>
</section>
<section className="time-block evening">
<h3>Evening Golden Hour</h3>
<p className="time-range">
{formatTime(goldenHour.evening.start)} –{" "}
{formatTime(goldenHour.evening.end)}
</p>
<p className="duration">
{getDuration(
goldenHour.evening.start,
goldenHour.evening.end,
)}{" "}
minutes
</p>
</section>
</div>
);
}
Countdown Timer#
Show time until the next golden hour:
function GoldenHourCountdown({ goldenHour }) {
const [timeUntil, setTimeUntil] = useState("");
useEffect(() => {
const update = () => {
const now = new Date();
const morningStart = new Date(goldenHour.morning.start);
const morningEnd = new Date(goldenHour.morning.end);
const eveningStart = new Date(goldenHour.evening.start);
const eveningEnd = new Date(goldenHour.evening.end);
let target, label;
if (now < morningStart) {
target = morningStart;
label = "Morning golden hour in";
} else if (now < morningEnd) {
target = morningEnd;
label = "Morning golden hour ends in";
} else if (now < eveningStart) {
target = eveningStart;
label = "Evening golden hour in";
} else if (now < eveningEnd) {
target = eveningEnd;
label = "Evening golden hour ends in";
} else {
setTimeUntil("Golden hour is over for today");
return;
}
const diff = target - now;
const hours = Math.floor(diff / 3600000);
const minutes = Math.floor((diff % 3600000) / 60000);
setTimeUntil(`${label} ${hours}h ${minutes}m`);
};
update();
const interval = setInterval(update, 60000);
return () => clearInterval(interval);
}, [goldenHour]);
return <div className="countdown">{timeUntil}</div>;
}
Including Blue Hour#
Blue hour is the period of twilight before sunrise and after sunset when the sky has a deep blue color. Many photographers value it equally:
async function getPhotographyTimes(latitude, longitude, date) {
const response = await fetch(
`https://worlddataapi.com/v1/sun/${latitude},${longitude}?date=${date}`,
{ headers: { "X-API-Key": API_KEY } },
);
const data = await response.json();
return {
date: data.date,
timezone: data.timezone,
schedule: [
{
name: "Morning Blue Hour",
start: data.blue_hour.morning.start,
end: data.blue_hour.morning.end,
type: "blue",
},
{
name: "Sunrise",
time: data.sunrise,
type: "event",
},
{
name: "Morning Golden Hour",
start: data.golden_hour.morning.start,
end: data.golden_hour.morning.end,
type: "golden",
},
{
name: "Evening Golden Hour",
start: data.golden_hour.evening.start,
end: data.golden_hour.evening.end,
type: "golden",
},
{
name: "Sunset",
time: data.sunset,
type: "event",
},
{
name: "Evening Blue Hour",
start: data.blue_hour.evening.start,
end: data.blue_hour.evening.end,
type: "blue",
},
],
};
}
Timeline Visualization#
function PhotoTimeline({ schedule, timezone }) {
return (
<div className="photo-timeline">
{schedule.map((item, i) => (
<div key={i} className={`timeline-item ${item.type}`}>
<span className="time">
{item.time
? formatTime(item.time, timezone)
: `${formatTime(item.start, timezone)} - ${formatTime(item.end, timezone)}`}
</span>
<span className="label">{item.name}</span>
</div>
))}
</div>
);
}
Multi-Day Planning#
Photographers often plan shoots days in advance:
async function getWeeklyGoldenHours(latitude, longitude, startDate) {
const days = [];
for (let i = 0; i < 7; i++) {
const date = new Date(startDate);
date.setDate(date.getDate() + i);
const dateStr = date.toISOString().split("T")[0];
const response = await fetch(
`https://worlddataapi.com/v1/sun/${latitude},${longitude}?date=${dateStr}`,
{ headers: { "X-API-Key": API_KEY } },
);
days.push(await response.json());
}
return days;
}
// Batch requests for better performance
async function getWeeklyGoldenHoursBatch(latitude, longitude, startDate) {
const dates = Array.from({ length: 7 }, (_, i) => {
const date = new Date(startDate);
date.setDate(date.getDate() + i);
return date.toISOString().split("T")[0];
});
const requests = dates.map((date) =>
fetch(
`https://worlddataapi.com/v1/sun/${latitude},${longitude}?date=${date}`,
{ headers: { "X-API-Key": API_KEY } },
).then((r) => r.json()),
);
return Promise.all(requests);
}
Handling Edge Cases#
Polar Regions#
Near the poles, golden hour can last for hours — or not occur at all:
async function getGoldenHourSafe(latitude, longitude, date) {
const response = await fetch(
`https://worlddataapi.com/v1/sun/${latitude},${longitude}?date=${date}`,
{ headers: { "X-API-Key": API_KEY } },
);
const data = await response.json();
// Check for polar conditions
if (data.polar_condition === "midnight_sun") {
return {
available: false,
reason: "Midnight sun — the sun does not set",
suggestion: "Any time has similar lighting conditions",
};
}
if (data.polar_condition === "polar_night") {
return {
available: false,
reason: "Polar night — the sun does not rise",
suggestion: "No natural golden hour available",
};
}
// Normal case
return {
available: true,
morning: data.golden_hour.morning,
evening: data.golden_hour.evening,
};
}
// Tromsø, Norway in June
const tromso = await getGoldenHourSafe(69.6496, 18.956, "2026-06-21");
// { available: false, reason: 'Midnight sun — the sun does not set', ... }
Null Values#
When golden hour doesn't occur (polar regions), the API returns null:
if (data.golden_hour === null) {
// Handle polar conditions
}
Notifications#
Push notifications for golden hour:
async function scheduleGoldenHourNotification(
latitude,
longitude,
date,
minutesBefore = 30,
) {
const goldenHour = await getGoldenHour(latitude, longitude, date);
const notifications = [];
// Morning notification
const morningNotifyTime = new Date(goldenHour.morning.start);
morningNotifyTime.setMinutes(
morningNotifyTime.getMinutes() - minutesBefore,
);
notifications.push({
time: morningNotifyTime,
title: "Morning Golden Hour Starting",
body: `Golden hour begins in ${minutesBefore} minutes`,
});
// Evening notification
const eveningNotifyTime = new Date(goldenHour.evening.start);
eveningNotifyTime.setMinutes(
eveningNotifyTime.getMinutes() - minutesBefore,
);
notifications.push({
time: eveningNotifyTime,
title: "Evening Golden Hour Starting",
body: `Golden hour begins in ${minutesBefore} minutes`,
});
return notifications;
}
Weather Integration#
Golden hour is useless if it's cloudy. Consider adding weather data:
function shouldShoot(goldenHour, weatherForecast) {
// Weather API not provided by World Data API
// Integrate with a weather service
const cloudCover = weatherForecast.clouds; // percentage
if (cloudCover > 80) {
return {
recommended: false,
reason: "Heavy cloud cover expected",
alternative: "Consider indoor or overcast photography",
};
}
if (cloudCover > 40) {
return {
recommended: true,
note: "Partial clouds — could create dramatic lighting",
};
}
return {
recommended: true,
note: "Clear skies — ideal golden hour conditions",
};
}
Caching Strategy#
Sun position data is deterministic — the same coordinates and date always produce the same result:
const cache = new Map();
async function getCachedSunData(latitude, longitude, date) {
// Round coordinates to reduce cache variations
const lat = latitude.toFixed(2);
const lon = longitude.toFixed(2);
const key = `${lat},${lon}:${date}`;
if (cache.has(key)) {
return cache.get(key);
}
const data = await fetchSunData(latitude, longitude, date);
cache.set(key, data);
return data;
}
For static locations (user's home, favorite spots), cache aggressively.
Complete Photography App Example#
class PhotographyPlanner {
constructor(apiKey) {
this.apiKey = apiKey;
this.cache = new Map();
}
async getPlanForDate(latitude, longitude, date) {
const sunData = await this.getSunData(latitude, longitude, date);
if (sunData.polar_condition) {
return this.handlePolarCondition(sunData);
}
return {
date,
location: { latitude, longitude },
timezone: sunData.timezone,
dayLength: sunData.day_length,
schedule: this.buildSchedule(sunData),
bestTimes: this.rankBestTimes(sunData),
tips: this.generateTips(sunData),
};
}
buildSchedule(sunData) {
return [
{
time: sunData.blue_hour.morning.start,
event: "Blue hour begins",
type: "blue",
},
{ time: sunData.sunrise, event: "Sunrise", type: "sun" },
{
time: sunData.golden_hour.morning.end,
event: "Golden hour ends",
type: "golden",
},
{
time: sunData.solar_noon,
event: "Solar noon (harsh light)",
type: "avoid",
},
{
time: sunData.golden_hour.evening.start,
event: "Golden hour begins",
type: "golden",
},
{ time: sunData.sunset, event: "Sunset", type: "sun" },
{
time: sunData.blue_hour.evening.end,
event: "Blue hour ends",
type: "blue",
},
];
}
rankBestTimes(sunData) {
return [
{
period: "Evening Golden Hour",
start: sunData.golden_hour.evening.start,
end: sunData.golden_hour.evening.end,
rating: 5,
notes: "Best light quality, most popular time",
},
{
period: "Morning Golden Hour",
start: sunData.golden_hour.morning.start,
end: sunData.golden_hour.morning.end,
rating: 5,
notes: "Fewer crowds, requires early wake-up",
},
{
period: "Evening Blue Hour",
start: sunData.blue_hour.evening.start,
end: sunData.blue_hour.evening.end,
rating: 4,
notes: "Great for cityscapes with artificial lights",
},
{
period: "Morning Blue Hour",
start: sunData.blue_hour.morning.start,
end: sunData.blue_hour.morning.end,
rating: 4,
notes: "Quiet, peaceful atmosphere",
},
];
}
generateTips(sunData) {
const tips = [];
const morningDuration = this.getDurationMinutes(
sunData.golden_hour.morning.start,
sunData.golden_hour.morning.end,
);
if (morningDuration > 45) {
tips.push("Long morning golden hour — plenty of time to set up");
} else if (morningDuration < 30) {
tips.push(
"Short morning golden hour — prepare your shots in advance",
);
}
return tips;
}
getDurationMinutes(start, end) {
return Math.round((new Date(end) - new Date(start)) / 60000);
}
handlePolarCondition(sunData) {
if (sunData.polar_condition === "midnight_sun") {
return {
type: "polar",
condition: "midnight_sun",
message: "The sun does not set. All-day shooting possible.",
tips: [
"Sun stays low, creating extended golden-hour-like conditions",
"Best light when sun is lowest (local midnight)",
"Use ND filters for long exposures",
],
};
}
return {
type: "polar",
condition: "polar_night",
message: "The sun does not rise.",
tips: [
"Consider aurora photography",
"Blue hour twilight may occur around solar noon",
"Artificial lighting or long exposures required",
],
};
}
async getSunData(latitude, longitude, date) {
const key = `${latitude.toFixed(2)},${longitude.toFixed(2)}:${date}`;
if (!this.cache.has(key)) {
const response = await fetch(
`https://worlddataapi.com/v1/sun/${latitude},${longitude}?date=${date}`,
{ headers: { "X-API-Key": this.apiKey } },
);
this.cache.set(key, await response.json());
}
return this.cache.get(key);
}
}
Common Pitfalls#
Ignoring Timezone Handling#
Golden hour times are returned in the local timezone for the coordinates. If you display times to users in a different timezone, you must convert appropriately:
// Wrong: parsing without timezone awareness
const time = new Date(goldenHour.morning.start);
// Right: use the timezone from the API response
const options = { timeZone: data.timezone, hour: "numeric", minute: "2-digit" };
const localTime = new Date(goldenHour.morning.start).toLocaleTimeString(
"en-US",
options,
);
Not Handling Polar Regions#
Applications that don't check for polar_condition will break when users query locations like Reykjavik in summer or Murmansk in winter. Always check for null golden hour values.
Over-Caching#
While sun data is deterministic, caching too aggressively can cause issues:
Users traveling to new locations see stale data for their previous location
Date rollover at midnight requires fresh data
Cache by the combination of coordinates + date, and clear location-based caches when user location changes.
Assuming Fixed Golden Hour Duration#
Golden hour duration varies significantly. Near the equator, it may last only 20 minutes. In Scotland during summer, it can exceed an hour. Do not hardcode duration assumptions in your UI.
Ignoring API Errors#
Network failures happen. Always provide fallback behavior or error messages rather than showing broken UI:
try {
const goldenHour = await getGoldenHour(lat, lon, date);
displayTimes(goldenHour);
} catch (error) {
displayFallbackMessage(
"Unable to load golden hour times. Check your connection.",
);
}
Summary#
Golden hour calculation requires accounting for latitude, date, and edge cases that simple formulas cannot handle. The World Data API provides accurate golden hour times for any location, including:
Morning and evening golden hour start/end times
Blue hour times for twilight photography
Polar region handling (midnight sun, polar night)
Proper timezone-aware responses
Key takeaways:
Use the
/v1/sunendpoint with coordinates and date to get golden hour timesAlways check for
polar_conditionin the response to handle edge casesCache responses by coordinates + date for performance
Display times using the timezone returned by the API
Ready to add golden hour to your photography app? Get your free API key and start building today.
Adding Sunrise and Sunset Times to Your Application — Complete sun data guide
Building a Moon Phase Display — Night photography planning
How to Build a Timezone-Aware Application — Display times correctly