Moon phase calculator
Enter a date and see the Moon's phase: on 1 January 2026 the Moon is 12.421 days old, 93.9% lit and waxing gibbous. The calculation uses the mean lunar month of 29.530588853 days. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Worked example
- Date
- 2026-01-01
- Lunar age (days)
- 12.421
- Illumination (%)
- 93.9
- Phase
- Waxing gibbous
- Lunar cycle (days)
- 29.530588853
Given an ISO date, returns the Moon's age in days since a known new moon, its illuminated fraction as a percentage, and the named phase, using the mean synodic month of 29.530588853 days.
How the Moon's phase is worked out
The Moon goes from new to full and back in a mean synodic month of 29.530588853 days. The calculator counts the days between a known new moon and your date, evaluated at 12:00 UTC, and keeps only the remainder within the current cycle. That remainder is the Moon's age. The share of the disc that is lit follows a smooth curve of that age, close to 0% at new moon and peaking at 100% near full moon. On 1 January 2026 the age is 12.421 days and the illumination is 93.9%.
What the result is good for, and its limits
The named phase comes from splitting the cycle into eight bands of roughly equal length, from new moon through the crescent, quarter and gibbous stages and back again. That makes it a dependable source for calendars, almanac pages, dashboards and lunar icons for each day of a month. It is a mean-phase approximation that ignores the slightly elliptical orbit of the Moon, so the phase and illumination can be off by about a day. For precise astronomical positions, use a proper ephemeris instead.
What you can do with it
Calendars and almanac widgets
Show the phase and illumination for each day of a month, driving lunar icons from the deterministic named phase.
FAQ
What synodic period does it use?
The mean synodic month of 29.530588853 days, anchored to a reference new moon at 2000-01-06 18:14 UTC.
How accurate are the phase and illumination?
It is a mean-phase model that ignores orbital eccentricity, so results are accurate to roughly a day, ideal for calendars and dashboards.
What are the eight phases of the Moon in order?
New moon, waxing crescent, first quarter, waxing gibbous, full moon, waning gibbous, last quarter and waning crescent. Each covers roughly one eighth of the lunar month, and after the last one the cycle starts again at new moon.
What does the Moon's age mean?
It is the number of days since the last new moon, running from 0 up to about 29.53 before the cycle restarts. On 1 January 2026 the age is 12.421 days, which places the Moon in its waxing gibbous phase.
At what time of day is the phase calculated?
Every date is evaluated at 12:00 UTC, the middle of the day in universal time. That keeps one answer per date, so a calendar shows the same phase and illumination wherever it is opened.
Related tools
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/dev/moon-phase \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"date":"2026-01-01"}'const res = await fetch("https://api.kit.forhosting.com/dev/moon-phase", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"date": "2026-01-01"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/dev/moon-phase",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"date": "2026-01-01"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/dev/moon-phase", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"date":"2026-01-01"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"date":"2026-01-01"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/dev/moon-phase", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"date": "2026-01-01"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "dev.moon_phase",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
Technical notes
The synodic-month age model
Illumination is derived from that age as (1 - cos(2*pi*age/SYNODIC_DAYS)) / 2 * 100, a smooth curve peaking at 100% near full moon and near 0% at new moon. It evaluates each date at 12:00 UTC, takes the elapsed milliseconds since the epoch, divides by 86400000 to get days, and reduces that modulo the synodic length; a negative remainder is wrapped forward by adding one full period, so age_days is always in [0, 29.53).
How the phase name is chosen
The phase string is picked by comparing age_days against eight fixed thresholds, each roughly one eighth of the synodic month. Ages below 1.84566 are 'new', then 'waxing_crescent' below 5.53699, 'first_quarter' below 9.22831, 'waxing_gibbous' below 12.91963, 'full' below 16.61096, 'waning_gibbous' below 20.30228, 'last_quarter' below 23.99361, and 'waning_crescent' below 27.68493; anything at or above that final boundary wraps back to 'new'. Because the age for 2026-01-01 is 12.421, it falls in the waxing_gibbous band. This is a mean-phase approximation: it ignores the Moon's orbital eccentricity, so the named phase and illumination are accurate to about a day, which is more than enough for calendars, dashboards and content but not for precise ephemeris work.
Date validation and rounding
The date must be a string in strict YYYY-MM-DD form, checked by regex; month must be 1 to 12 and day 1 to 31, and the real calendar is verified with a UTC probe before any arithmetic so that an impossible date like 2026-02-30, which JavaScript's Date would silently roll over into March, is rejected as invalid_input instead of quietly accepted. The optional decimals parameter defaults to 3 and must be an integer from 0 to 6. age_days is rounded half-up to that precision, while illumination_percent is always rounded to a single decimal regardless of decimals. The response also echoes the input date and the synodic_days constant, and any non-finite intermediate raises invalid_input.
Integration use cases
Photography and outdoor planning
Query illumination_percent across a date range to find the darkest or brightest nights for astrophotography or fieldwork.
Content and scheduling automation
Batch dates through the API to tag posts, events or fishing/gardening guides with the correct moon phase.
Technical questions
Which date formats are accepted?
Only strict YYYY-MM-DD strings. Impossible dates such as 2026-02-30 are rejected as invalid_input rather than rolled over.
What are the possible phase values?
new, waxing_crescent, first_quarter, waxing_gibbous, full, waning_gibbous, last_quarter and waning_crescent, chosen by age thresholds.
Is it free and what does the API cost?
The on-page widget is free (tier A) and runs the same deterministic core; API calls are billed per successful request from $0.002.