Skip to main content

Sunrise, Sunset & Solar Position API

REAL DATA

Sunrise, sunset, solar noon, day length, the three twilights and solar elevation/azimuth for any coordinate and date, from the NOAA equations.

Authoritative reference data or standards computation

Base URL
/api/sun
Category
Date & Time
Capabilities
3 routes
Last updated
July 30, 2026
Data source

GET /api/sun/times

date is a local civil date in tz; omit it for today in that zone. All events are anchored to the solar noon of that local date, so sunrise always precedes solar noon and solar noon always precedes sunset.

Live requestRuns against the public API
No key required
GET/api/sun/times?lat=55.6761&lng=12.5683&date=2026-06-21&tz=Europe%2FCopenhagen

Request parameters

Observer latitude in WGS 84 decimal degrees, positive north (−90 to 90).

Observer longitude in WGS 84 decimal degrees, positive east (−180 to 180).

Local civil date (YYYY-MM-DD) in the resolved zone, years 1900–2100. Defaults to today in that zone. An RFC 3339 timestamp is accepted too and is reduced to the local civil date that instant falls on in `tz` — except at exactly 00:00 UTC, which is indistinguishable from the bare date and is read as that calendar date.

IANA zone used to resolve the local civil date and to render every `local` wall clock, case-insensitive. Unknown ids return a 400 — browse them at /api/timezones.

Advanced response options4 options

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

Return all fields except these (comma-separated).

Pretty-print the JSON response.

Drop the envelope: return the raw array/object without data/meta wrapper.

Response

Example parameters are ready. Send the request to inspect the live response.

Route reference

Code samples Ready-to-copy requests in 4 languages
Choose a code sample language
curl "https://randomapi.dev/api/sun/times?lat=55.6761&lng=12.5683&date=2026-06-21&tz=Europe/Copenhagen"
const res = await fetch("https://randomapi.dev/api/sun/times?lat=55.6761&lng=12.5683&date=2026-06-21&tz=Europe/Copenhagen");
const { data, meta } = await res.json();
import requests

data = requests.get("https://randomapi.dev/api/sun/times?lat=55.6761&lng=12.5683&date=2026-06-21&tz=Europe/Copenhagen").json()["data"]
$json = json_decode(file_get_contents(
  "https://randomapi.dev/api/sun/times?lat=55.6761&lng=12.5683&date=2026-06-21&tz=Europe/Copenhagen"
), true);
$data = $json["data"];
Parameters Route-specific request inputs 4
lat float required

Observer latitude in WGS 84 decimal degrees, positive north (−90 to 90).

allowed: -90 – 90
example: lat=55.6761
lng float required

Observer longitude in WGS 84 decimal degrees, positive east (−180 to 180).

allowed: -180 – 180
example: lng=12.5683
date date

Local civil date (YYYY-MM-DD) in the resolved zone, years 1900–2100. Defaults to today in that zone. An RFC 3339 timestamp is accepted too and is reduced to the local civil date that instant falls on in `tz` — except at exactly 00:00 UTC, which is indistinguishable from the bare date and is read as that calendar date.

example: date=2026-06-21
tz string

IANA zone used to resolve the local civil date and to render every `local` wall clock, case-insensitive. Unknown ids return a 400 — browse them at /api/timezones.

default: UTC
example: tz=Europe/Copenhagen
Universal parameters Shared response and formatting options 4
fields list

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

example: fields=date,timezone
exclude list

Return all fields except these (comma-separated).

example: exclude=astronomicalDusk
pretty boolean

Pretty-print the JSON response.

default: false
example: pretty=true
unwrap boolean

Drop the envelope: return the raw array/object without data/meta wrapper.

default: false
example: unwrap=true
Response schema Fields returned in each record 14
date string (YYYY-MM-DD)

The local civil date the results describe, in the resolved zone. On the rare date that contains no solar transit at all — where local solar noon sits within seconds of local midnight — the events are anchored to the nearest transit and `meta.warnings` names the date it falls on.

example: 2026-06-21

timezone string

Canonical IANA id applied to the local date and every `local` wall clock.

example: Europe/Copenhagen

latitude float

Observer latitude in decimal degrees, echoed as supplied.

example: 55.6761

longitude float

Observer longitude in decimal degrees, echoed as supplied.

example: 12.5683

solarNoon object

Meridian transit — the Sun's highest point of the local day. Always present, so `reason` is always null here. Object with `instant` (UTC ISO 8601, whole seconds), `local` (naive wall clock in `tz`) and `reason`. On a normal day `reason` is null; when the crossing does not happen it is `polar-day` (the Sun stays above this altitude all day) or `polar-night` (it stays below), and both times are null.

example: {"instant":"2026-06-21T11:11:33.000Z","local":"2026-06-21T13:11:33","reason":null}

sunrise object

Sun's centre crossing the −0.833° horizon while rising. Object with `instant` (UTC ISO 8601, whole seconds), `local` (naive wall clock in `tz`) and `reason`. On a normal day `reason` is null; when the crossing does not happen it is `polar-day` (the Sun stays above this altitude all day) or `polar-night` (it stays below), and both times are null.

example: {"instant":"2026-06-21T02:25:20.000Z","local":"2026-06-21T04:25:20","reason":null}

sunset object

Sun's centre crossing the −0.833° horizon while setting. Object with `instant` (UTC ISO 8601, whole seconds), `local` (naive wall clock in `tz`) and `reason`. On a normal day `reason` is null; when the crossing does not happen it is `polar-day` (the Sun stays above this altitude all day) or `polar-night` (it stays below), and both times are null.

example: {"instant":"2026-06-21T19:57:45.000Z","local":"2026-06-21T21:57:45","reason":null}

dayLengthSeconds integer (0-86400)

Seconds the Sun spends above the −0.833° horizon on this solar day: exactly the gap between the returned sunrise and sunset instants when both exist, 86400 through a polar day and 0 through a polar night. On the changeover day, where only one of the two crossings happens, the measured half is added to the full 12 hours on the side the Sun never sets or never rises.

example: 63145

civilDawn object

Sun's centre reaching 6° below the horizon while rising. Object with `instant` (UTC ISO 8601, whole seconds), `local` (naive wall clock in `tz`) and `reason`. On a normal day `reason` is null; when the crossing does not happen it is `polar-day` (the Sun stays above this altitude all day) or `polar-night` (it stays below), and both times are null.

example: {"instant":"2026-06-21T01:24:09.000Z","local":"2026-06-21T03:24:09","reason":null}

civilDusk object

Sun's centre reaching 6° below the horizon while setting. Object with `instant` (UTC ISO 8601, whole seconds), `local` (naive wall clock in `tz`) and `reason`. On a normal day `reason` is null; when the crossing does not happen it is `polar-day` (the Sun stays above this altitude all day) or `polar-night` (it stays below), and both times are null.

example: {"instant":"2026-06-21T20:58:55.000Z","local":"2026-06-21T22:58:55","reason":null}

nauticalDawn object

Sun's centre reaching 12° below the horizon while rising. Object with `instant` (UTC ISO 8601, whole seconds), `local` (naive wall clock in `tz`) and `reason`. On a normal day `reason` is null; when the crossing does not happen it is `polar-day` (the Sun stays above this altitude all day) or `polar-night` (it stays below), and both times are null.

example: {"instant":null,"local":null,"reason":"polar-day"}

nauticalDusk object

Sun's centre reaching 12° below the horizon while setting. Object with `instant` (UTC ISO 8601, whole seconds), `local` (naive wall clock in `tz`) and `reason`. On a normal day `reason` is null; when the crossing does not happen it is `polar-day` (the Sun stays above this altitude all day) or `polar-night` (it stays below), and both times are null.

example: {"instant":null,"local":null,"reason":"polar-day"}

astronomicalDawn object

Sun's centre reaching 18° below the horizon while rising. Object with `instant` (UTC ISO 8601, whole seconds), `local` (naive wall clock in `tz`) and `reason`. On a normal day `reason` is null; when the crossing does not happen it is `polar-day` (the Sun stays above this altitude all day) or `polar-night` (it stays below), and both times are null.

example: {"instant":null,"local":null,"reason":"polar-day"}

astronomicalDusk object

Sun's centre reaching 18° below the horizon while setting. Object with `instant` (UTC ISO 8601, whole seconds), `local` (naive wall clock in `tz`) and `reason`. On a normal day `reason` is null; when the crossing does not happen it is `polar-day` (the Sun stays above this altitude all day) or `polar-night` (it stays below), and both times are null.

example: {"instant":null,"local":null,"reason":"polar-day"}

Documented examples

Build-generated requests and complete responses
4
Copenhagen at midsummer
GET /api/sun/times?lat=55.6761&lng=12.5683&date=2026-06-21&tz=Europe/Copenhagen
{
  "data": {
    "date": "2026-06-21",
    "timezone": "Europe/Copenhagen",
    "latitude": 55.6761,
    "longitude": 12.5683,
    "solarNoon": {
      "instant": "2026-06-21T11:11:33.000Z",
      "local": "2026-06-21T13:11:33",
      "reason": null
    },
    "sunrise": {
      "instant": "2026-06-21T02:25:20.000Z",
      "local": "2026-06-21T04:25:20",
      "reason": null
    },
    "sunset": {
      "instant": "2026-06-21T19:57:45.000Z",
      "local": "2026-06-21T21:57:45",
      "reason": null
    },
    "dayLengthSeconds": 63145,
    "civilDawn": {
      "instant": "2026-06-21T01:24:09.000Z",
      "local": "2026-06-21T03:24:09",
      "reason": null
    },
    "civilDusk": {
      "instant": "2026-06-21T20:58:55.000Z",
      "local": "2026-06-21T22:58:55",
      "reason": null
    },
    "nauticalDawn": {
      "instant": null,
      "local": null,
      "reason": "polar-day"
    },
    "nauticalDusk": {
      "instant": null,
      "local": null,
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Quito on the March equinox
GET /api/sun/times?lat=-0.1807&lng=-78.4678&date=2026-03-20&tz=America/Guayaquil
{
  "data": {
    "date": "2026-03-20",
    "timezone": "America/Guayaquil",
    "latitude": -0.1807,
    "longitude": -78.4678,
    "solarNoon": {
      "instant": "2026-03-20T17:21:14.000Z",
      "local": "2026-03-20T12:21:14",
      "reason": null
    },
    "sunrise": {
      "instant": "2026-03-20T11:17:59.000Z",
      "local": "2026-03-20T06:17:59",
      "reason": null
    },
    "sunset": {
      "instant": "2026-03-20T23:24:30.000Z",
      "local": "2026-03-20T18:24:30",
      "reason": null
    },
    "dayLengthSeconds": 43591,
    "civilDawn": {
      "instant": "2026-03-20T10:57:19.000Z",
      "local": "2026-03-20T05:57:19",
      "reason": null
    },
    "civilDusk": {
      "instant": "2026-03-20T23:45:09.000Z",
      "local": "2026-03-20T18:45:09",
      "reason": null
    },
    "nauticalDawn": {
      "instant": "2026-03-20T10:33:19.000Z",
      "local": "2026-03-20T05:33:19",
      "reason": null
    },
    "nauticalDusk": {
      "instant": "2026-03-21T00:09:09.000Z",
      "local": "2026-03-20T19:09:09",
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Tromsø in the polar night
GET /api/sun/times?lat=69.6492&lng=18.9553&date=2026-12-21&tz=Europe/Oslo
{
  "data": {
    "date": "2026-12-21",
    "timezone": "Europe/Oslo",
    "latitude": 69.6492,
    "longitude": 18.9553,
    "solarNoon": {
      "instant": "2026-12-21T10:42:14.000Z",
      "local": "2026-12-21T11:42:14",
      "reason": null
    },
    "sunrise": {
      "instant": null,
      "local": null,
      "reason": "polar-night"
    },
    "sunset": {
      "instant": null,
      "local": null,
      "reason": "polar-night"
    },
    "dayLengthSeconds": 0,
    "civilDawn": {
      "instant": "2026-12-21T08:31:13.000Z",
      "local": "2026-12-21T09:31:13",
      "reason": null
    },
    "civilDusk": {
      "instant": "2026-12-21T12:53:13.000Z",
      "local": "2026-12-21T13:53:13",
      "reason": null
    },
    "nauticalDawn": {
      "instant": "2026-12-21T06:46:42.000Z",
      "local": "2026-12-21T07:46:42",
      "reason": null
    },
    "nauticalDusk": {
      "instant": "2026-12-21T14:37:45.000Z",
      "local": "2026-12-21T15:37:45",
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Today in Tokyo
GET /api/sun/times?lat=35.6762&lng=139.6503&tz=Asia/Tokyo
{
  "data": {
    "date": "2026-07-31",
    "timezone": "Asia/Tokyo",
    "latitude": 35.6762,
    "longitude": 139.6503,
    "solarNoon": {
      "instant": "2026-07-31T02:47:50.000Z",
      "local": "2026-07-31T11:47:50",
      "reason": null
    },
    "sunrise": {
      "instant": "2026-07-30T19:48:17.000Z",
      "local": "2026-07-31T04:48:17",
      "reason": null
    },
    "sunset": {
      "instant": "2026-07-31T09:46:55.000Z",
      "local": "2026-07-31T18:46:55",
      "reason": null
    },
    "dayLengthSeconds": 50318,
    "civilDawn": {
      "instant": "2026-07-30T19:20:03.000Z",
      "local": "2026-07-31T04:20:03",
      "reason": null
    },
    "civilDusk": {
      "instant": "2026-07-31T10:15:04.000Z",
      "local": "2026-07-31T19:15:04",
      "reason": null
    },
    "nauticalDawn": {
      "instant": "2026-07-30T18:45:44.000Z",
      "local": "2026-07-31T03:45:44",
      "reason": null
    },
    "nauticalDusk": {
      "instant": "2026-07-31T10:49:16.000Z",
      "local": "2026-07-31T19:49:16",
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}

GET /api/sun/position

at is an absolute instant (date-only means UTC midnight); omit it for now. Elevation is geometric — no refraction correction is applied to it.

Live requestRuns against the public API
No key required
GET/api/sun/position?lat=55.6761&lng=12.5683&at=2026-06-21T12%3A00%3A00Z&tz=Europe%2FCopenhagen

Request parameters

Observer latitude in WGS 84 decimal degrees, positive north (−90 to 90).

Observer longitude in WGS 84 decimal degrees, positive east (−180 to 180).

Absolute instant: YYYY-MM-DD (UTC midnight) or RFC 3339 with Z or an explicit offset, years 1900–2100. Defaults to now.

IANA zone used only to render `localTime`, case-insensitive. It does not move the instant; unknown ids return a 400.

Advanced response options4 options

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

Return all fields except these (comma-separated).

Pretty-print the JSON response.

Drop the envelope: return the raw array/object without data/meta wrapper.

Response

Example parameters are ready. Send the request to inspect the live response.

Route reference

Code samples Ready-to-copy requests in 4 languages
Choose a code sample language
curl "https://randomapi.dev/api/sun/position?lat=55.6761&lng=12.5683&at=2026-06-21T12:00:00Z&tz=Europe/Copenhagen"
const res = await fetch("https://randomapi.dev/api/sun/position?lat=55.6761&lng=12.5683&at=2026-06-21T12:00:00Z&tz=Europe/Copenhagen");
const { data, meta } = await res.json();
import requests

data = requests.get("https://randomapi.dev/api/sun/position?lat=55.6761&lng=12.5683&at=2026-06-21T12:00:00Z&tz=Europe/Copenhagen").json()["data"]
$json = json_decode(file_get_contents(
  "https://randomapi.dev/api/sun/position?lat=55.6761&lng=12.5683&at=2026-06-21T12:00:00Z&tz=Europe/Copenhagen"
), true);
$data = $json["data"];
Parameters Route-specific request inputs 4
lat float required

Observer latitude in WGS 84 decimal degrees, positive north (−90 to 90).

allowed: -90 – 90
example: lat=55.6761
lng float required

Observer longitude in WGS 84 decimal degrees, positive east (−180 to 180).

allowed: -180 – 180
example: lng=12.5683
at date

Absolute instant: YYYY-MM-DD (UTC midnight) or RFC 3339 with Z or an explicit offset, years 1900–2100. Defaults to now.

example: at=2026-06-21T12:00:00Z
tz string

IANA zone used only to render `localTime`, case-insensitive. It does not move the instant; unknown ids return a 400.

default: UTC
example: tz=Europe/Copenhagen
Universal parameters Shared response and formatting options 4
fields list

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

example: fields=at,timezone
exclude list

Return all fields except these (comma-separated).

example: exclude=isDaylight
pretty boolean

Pretty-print the JSON response.

default: false
example: pretty=true
unwrap boolean

Drop the envelope: return the raw array/object without data/meta wrapper.

default: false
example: unwrap=true
Response schema Fields returned in each record 9
at string (ISO 8601 UTC)

The evaluated instant in UTC.

example: 2026-06-21T12:00:00.000Z

timezone string

Canonical IANA id used to render localTime.

example: Europe/Copenhagen

localTime string (YYYY-MM-DDTHH:mm:ss)

Naive local wall clock of the same instant in `timezone`.

example: 2026-06-21T14:00:00

elevationDegrees float (-90 to 90)

Geometric elevation of the Sun's centre above the true horizon. No atmospheric refraction correction is applied, so near the horizon the apparent Sun sits about 0.57° higher than this figure.

example: 56.5453

azimuthDegrees float (0 to 360)

Compass bearing of the Sun, 0° = north increasing clockwise. Within ~0.06° of the zenith or of a pole the bearing is geometrically undefined and NOAA's meridian convention is reported instead: 180 for an observer north of the equator, 0 otherwise.

example: 200.4393

declinationDegrees float (-23.5 to 23.5)

Apparent solar declination at the instant.

example: 23.4381

equationOfTimeMinutes float

Apparent solar time minus mean solar time, in minutes (roughly −14 to +16 over a year).

example: -1.8243

hourAngleDegrees float (-180 to 180)

Local hour angle of the Sun: 0 at solar noon, negative before it, positive after, 15° per hour.

example: 12.1122

isDaylight boolean

True when elevationDegrees is above the −0.833° horizon that /times uses for sunrise and sunset, so the two routes agree.

example: true

Documented examples

Build-generated requests and complete responses
3
Sun over Copenhagen at noon UTC
GET /api/sun/position?lat=55.6761&lng=12.5683&at=2026-06-21T12:00:00Z&tz=Europe/Copenhagen
{
  "data": {
    "at": "2026-06-21T12:00:00.000Z",
    "timezone": "Europe/Copenhagen",
    "localTime": "2026-06-21T14:00:00",
    "elevationDegrees": 56.5453,
    "azimuthDegrees": 200.4393,
    "declinationDegrees": 23.4381,
    "equationOfTimeMinutes": -1.8243,
    "hourAngleDegrees": 12.1122,
    "isDaylight": true
  },
  "meta": {
    "endpoint": "sun",
    "route": "/position",
    "params": {
      "lat": 55.6761,
      "lng": 12.5683,
      "at": "2026-06-21T12:00:00.000Z",
      "tz": "Europe/Copenhagen"
    },
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Midnight sun above Tromsø
GET /api/sun/position?lat=69.6492&lng=18.9553&at=2026-06-21T22:45:00Z&tz=Europe/Oslo
{
  "data": {
    "at": "2026-06-21T22:45:00.000Z",
    "timezone": "Europe/Oslo",
    "localTime": "2026-06-22T00:45:00",
    "elevationDegrees": 3.0863,
    "azimuthDegrees": 359.7472,
    "declinationDegrees": 23.4369,
    "equationOfTimeMinutes": -1.9216,
    "hourAngleDegrees": 179.7249,
    "isDaylight": true
  },
  "meta": {
    "endpoint": "sun",
    "route": "/position",
    "params": {
      "lat": 69.6492,
      "lng": 18.9553,
      "at": "2026-06-21T22:45:00.000Z",
      "tz": "Europe/Oslo"
    },
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
The Sun right now over Sydney
GET /api/sun/position?lat=-33.8688&lng=151.2093&tz=Australia/Sydney
{
  "data": {
    "at": "2026-07-30T15:14:08.000Z",
    "timezone": "Australia/Sydney",
    "localTime": "2026-07-31T01:14:08",
    "elevationDegrees": -67.6183,
    "azimuthDegrees": 129.1672,
    "declinationDegrees": 18.3986,
    "equationOfTimeMinutes": -6.4635,
    "hourAngleDegrees": -161.8733,
    "isDaylight": false
  },
  "meta": {
    "endpoint": "sun",
    "route": "/position",
    "params": {
      "lat": -33.8688,
      "lng": 151.2093,
      "tz": "Australia/Sydney",
      "at": "2026-07-30T15:14:08.000Z"
    },
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}

GET /api/sun/seasons

Meeus chapter 27 (mean expressions plus the 24 periodic corrections of table 27.C). The Terrestrial-Time result is converted to UTC with the Espenak & Meeus ΔT polynomials, and the ΔT applied is returned per event.

Live requestRuns against the public API
No key required
GET/api/sun/seasons?year=2026

Request parameters

Calendar year (1900–2100). Defaults to the current year in the resolved zone.

IANA zone used to render each `local` wall clock and to resolve the default year, case-insensitive. Unknown ids return a 400.

Advanced response options4 options

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

Return all fields except these (comma-separated).

Pretty-print the JSON response.

Drop the envelope: return the raw array/object without data/meta wrapper.

Response

Example parameters are ready. Send the request to inspect the live response.

Route reference

Code samples Ready-to-copy requests in 4 languages
Choose a code sample language
curl "https://randomapi.dev/api/sun/seasons?year=2026"
const res = await fetch("https://randomapi.dev/api/sun/seasons?year=2026");
const { data, meta } = await res.json();
import requests

data = requests.get("https://randomapi.dev/api/sun/seasons?year=2026").json()["data"]
$json = json_decode(file_get_contents(
  "https://randomapi.dev/api/sun/seasons?year=2026"
), true);
$data = $json["data"];
Parameters Route-specific request inputs 2
year int

Calendar year (1900–2100). Defaults to the current year in the resolved zone.

allowed: 1900 – 2100
example: year=2026
tz string

IANA zone used to render each `local` wall clock and to resolve the default year, case-insensitive. Unknown ids return a 400.

default: UTC
example: tz=Europe/Copenhagen
Universal parameters Shared response and formatting options 4
fields list

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

example: fields=year,timezone
exclude list

Return all fields except these (comma-separated).

example: exclude=decemberSolstice
pretty boolean

Pretty-print the JSON response.

default: false
example: pretty=true
unwrap boolean

Drop the envelope: return the raw array/object without data/meta wrapper.

default: false
example: unwrap=true
Response schema Fields returned in each record 6
year integer

Calendar year the four instants belong to.

example: 2026

timezone string

Canonical IANA id used to render every `local` wall clock.

example: UTC

marchEquinox object

March (northward) equinox: `instant` in UTC, `local` wall clock in `tz`, and `deltaTSeconds`, the TT−UT offset applied.

example: {"instant":"2026-03-20T14:45:30.000Z","local":"2026-03-20T14:45:30","deltaTSeconds":75.2}

juneSolstice object

June (northern summer) solstice, in the same shape as marchEquinox.

example: {"instant":"2026-06-21T08:24:49.000Z","local":"2026-06-21T08:24:49","deltaTSeconds":75.4}

septemberEquinox object

September (southward) equinox, in the same shape as marchEquinox.

example: {"instant":"2026-09-23T00:05:24.000Z","local":"2026-09-23T00:05:24","deltaTSeconds":75.5}

decemberSolstice object

December (northern winter) solstice, in the same shape as marchEquinox.

example: {"instant":"2026-12-21T20:50:07.000Z","local":"2026-12-21T20:50:07","deltaTSeconds":75.7}

Documented examples

Build-generated requests and complete responses
3
2026 equinoxes and solstices in UTC
GET /api/sun/seasons?year=2026
{
  "data": {
    "year": 2026,
    "timezone": "UTC",
    "marchEquinox": {
      "instant": "2026-03-20T14:45:30.000Z",
      "local": "2026-03-20T14:45:30",
      "deltaTSeconds": 75.2
    },
    "juneSolstice": {
      "instant": "2026-06-21T08:24:49.000Z",
      "local": "2026-06-21T08:24:49",
      "deltaTSeconds": 75.4
    },
    "septemberEquinox": {
      "instant": "2026-09-23T00:05:24.000Z",
      "local": "2026-09-23T00:05:24",
      "deltaTSeconds": 75.5
    },
    "decemberSolstice": {
      "instant": "2026-12-21T20:50:07.000Z",
      "local": "2026-12-21T20:50:07",
      "deltaTSeconds": 75.7
    }
  },
  "meta": {
    "endpoint": "sun",
    "route": "/seasons",
    "params": {
      "year": 2026,
      "tz": "UTC"
    },
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
2026 seasons on the Copenhagen clock
GET /api/sun/seasons?year=2026&tz=Europe/Copenhagen
{
  "data": {
    "year": 2026,
    "timezone": "Europe/Copenhagen",
    "marchEquinox": {
      "instant": "2026-03-20T14:45:30.000Z",
      "local": "2026-03-20T15:45:30",
      "deltaTSeconds": 75.2
    },
    "juneSolstice": {
      "instant": "2026-06-21T08:24:49.000Z",
      "local": "2026-06-21T10:24:49",
      "deltaTSeconds": 75.4
    },
    "septemberEquinox": {
      "instant": "2026-09-23T00:05:24.000Z",
      "local": "2026-09-23T02:05:24",
      "deltaTSeconds": 75.5
    },
    "decemberSolstice": {
      "instant": "2026-12-21T20:50:07.000Z",
      "local": "2026-12-21T21:50:07",
      "deltaTSeconds": 75.7
    }
  },
  "meta": {
    "endpoint": "sun",
    "route": "/seasons",
    "params": {
      "year": 2026,
      "tz": "Europe/Copenhagen"
    },
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
This year's seasons
GET /api/sun/seasons
{
  "data": {
    "year": 2026,
    "timezone": "UTC",
    "marchEquinox": {
      "instant": "2026-03-20T14:45:30.000Z",
      "local": "2026-03-20T14:45:30",
      "deltaTSeconds": 75.2
    },
    "juneSolstice": {
      "instant": "2026-06-21T08:24:49.000Z",
      "local": "2026-06-21T08:24:49",
      "deltaTSeconds": 75.4
    },
    "septemberEquinox": {
      "instant": "2026-09-23T00:05:24.000Z",
      "local": "2026-09-23T00:05:24",
      "deltaTSeconds": 75.5
    },
    "decemberSolstice": {
      "instant": "2026-12-21T20:50:07.000Z",
      "local": "2026-12-21T20:50:07",
      "deltaTSeconds": 75.7
    }
  },
  "meta": {
    "endpoint": "sun",
    "route": "/seasons",
    "params": {
      "tz": "UTC",
      "year": 2026
    },
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}

About this API

Coverage & behavior

Sunrise, sunset, twilight and solar-position values computed from the NOAA Global Monitoring Laboratory solar equations (after Jean Meeus, Astronomical Algorithms, 2nd ed.). It is pure trigonometry evaluated per request — no ephemeris file to download, no upstream service that can go down, no key.

  • /api/sun/times — sunrise, sunset, solar noon, day length and the civil, nautical and astronomical twilights for one coordinate on one local civil date. Every event is an object carrying the UTC instant, the local wall clock in the requested zone, and a reason.
  • /api/sun/position — geometric elevation, azimuth (0° = north, increasing clockwise), declination, equation of time and hour angle at one instant.
  • /api/sun/seasons — the March and September equinox and the June and December solstice instants for a year.

Polar day and polar night are answered honestly. When the Sun never climbs to an event's altitude the times are null and reason is polar-night; when it never drops to that altitude reason is polar-day. A placeholder timestamp is never substituted. The same mechanism covers mid-latitude twilight gaps: Copenhagen has no nautical twilight for weeks around midsummer, so nauticalDawn/nauticalDusk come back null with reason: "polar-day" even though 55°N is nowhere near the Arctic.

/times is anchored to the solar noon that falls on the requested local date. Whenever sunrise and sunset exist, sunrise < solarNoon < sunset therefore always holds, and dayLengthSeconds is exactly the gap between the two returned instants. At high latitudes that means a dusk can carry the next local calendar date — which the local timestamp shows explicitly rather than silently clamping.

On the single day a polar day or polar night begins or ends, only one of the two crossings happens: a real sunrise with sunset: polar-day, for instance. dayLengthSeconds then counts the half-day the crossing bounds plus the full 12 hours on the side where the Sun never leaves the sky, so Tromsø's curve runs 83 508 → 85 759 → 86 400 s rather than dropping to zero for a day. The endpoint says which half was measured and which was assumed by the reason on each event.

Consecutive meridian transits are 24 hours ± ~20 seconds apart, never exactly 24 hours. At a longitude/zone pairing that puts solar noon within seconds of local midnight — near the antimeridian in UTC, for example — the drift leaves a couple of local civil dates a year containing no transit at all, and a neighbour containing two — a sweep of every date of a year found two such dates at the antimeridian in UTC and two more at longitude 0 on the Auckland clock, and none at all in Copenhagen. A date with no transit is anchored to the nearest one and meta.warnings names the transit date actually used; nothing is substituted silently.

Accuracy, and what is deliberately not modelled

NOAA states its calculator is "theoretically accurate to within a minute for locations between +/- 72° latitude, and within 10 minutes outside of those latitudes". Plan against that bound. The reference days in this endpoint's regression suite — Copenhagen, Quito, New York, Sydney, Reykjavík, Kiritimati, a point on the equator and two daylight-saving change days — reproduce the U.S. Naval Observatory's published rise, transit, set and civil-twilight times to within a minute, and solar declination and elevation track the USNO's own figures to about 0.01° (azimuth is intrinsically less stable when the Sun is near the zenith).

That ±1 minute assumes a reasonably steep horizon crossing. Within a few weeks of a polar-day or polar-night boundary the Sun grazes the horizon instead, and the same equations drift further: at Tromsø (69.6°N) in mid-May this endpoint lands about two minutes off the USNO's published sunset. Treat high-latitude answers near the transition dates as approximate.

The one or two days a year on which a polar day or polar night actually begins or ends are weaker still, and in a way worth spelling out. NOAA solves for a rise and for a set independently, each from a first guess at 0h UT, and each pass re-reads the Sun's declination at wherever the previous pass landed. When the Sun clears the horizon by only a few hundredths of a degree, the declination half a day either side of the crossing is enough to move it to the other side of the threshold, so the search can conclude that the crossing never happens at all — and then polar-day or polar-night is returned for a day that really does have a sunrise, or the reverse.

Measured, not estimated: 27 450 requests across fifteen polar latitudes, five longitudes and a full year of dates were checked against a brute-force scan of this endpoint's own /position output. 116 of them — 0.4%, every single one a changeover day — landed on the wrong side. The largest error inside the ±72° band NOAA's ±1 minute claim covers was 68°N on 2026-01-03, reported as polar night when the Sun was in fact up for 43 minutes; the largest anywhere was 85°N on 2026-03-05, off by 80 minutes. Days that are not a changeover are unaffected. If the changeover dates themselves have to be right, take them from a national almanac office.

Where the two searches contradict each other the answer is repaired rather than published: a real sunrise cannot sit beside a sunset that claims the Sun never reached the horizon at all, so that set is re-solved starting from the sunrise, which is by construction inside the arc. A real sunrise beside sunset: polar-day is not a contradiction — the Sun rose and then stayed up — and is published as-is, which is what the USNO also lists for Tromsø on 2026-05-18.

Not modelled: observer elevation above the terrain, local atmospheric pressure and temperature, and the real horizon profile. Rise and set use NOAA's fixed −0.833° horizon (34′ of mean refraction plus the Sun's 16′ semi-diameter); /position reports geometric elevation with no refraction correction at all. These values are not suitable for navigational, legal or safety-critical use — use a national almanac office for that.

"Golden hour" and "blue hour" are photographic conventions with no standards body behind them, so they are deliberately absent. The three twilights returned here are the standard definitions: civil −6°, nautical −12°, astronomical −18°.

/seasons uses Meeus chapter 27 (the mean expressions of table 27.B plus the 24 periodic corrections of table 27.C), which yields Terrestrial Time. The result is converted to UTC with the Espenak & Meeus ΔT polynomials published by NASA GSFC, and the ΔT actually applied is returned per event as deltaTSeconds so the conversion is auditable. Meeus gives the method an error under a minute for 1951–2050; measured against the USNO table it holds to about a minute from 1900 through 2050 and widens to about two minutes near 2100, where ΔT itself is an extrapolation.

Every route is limited to years 1900–2100; anything outside returns a 400 rather than an unfitted extrapolation.

Use it for

  • Trigger outdoor camera captures, irrigation or lighting between real sunrise and sunset at a site
  • Switch an app to dark mode at the user's actual local dusk instead of a fixed hour
  • Plan photography around civil twilight without bundling an ephemeris library
  • Feed solar elevation into shading, HVAC or PV-yield estimates for a timestamp
  • Chart day length across a year to explain seasonal demand patterns

Frequently asked questions

How do I get sunrise and sunset times from an API without a key?

Call /api/sun/times?lat=55.6761&lng=12.5683&tz=Europe/Copenhagen. It returns sunrise, sunset, solar noon and the three twilights — each with a UTC instant and a local wall clock — plus dayLengthSeconds. No key, no signup, no quota form.

What does the API return during polar day or polar night?

null for the times, plus a reason of polar-day or polar-night on the same event object, and it never returns a placeholder timestamp. The same applies to twilights that do not occur — Copenhagen's nautical twilight around midsummer, for example. On the day a polar day begins there is a real sunrise and no sunset, and dayLengthSeconds counts the 12 hours the Sun never leaves rather than reporting zero.

How accurate are the sunrise and sunset times?

NOAA describes its equations as "theoretically accurate to within a minute for locations between +/- 72° latitude, and within 10 minutes outside of those latitudes". The regression suite checks the rise, transit, set and civil-twilight values against the U.S. Naval Observatory's published times for Copenhagen, Quito, New York, Sydney, Reykjavík, Kiritimati and the equator; they agree to within a minute.

Does it account for altitude, air pressure or the local horizon?

No. Sunrise and sunset use NOAA's fixed −0.833° horizon (34′ of mean refraction plus the Sun's 16′ semi-diameter); observer elevation, pressure, temperature and terrain are ignored. Do not use the output for navigational, legal or safety-critical decisions.

How do I calculate solar elevation and azimuth for a timestamp?

/api/sun/position?lat=55.6761&lng=12.5683&at=2026-06-21T12:00:00Z returns geometric elevationDegrees (no refraction correction), azimuthDegrees measured clockwise from north, plus declination, equation of time and hour angle.

Are golden hour and blue hour included?

No. They are photographic conventions with no standards body defining them, so publishing them beside NOAA-derived values would imply an authority they do not have. Civil (−6°), nautical (−12°) and astronomical (−18°) twilight are included instead.

Standards & references