Displaying Sunset Times in Javascript using a Free Sunset Times API

Looking to display sunrise or sunset times in JavaScript? There are a few ways to do this, including the SunCalc library that calculates times client-side, but in this guide I’ll show you how to use the free SunriseSunset.io API.

Full disclosure: I built this API to simplify getting accurate sunrise and sunset times in apps and websites. It localizes times to the location you query, auto-adjusts for terrain elevation, and now returns moon and sun-position data in every response. Keyless by default, so there’s nothing to sign up for before your first request.

Using the SunriseSunset.io API

The base request needs just a latitude and longitude. Drop them into a URL and you get JSON back:

https://api.sunrisesunset.io/json?lat=38.907192&lng=-77.036873

No key, no signup. Times come back in the local timezone of the coordinates you pass, resolved from the lat/lng.

There is now an optional API key if you want higher limits or per-app usage tracking. You mint it from your account and send it as a header:

Authorization: Bearer sss_...

Send it as a header, never as a query-string parameter. Query strings get logged by every proxy and CDN between you and the origin, so a key in the URL is a key in somebody’s access log. Most apps don’t need one at all.

What you get back

Every response includes more than just sunrise and sunset. Here’s the shape of a typical result:

{
  "results": {
    "date": "2026-04-04",
    "sunrise": "6:48:29 AM",
    "sunset": "7:36:01 PM",
    "first_light": "5:16:33 AM",
    "last_light": "9:07:57 PM",
    "dawn": "6:21:29 AM",
    "dusk": "8:03:01 PM",
    "solar_noon": "1:12:15 PM",
    "golden_hour": "7:00:44 PM",
    "day_length": "12:47:32",
    "nautical_twilight_begin": "5:49:31 AM",
    "nautical_twilight_end": "8:34:59 PM",
    "timezone": "America/New_York",
    "utc_offset": -240,
    "sun_altitude": 56.81,
    "sun_azimuth": 180.33,
    "sunrise_azimuth": 82,
    "sunset_azimuth": 278.38,
    "moonrise": "9:38:28 PM",
    "moonset": "7:48:47 AM",
    "moon_illumination": 92.7,
    "moon_phase": "Waning Gibbous",
    "moon_phase_value": 0.59,
    "moon_always_up": false,
    "moon_always_down": false,
    "elevation": 26
  },
  "status": "OK",
  "tzid": "America/New_York"
}

If you only need sunrise and sunset, ignore the rest. If you’re building for photographers, hikers, climbers, or anyone who cares about golden hour, twilight, or moon phase, it’s already in the response.

Making sense of the twilight fields

There are three tiers of twilight in the response and the names don’t make the order obvious, so here it is from darkest to brightest:

  • first_light and last_light are astronomical twilight, the sun 18 degrees below the horizon. This is the faintest brightening of the sky and the boundary astrophotographers care about.
  • nautical_twilight_begin and nautical_twilight_end put the sun 12 degrees below the horizon. The horizon is visible at sea but you still can’t read outside.
  • dawn and dusk are civil twilight, 6 degrees below the horizon. Enough light to function outdoors without a torch. This is the one most apps actually want when they say “is it dark yet.”

A couple of other conventions worth knowing before you use the numbers. utc_offset is in minutes, not hours, so -240 means UTC-4. The azimuth fields are compass bearings with north at 0 degrees running clockwise, so sunset_azimuth: 301.86 means the sun set in the northwest. sun_altitude and sun_azimuth are measured at solar noon, while sunrise_azimuth and sunset_azimuth are measured at those events.

The times are accurate to the second, with atmospheric refraction and terrain elevation both factored in by default.

Basic JavaScript example

You can get coordinates for any location by right-clicking on Google Maps and copying the lat/lng.

Right-click on Google Maps to copy coordinates

Here is a basic fetch using the coordinates of the Gateway Arch in St. Louis:

// Coordinates for Gateway Arch in St. Louis
const lat = 38.624519738607894;
const lng = -90.18581515692256;
const url = `https://api.sunrisesunset.io/json?lat=${lat}&lng=${lng}`;

fetch(url)
  .then(res => res.json())
  .then(({ results, status }) => {
    if (status !== 'OK') {
      console.error('API error:', status);
      return;
    }
    console.log(`Sunrise: ${results.sunrise}`);
    console.log(`Sunset:  ${results.sunset}`);
  })
  .catch(err => console.error('Network error:', err));

A fuller example on CodePen renders the same data to the page.

Useful parameters

Date and date ranges

Pass date=YYYY-MM-DD, date=today, or date=tomorrow for a single date. To grab a range, use date_start and date_end. You can request up to 365 days of data in one call:

https://api.sunrisesunset.io/json?lat=38.62&lng=-90.19&date_start=2026-06-01&date_end=2026-06-07

One thing to watch: when you use a date range, results comes back as an array of daily objects instead of a single object. Code written against the single-date response will break the moment someone passes a range, so branch on it or normalize with [].concat(results).

Time format

Pass time_format=24 for 24-hour times, time_format=military, or time_format=unix for UTC timestamps. The default is 12-hour with AM/PM.

ISO 8601 output

If you want machine-parseable timestamps instead of strings like "6:48:29 AM", pass formatted=0. The response switches to ISO 8601, and day_length comes back as seconds.

Elevation

Sunrise and sunset are automatically adjusted for the terrain elevation at the requested coordinates. Higher elevations give an earlier sunrise and a later sunset, which matters more than people expect for mountain or coastal locations. The terrain data comes from ETOPO1. Pass elevation=false to compute at sea level instead, or pass a value in meters (like elevation=1609) to override what it detected. The elevation actually used comes back in the response, so you can check what it picked.

Timezone

Times return in the location’s local timezone by default, resolved from the coordinates. Pass timezone=Europe/Paris (any IANA tz name) to force a different zone. If the value doesn’t validate, the API falls back to the location’s own timezone and still returns data rather than failing the request, so a typo here gets you correct times in the wrong zone rather than an error. Check tzid in the response if it matters.

JSONP

If you need to call the API from a browser environment that requires JSONP, pass callback=yourFunctionName and the response will be wrapped accordingly.

Status codes

The API returns a specific status field you can branch on:

  • OK for a successful request
  • INVALID_REQUEST for malformed parameters
  • INVALID_DATE for an unparseable date, or a range longer than one year
  • INVALID_TZID for a timezone that couldn’t be validated. Worth flagging: this one still returns data on a 200, using the location’s own timezone, so treating it as a hard failure throws away a perfectly good response
  • UNKNOWN_ERROR if something fails server-side

The HTTP status follows the field: 200 for OK and INVALID_TZID, 400 for bad input, 500 for something failing server-side.

One edge case worth knowing: at extreme latitudes near the poles, sunrise or sunset may not exist on a given day. In those cases the corresponding field returns null rather than a time string. Check for it if your app might be queried for arctic or antarctic coordinates. The moon has the same problem, which is what moon_always_up and moon_always_down are for: when either is true, the corresponding moonrise or moonset is null because the event never happens that day.

What to build with it

A few patterns the API gets used for:

  • Photography apps showing golden hour, blue hour, and twilight for a chosen location
  • Outdoor reservation sites that need to know whether a booking is in daylight
  • Local business pages displaying sunset times to give visitors context
  • Smart home dashboards triggering events around solar noon or civil twilight
  • Travel sites comparing day length and sunset azimuth across destinations
  • Astronomy apps using moon phase and illumination data for planning

A few less obvious uses I’ve seen people build:

  • Astrophotography planning. Use the nautical twilight times and moon_illumination to find true dark windows when the moon isn’t washing out the sky.
  • Wedding ceremony timing. Pair golden_hour with sunset_azimuth to recommend a ceremony time and orient the altar so the sunset frames the couple.
  • Drone shows and fireworks. Trigger the first launch once last_light passes a threshold, so the sky is actually dark enough for the show to land.
  • Solar panel installations. Use sun_altitude and sun_azimuth at solar noon, combined with the auto-detected elevation, to recommend roof tilt and orientation for a given address.
  • Hiking trail safety. Compare last_light against the hiker’s estimated return time and warn before they commit to a leg they can’t finish in daylight.
  • Trick-or-treat timers. Ping parents when civil twilight starts on October 31st in their location so they know it’s officially dark enough.
  • Religious observance apps. Compute Islamic prayer times (Fajr at first light, Maghrib at sunset, Isha at last light) or Sabbath start times from the sunset and twilight fields.
  • Real estate listings. Show a property’s day_length and golden_hour window as part of the listing so buyers know exactly how much natural light to expect.

It’s free for personal and commercial use. The only requirement is a visible link back from any page or app that displays the data:

Powered by <a href="https://sunrisesunset.io">SunriseSunset.io</a>

There are no enforced rate limits. That is not an invitation to hammer it: if you’re calling once an hour for every user, cache on your end. Sunrise and sunset for a fixed location only change once a day, so a daily cache key covers almost every use case. The API runs on Cloudflare Workers across 300+ cities with P99 response times under 50 ms, and there’s a status page if you want to watch it.

Leave a Comment