API Versioning

CycleCalcs publishes two API versions. /v1 is closed to new development and will not change again. /v2 is the current, actively developed contract, and the one to build against. This page states what "change" means for each, how a breaking change ships, and how to pin your integration to a specific published contract.

On this page

Where things stand

PathStatusWhat that means
/v1FrozenNo field is renamed, removed or retyped. No error code changes meaning. No cap tightens. Existing integrations keep working exactly as they do today, indefinitely.
/v2CurrentEvery new endpoint, field and fix lands here. This is the version to build against.

The API states this itself, as data rather than prose. GET /v2 reports:

"frozen_versions": [
  {
    "version": "1",
    "base": "https://www.cyclecalcs.com/v1",
    "status": "frozen",
    "note": "Version 1 is unchanged and will not change. New work happens on /v2."
  }
]

What counts as additive

These change /v2 at any time, without a version bump and without advance notice, because a client that ignores what it does not recognize is unaffected by them:

  • A new field appended to a response. In CSV it is a new column after every existing column, so a reader that takes columns by position keeps reading the values it read before.
  • A new warning code appended to the warnings array.
  • A new endpoint added under /v2.
  • A new optional parameter. Leaving it out returns what the endpoint returned before.
  • A newly accepted value of an existing parameter, one the endpoint refused before. Two examples:
    • at=jd:2461000.5, a Julian Day on the UT scale, read as UTC (2025-11-21T00:00:00Z) wherever at, start or end is read. It was refused as BAD_DATE.
    • at=2026-09-25T00:00:00Z,2026-09-25T06:00:00Z on /v2/positions, a list of up to 24 instants answered in the order sent. It was refused as BAD_DATE.
  • A loosened cap: a higher rate limit, a larger row cap, a longer range or search window. A request that was refused may now be answered, and a list cut short with a truncated_results warning may return more rows.

None of these renames or removes anything you already depend on. If your client reads known fields by name and does not choke on an extra JSON key or an unrecognized warning code, additive changes are invisible to you.

One case is announced all the same. /v2 does not refuse a parameter it does not accept: it ignores it and says so with an unknown_parameter warning. So a caller who was already sending a parameter, or a value, that the API ignored gets different answers on the day it gains a meaning. Such a change is announced the way a correction is.

Corrections

A correction fixes an answer that was wrong. It keeps every field's name and type, so it is not on the breaking list below, but it changes what an existing request returns, so it is never silent. These are corrections:

  • A corrected value: a number that was computed wrongly, or a constant restated to its measured figure.
  • A reclassified row: an event whose kind was wrong, for example a close_approach that is really a conjunction.
  • A corrected catalog: a registered error or warning code that no shipped route has ever returned, removed from /v2/enums, the OpenAPI enums and API Errors. No route ever returned it as an error or a warning, so no client can have met it on a failure path. A client that built its handling from /v2/enums or an OpenAPI enum only loses a branch that could never run.
  • A corrected description: a map in meta that describes the response, such as meta.frames, which defines each frame the response uses, corrected to list only what the response contains. An entry for something the response never carried is dropped. The entries describe the answer and carry none of its data, so no data field changes.

Each correction ships as a minor contract version, with a changelog entry naming the old and the new behavior. Where that entry links a dated note, API Accuracy says at more length what was wrong and why.

What counts as breaking

These never happen inside /v2. Each one ships as a new major version path instead:

  • Removing a field.
  • Renaming a field.
  • Changing a field's type, a number becoming a string, for instance.
  • Changing the error code returned for a condition that already has one.
  • Tightening a published cap: a lower rate limit, a smaller row cap, a shorter search window.

If a change like this is ever needed, it appears at /v3. /v2 keeps its present behavior for the rest of its life, the same way /v1 does today.

Pin your integration

Every /v2 response carries meta.contract_version, a semantic version. Right now, every response reports:

"meta": { "contract_version": "2.7.0", ... }

GET /v2 also echoes it as data.contract_version, describing the API as a whole, but meta.contract_version is the one to check: it is present on every single 2xx response from every endpoint, so you can read it off the exact answer you just received rather than making a separate call to the index.

The three numbers mean:

  • Patch (the third number) changes for a fix that does not alter behavior anything could depend on.
  • Minor (the second number) changes when an additive change or a correction, as defined above, ships.
  • Major (the first number) stays 2 for the entire life of this path. A change big enough to need a new major number ships at /v3, per the policy above; it is never applied to /v2 in place.

A caller that wants an early signal of an additive change can watch meta.contract_version across requests. A caller that only reads the fields it already knows about does not need to watch anything.

Deprecation

/v2 has not retired anything since launch. If an endpoint is ever retired, the response for that endpoint becomes 410 Gone with code ENDPOINT_RETIRED, and the hint on that response names the replacement endpoint directly, not a separate document you have to go find first.

/v2 does not send Deprecation or Sunset response headers. There is no header to poll for advance warning. A live endpoint tells you immediately, in its own response, if it has anything pending; this page is the other place to check.

Because breaking changes move to a new major version path rather than mutating /v2 in place, retiring an endpoint inside /v2 should be rare. Most future work is additive, and most callers will never see a 410 from this API.

Changelog

Every contract version since launch, newest first, grouped as additions, corrections and documentation. Each correction names the behavior it replaced; where it links a dated note, API Accuracy says at more length what was wrong and why.

2.7.0, 2026-10-30

One correction, to Espenak's supermoon verdict on /v2/phases, and three fields added beside it. Every other route answers as it did on 2.6.0 except meta.contract_version.

Added

  • The orbit behind Espenak's verdict, on /v2/phases. classification.espenak gains relative_distance, (apogee - distance) / (apogee - perigee) for the apogee and perigee nearest in time to the phase, the number Espenak's tables print: 0.9 or more is a supermoon under his rule. It also gains orbit_perigee_km and orbit_apogee_km, that perigee and apogee, so the verdict can be checked from the response alone. In CSV they are the last three columns, in that order, and under precision relative_distance keeps at least three decimals.

Corrected

  • Espenak's supermoon rule. classification.espenak drew one line per calendar year, ten percent of the way from the year's closest perigee to its farthest apogee (361,617.6 km in 2026), and credited it to Fred Espenak. His tables apply Nolle's 90 percent rule to each orbit instead. They list 50 supermoons for 2024 to 2029, and the yearly line missed 18, among them the full Moon of 2026-01-03 at 362,300 km. qualifies now follows his rule and agrees with all 50 and with no other new or full Moon of those six years. Across 1700 to 2200, 1,372 new and full Moons turn from false to true, about three a year, and none turns the other way; criteria_disagree, disagreement_note and the summary sentence follow. threshold_km is the distance at a relative distance of 0.9 in that orbit, so it moves from one orbit to the next, and rule_definition states the per-orbit rule. threshold_year, closest_perigee_of_year_km and farthest_apogee_of_year_km keep their names and change from a number to null on every new and full Moon: there is no yearly line to report, and a field is never removed inside /v2. A client that reads them as numbers must allow null; the orbit's own perigee and apogee are in the two fields added above. Dated note.

Documentation

  • apis.json and both OpenAPI files say 2.7.0, and the Endpoint reference is regenerated. API Accuracy and the Postman collection describe the per-orbit rule, and the event-database sample is reissued as version 1.0.4.

2.6.0, 2026-10-18

One endpoint is added. Every existing endpoint answers as it did on 2.5.0 except meta.contract_version.

Added

  • /v2/aurora. The live Kp index and NOAA storm level, the three-day Kp forecast, the solar wind and the OVATION auroral oval, all read from the NOAA Space Weather Prediction Center at request time. With a location (lat and lon, or place) it also says whether the oval is overhead or on your horizon, whether it is dark enough to see it, and the forecast Kp over tonight's dark hours. It is the first live route in /v2: its cache class is now_relative and it takes no at. A feed NOAA cannot serve leaves its block null and adds the new warning feed_unavailable; a stale feed adds feed_stale; a located answer always carries aurora_estimate. When every feed is down the route answers 503 DATASET_UNAVAILABLE.
  • NOAA SWPC in /v2/attribution. A new source row, public domain, credited on every /v2/aurora response.

Corrected

  • A place answer keeps a route's own sources. When place resolved, meta.attribution.sources was replaced by the GeoNames row alone. It now lists GeoNames first and keeps any source the route already named. No route before /v2/aurora named one, so no shipped response changes.

Documentation

2.5.0, 2026-10-15

What a place sees of a solar eclipse, and the central path, change by a few seconds and a few kilometers. Every field keeps its name and type. /v2/attribution gains one source.

Added

  • NASA's eclipse predictions in /v2/attribution. sources lists the Five Millennium Canon of Solar Eclipses' Besselian elements, credited "Eclipse Predictions by Fred Espenak, NASA's GSFC", and the rows for /v2/eclipses, /v2/phases and /v2/today name it among their conditional sources. The data is public domain, so no response's rights change; NASA asks that its acknowledgment stay with the figures, and /v2/eclipses carries it in central_geometry_note.

Corrected

  • Local solar eclipse circumstances. include=local on /v2/eclipses, and every answer that carries what a place sees of a solar eclipse, came from the engine's own Sun and Moon, which put the shadow of 2 August 2027 1.7 to 2.5 km south of JPL's DE440, and from a contact search that collapsed every totality under about 26 s to 0 to 2 s. Tripoli read total where it sees a partial eclipse, Al Khums 87 s where it has 33 s, and the rings of 6 February 2027 ran 2.5 to 7.4 s short. They now come from NASA's Besselian elements for each eclipse of 1700 to 2200 (Eclipse Predictions by Fred Espenak, NASA's GSFC), evaluated at the IERS delta-T. Against DE440 at 191 places on both 2027 eclipses every verdict agrees and every contact is within 1.4 s. Totality, the ring and obscuration use NASA's lunar radius k2 = 0.272281 Earth radii, where the engine's mean radius ran totality about 3 s long; first and last contact use k1 = 0.272488.
  • Central paths and hybrids. include=path, the greatest_eclipse_* duration, width and Sun altitude, and hybrid come from the same elements, so the limits and the local verdict agree. The path of 2 August 2027 moves about 2 km north. hybrid now names all 51 hybrids NASA lists for 1700 to 2200, 2172-10-17 among them.

Documentation

  • The central geometry note on /v2/eclipses names the source and the radii. apis.json and the OpenAPI file say 2.5.0, and the Endpoint reference is regenerated.

2.4.0, 2026-10-11

Two summary sentences print a different number at about one instant in twenty. Every other field is unchanged except meta.contract_version.

Corrected

  • Moon age in the /v2/moon summary. The sentence rounded phase.age_days, already rounded to a hundredth, again to a tenth, and could be a tenth off in either direction: 14.95361 days after the new moon, age_days is 14.95 and the sentence read "14.9 days into its cycle". It now rounds the elapsed time once and reads 15.0. This is the summary field and the format=txt style=sentence body, for one instant or a range. age_days keeps two decimals.
  • Illumination in the /v2/today summary. The sentence rounded moon.phase.illumination_percent, already rounded to a tenth, again to a whole percent, and could read a percent high: a Moon 49.46 percent lit publishes 49.5 and read "50 percent lit". It now rounds the illuminated fraction once and reads 49. illumination_percent keeps one decimal.

Documentation

2.3.0, 2026-10-10

A request that sends neither an Authorization header carrying a Bearer value that starts CCK1. nor an X-Api-Key header gets the answers, the range caps and the rate quotas it got on 2.2.0. Four things differ for it. The rate windows are counted from their start, so the reset values in RateLimit (t=), X-RateLimit-Reset and Retry-After count to the end of the current window, and a request over any window's quota is refused (both under Corrected). Access-Control-Allow-Headers gains X-Api-Key. Vary and the header directive of Netlify-Vary each gain authorization and x-api-key after the two RapidAPI headers. /v2/conventions and /v2/enums gain the fields and codes listed under Added, and the api_keys reason is reworded.

Added

  • CycleCalcs API keys. A key is read from Authorization: Bearer or from X-Api-Key. The URL is never read for one: key as a query parameter is still accepted, ignored and disclosed with parameter_ignored. A request through the RapidAPI gateway is served at its plan and a key beside it is not read. A key that verifies is served at the tier it names. A key that does not, or that is expired or past its transfer allowance, is still served, at the anonymous tier with a warning: INVALID_KEY, REVOKED_KEY and EXPIRED_KEY still never fire.
  • Two tiers. founding: 600 a minute, 5,000 an hour, 10,000 a day, with the pro range caps. professional: 1,200 a minute, 10,000 an hour, 20,000 a day, with the ultra range caps. RateLimit-Policy names them as founding, founding-hour and founding-day, and likewise for professional. Neither is reachable through a RapidAPI plan.
  • Five warning codes. key_invalid, key_expired, key_revoked, key_expiring (inside the last 60 days of a key whose life is longer than that) and key_transfer_spent. Each names the header it read in field and never carries the key.
  • The transfer allowance. A key carries a monthly allowance in decimal gigabytes, counted from the bytes the origin serves it in the UTC month; CDN hits are not counted. Past it the key is served at the anonymous tier, on the limits of any caller without a key, until the month ends, with key_transfer_spent on every response.
  • /v2/conventions. limits.tiers_defined lists every tier in order, and range_caps_by_shape carries one column per tier, founding and professional included. keyed_tier_reachable is true when either the RapidAPI gateway or the key channel is configured, and keyed_tier_note, like the cors sentence on /v2, describes the doors the serving deploy has.
  • Keyed CDN lifetime. A response to a request carrying a key claim has Netlify-CDN-Cache-Control public, s-maxage=300 in every cache class, so a change in a key's state reaches the CDN within five minutes. Its browser Cache-Control is unchanged.
  • Key warnings on refusals. A refusal (a 400 such as RANGE_TOO_LARGE, or a 429) to a request whose key was not honored carries a warnings member after docs, holding the key_* warnings a 200 would carry, so a drop to the anonymous tier is never silent. A refusal with no key warning has no such member.

Corrected

  • Fixed windows. The rate limiter counted a trailing window for each policy, the last 60, 3,600 and 86,400 seconds. It now counts each window from its start: the minute, the hour and the UTC day. Every quota is unchanged, and within one window the request after the last one allowed is still refused. RateLimit t=, X-RateLimit-Reset and Retry-After now count to the end of the current window, where they counted to the moment the oldest counted request left the trailing window. Across one window boundary up to twice a quota can be served, and on the same traffic the two limiters refuse different requests.
  • Every window enforced. A request was served when the minute window sat exactly at its quota while the hour or day window was already over its own, because the limiter judged each request against one window only. A request over any window's quota is now refused, and the refusal names the exceeded window that ends last.
  • The api_keys reason. /v2/enums and API Errors gave the reason the three key codes never fire as the RapidAPI gateway alone. It now reads "These codes never fire: a key this deployment cannot verify is still served, at the anonymous tier with a warning."

Documentation

  • The tier table gains the founding and professional columns.
  • OpenAPI (/v2/openapi.json and the RapidAPI 3.0.3 file): the warning enum gains the five key codes, and Problem gains the optional warnings member. API Errors and the Endpoint reference are regenerated.

2.2.0, 2026-10-09

Every instant is now timed on the Earth's measured and predicted rotation. Request shapes, caps and fields are unchanged except as listed.

Added

  • The delta-T statement. /v2/conventions gains time_scales.delta_t_description, the model in full: its sources, how far each runs, and the years it blends back to the polynomial.

Corrected

  • Delta-T. From 1972 to the end of the IERS prediction (2027-10-08 in this release), delta-T is the IERS value, measured (EOP 20 C04, then the Rapid Service) and predicted (Bulletin A), interpolated linearly; after the prediction it blends linearly back to the Espenak and Meeus polynomial by 2040. It was the polynomial everywhere, which ran 6.2 s high in 2026. An event computed for the present day (a phase, season, conjunction, station, apsis, node crossing or eclipse contact) now comes out about 6 s later in UTC; a rise, set, transit or twilight instant moves far less, by up to 1.4 s for the Moon at 66 degrees north and no more than 0.5 s at 60 degrees and below. Nothing before 1972 or after 2040 changes. meta.time.delta_t_model reads "IERS, Espenak-Meeus" in place of "Espenak and Meeus", /v2/time reports delta_t_model_residual_seconds of minus UT1 - UTC (-0.013 s at 2026-07-31, where it read 6.224), and the UT1 notes no longer say that no IERS bulletin is loaded. Dated note.
  • Conjunction end bound. /v2/conjunctions decides whether an approach falls inside end by the millisecond the row prints. It used the unrounded instant, which can sit a fraction of a millisecond later, so an instant passed back as end could lose its own row.
  • Neptune's stated accuracy. meta.conventions.solar_angle_accuracy_note on /v2/planet-events gives 5.6 minutes for Neptune against JPL Horizons, up from 5.5: the instants moved with delta-T, and the figure covers the largest measured difference plus the search precision.

2.1.0, 2026-09-25

No range 2.0.0 served is refused for its size on 2.1.0. A response is now refused only past 2,105,344 bytes, 8 KiB above the 2 MiB that every row bound is still computed against, so an answer 2.0.0 served a few bytes under the cap still serves with the bytes 2.1.0 adds to it (at most 140 in the answers measured at the size edge). For every route with a response size bound, the largest range 2.0.0 served was recorded in nested and flat JSON, in CSV and at precision=9, with wide ordinary rows (a southern location and a long zone name where the route takes them), and each of those 156 requests still serves, as do five more that 2.0.0 served within 129 bytes of the cap. A per-row field that would have pushed one of them past the size cap is carried only where it does not: on an answer for one instant, or once in meta. In CSV, every field added since 2.0.0 is a column after every 2.0.0 column.

Added

  • Julian Days. jd: followed by a Julian Day on the UT scale is accepted in at, start and end on every route and read as UTC: jd:2461000.5 is 2025-11-21T00:00:00Z. It was refused as BAD_DATE. It never raises assumed_utc.
  • A list of instants. /v2/positions accepts at=t1,t2,..., up to 24 instants on every tier, and answers data.series in the order sent with query.mode "list". The cap is range_caps_by_shape.instant_list on /v2/conventions. A list was refused as BAD_DATE.
  • Weekdays. A /v2/time answer for one instant carries data.weekday (local and utc, each with date, iso_weekday 1 to 7 and name) and meta.weekday_convention. A one-day /v2/rise-set answer carries window.weekday, /v2/twilight day windows carry window.local_date and window.weekday, and /v2/dark-window nights carry night_of_weekday, in JSON only. /v2/time series rows, the day rows of a /v2/rise-set range, and /v2/sun and /v2/moon day windows carry none: on those rows it would have refused requests 2.0.0 served.
  • Time zone warnings. tzdb_lmt_interval and tz_source_boundary_1970, registered and never emitted, are now emitted. The boundary warning is raised for a tz database link by the name sent, including links the runtime resolves to another name.
  • Named stars. star:<name> and hr:<n> targets, from a full-precision reduction of the Bright Star Catalog (BSC5P) with IAU WGSN names, proper motion, parallax and radial velocity applied to the date, in body, bodies, a and b on /v2/positions, /v2/rise-set, /v2/separation and /v2/rarity, and in bodies and pairs on /v2/conjunctions. The set is the 57 Nautical Almanac stars, Polaris, and the IAU-named stars of V 3.0 or brighter within 10.3 deg of the ecliptic, a close double counted at its combined light; the list and its count are at /v2/enums?set=stars (fields code, name, hr, v_magnitude, combined_v_magnitude and sets), and /v2/enums?endpoint= scopes it to the five star routes. These were UNKNOWN_BODY.
  • Fixed-target rows on /v2/positions. After the row's 2.0.0 fields, a star row gains a star block (hr, magnitudes, the J2000 catalog place, proper motion, parallax with its kind and note, radial velocity, sets and sources), constellation, and row-level ecliptic (true of date and J2000) and galactic blocks, and no distance. A radec row gains constellation, ecliptic and galactic the same way. A body row keeps its ecliptic place inside position.
  • Label origin. /v2/positions gains meta.conventions.label_origin, the place every row's tropical_sign and constellation were taken from: geocentric, or heliocentric under frame=heliocentric, never topocentric, one value per response, present when some row carries either label. /v2/conventions gains field_definitions for tropical_sign, constellation and origin, and OpenAPI gains the TropicalSign, Constellation and LabelOrigin schemas.
  • Placeholder distances. /v2/separation rows gain distance_is_placeholder {a, b}, and distance_note when either target is a named star or radec pair.
  • Newly answered. frame=ecliptic with a radec target on /v2/separation, and named stars on /v2/conjunctions including frame=ecliptic, were refused with 400. A new refusal covers a new condition only: a /v2/separation range search over two fixed targets when either is a named star is BAD_PAIR, and a radec-radec range keeps its answer and gains the new warning fixed_pair_minima. Two stars in one /v2/conjunctions pair are BAD_PAIR, and a star-star combination from bodies is skipped.
  • Light deflection, stated. corrections.light_deflection { applied: false } with a note on /v2/conventions, and light_deflection: false in meta.corrections on every position-bearing response.
  • Star sources. /v2/attribution lists the Bright Star Catalog (BSC5P, via NASA HEASARC) and the IAU Catalog of Star Names (WGSN, CC BY 4.0), in sources and in the sources and conditional_sources of the five routes that take a named star. No route's rights value changes.
  • Pair longitudes. longitude_conjunction_instants (every longitude crossing in the minimum's basin) and sky_separation_deg on /v2/conjunctions approaches and /v2/separation rows. meta.conventions gains coincidence_rule, coincidence_rule_note, basin_cap_days, basin_stride_days, basin_stride_days_with_moon and sky_separation_note; /v2/phases and /v2/seasons gain meta.method.definition, and /v2/conjunctions gains meta.method.longitudes.
  • The Moon's extrapolated magnitude. Past 150 deg of phase angle, within about 2.4 days of new moon, the Moon's magnitude is an extrapolation of its observed phase curve, kept and flagged: magnitude_extrapolated on /v2/phases rows (include=distance) and on a /v2/moon or /v2/positions answer for one instant, and magnitudes_extrapolated {a, b} on /v2/conjunctions rows (true or false in the Moon's slot, null for any other body). The rows of a /v2/moon or /v2/positions range or list carry no flag; a /v2/moon or /v2/positions answer that carries a Moon magnitude gives the threshold as meta.conventions.moon_magnitude_observed_phase_angle_deg (150). /v2/conventions gains photometry.moon_phase_law and photometry.moon_magnitude_observed_phase_angle_deg.
  • Planet events for Mars to Neptune. /v2/planet-events accepts mars, jupiter, saturn, uranus and neptune (BODY_NOT_SUPPORTED_HERE before), with the kinds conjunction_with_sun, quadrature_west, opposition and quadrature_east: the instants the planet's apparent geocentric ecliptic longitude minus the Sun's reaches 0, 270, 180 and 90 deg. Each event carries value_deg (the elongation), ecliptic_longitude_deg, constellation, distance_from_earth_au, magnitude, longitude_convention and convention_note, with instant_precision_seconds 2. Unasked, a superior planet gets these four kinds and Mercury and Venus keep their seven apparition kinds. There is no arbitrary-angle parameter. The Sun, the Moon, Pluto, named stars and radec targets are still refused.
  • Constellation entries. The kind constellation_entry on /v2/planet-events, for Mercury to Neptune, returned only when kinds lists it: each crossing of an IAU 1930 constellation boundary by the planet's apparent geocentric place, with from_constellation, to_constellation, direction, ecliptic_longitude_deg, instant_precision_seconds 1, and pass_index and pass_count, which number a run of consecutive crossings of one boundary that ends at each conjunction with the Sun between retrograde loops (every conjunction for Mars to Neptune, the superior conjunction for Mercury and Venus), so a run never spans two loops: 3 for a loop that carries the planet over, back and over again, at most 7 over 1700-2200. The label reads "Uranus enters Gemini from Taurus".
  • Planet-events meta. meta.conventions gains the solar_angle_* conventions only when a Sun-relative kind is walked, and the constellation_* conventions only when constellation entries are asked for; meta.method gains solar_angles and constellation_entries on the same conditions. The apparition family's heliocentric conventions and meta.method.events appear when a Mercury or Venus apparition kind is walked, which covers every request 2.0.0 accepted, and an answer holding both families gains convention_scope_note. A kinds token none of the requested bodies can have is ignored with a parameter_ignored warning.
  • Solar terms. /v2/seasons accepts include=solar_terms: every instant the Sun's apparent geocentric ecliptic longitude reaches a multiple of 15 deg, 24 a year, on the same search as the equinoxes. The quarters and cross-quarters keep their kinds; the other sixteen are the new kind solar_term, labeled by longitude, with solar_longitude_deg and solar_longitude_is_definitional. The token brings events and cross_quarters with it and adds meta.method.solar_terms.
  • Event kinds. /v2/enums event_kinds gains conjunction_with_sun, quadrature_east, quadrature_west, constellation_entry and solar_term.
  • Eclipse paths. /v2/eclipses include=path returns the central path of the central solar eclipses of 2027-02-06, 2027-08-02, 2028-01-26 and 2028-07-22 as inline GeoJSON (central line, northern and southern limits, and points carrying instant, central phase, duration, path width and Sun altitude). The geometry travels in nested JSON only, and is left out with a note under CSV or shape=flat, or with a truncated_results warning where it would push the response past the size limit; other eclipses return available: false with the reason. Solar rows gain hybrid and hybrid_transitions, central rows gain greatest_eclipse_duration_seconds, greatest_eclipse_path_width_km and greatest_eclipse_sun_altitude_deg, and hybrid rows add hybrid_definition. New warning hybrid_eclipse; new data.central_geometry_note and central-path conventions; parameter_ignored when include=greatest cannot apply or count is sent in range mode. /v2/phases solar eclipse objects and /v2/today solar_eclipse events gain hybrid.
  • Traditional precession figure. The axial_precession row of a /v2/cycles answer for one instant gains traditional_period_days and traditional_period_years, the traditional 360 x 72 years, after page_url; the rows of a range do not carry them. Every answer gains meta.conventions.precession_years_traditional.
  • MCP. astro_eclipses takes an optional include (path, greatest), and astro_planet_events takes the seven planets and all twelve kinds. The server still has 11 tools.

Corrected

  • Error catalog. 14 error codes no route has ever returned, registered for chart constructs this API does not publish, were listed in the error registry, /v2/enums error_codes, API Errors and the OpenAPI Problem.code enum of both OpenAPI files. They are removed, and status 422, which only one of them used, leaves the Problem.status enum.
  • Warning catalog. 16 warning codes no route has ever emitted, registered for the same unbuilt features, were listed in /v2/enums warning_codes and the OpenAPI Warning.code enum. They are removed.
  • TIME_REQUIRED. It was listed as reachable; every route reads a missing at as now, so /v2/enums now marks it known_unreachable: true (surface time_defaults_to_now) and API Errors says it is not returned. Its hint named two time forms that never existed; it now reads "Send `at` as an ISO 8601 date or date-time, for example at=2026-08-12T21:00:00Z".
  • /v2/enums. A surface name for an unbuilt feature no longer appears in any surface or reason field. 10 registered warning codes no route emits, listed as reachable, are marked known_unreachable: true (surface not_detected). The time_format iso definition now states the UTC exception, and the catalogs withheld reason is rewritten.
  • Local mean time. An instant whose zone offset has nonzero seconds (pre-1972 local mean time) was written with a truncated offset; it is now written in UTC with Z. For such offsets utc_offset_iso was truncated and is now rounded to the nearest minute; utc_offset_seconds is unchanged. Summaries read clock digits and dates from the local wall clock.
  • Eclipse summary dates. With time_format=unix, the /v2/eclipses summary named the peak's unix seconds as its date; it now names the local date.
  • Range refusals. A RANGE_TOO_LARGE refusal on a span cap now states the span cap and the remedy for the caller's own step, names count when count= set the end, carries a legal first-page example, and takes precedence over the size refusal. Size refusals offered fields= and paging, and no longer do; events-cap refusals offered a wider step or count=, and no longer do. The catalog hint is rewritten. The code is unchanged.
  • BAD_DATE details. They now name the actual fault: signed or expanded years, offsets with seconds, years 0000-0099, a malformed jd:, and a comma used as the decimal sign, which is now one value refused where it was split into a list. The offset refusal states the 24:00 bound. The code is unchanged.
  • Large historical offsets. A local wall time within 16 hours of a transition, on a zone whose historical offset exceeded 14 hours (Alaska, October 1867), resolved wrongly as ambiguous or nonexistent; it now resolves correctly.
  • Phase instants. On /v2/phases, /v2/moon next_phases, /v2/today, /v2/cycles (synodic month) and /v2/apsides, a phase was timed with the Sun taken without aberration. It is now the instant the Moon's apparent ecliptic longitude minus the Sun's, as /v2/positions reports them, reaches 0, 90, 180 or 270 deg, so every instant moves 34 to 46 s earlier; phase_angle_deg, age_days and the lunation anchors move with them. The definition texts say so: phase.phase_angle_convention on /v2/moon, and the synodic month's fraction_method on /v2/cycles, named the engine's MoonPhase elongation and now name the apparent longitudes, and meta.method.definition credits USNO for the definition. Dated note.
  • Season instants. /v2/seasons equinoxes, solstices and cross-quarters used a fixed one-AU light time; they now use the apparent solar longitude /v2/positions reports: June solstices about 8 s later, December solstices about 8 s earlier, equinoxes about 2 s. Dated note.
  • The Moon's phase angle. phase.phase_angle_deg on /v2/moon and geometry.phase_angle_deg on /v2/libration took the Sun without aberration; they now use the apparent longitudes, as the phase instants do, and move by about 20 arcsec (0.006 deg). The Moon phase names on /v2/dark-window and /v2/sky-quality are read from the same angle.
  • Pair longitudes. frame=ecliptic longitude differences on /v2/conjunctions and /v2/separation, ecliptic_separation_from_sun_deg on /v2/positions and ecliptic_separation_deg on /v2/planet-board left out aberration and now use apparent longitudes, for named stars and radec targets as for bodies. Moon-pair crossings move by up to about 45 s, other values by about 20 arcsec at most.
  • Conjunction or close approach. A separation minimum was a conjunction only when the longitudes crossed within 24 hours of it. It is now a conjunction when the apparent longitudes cross anywhere in its basin, between the separation maxima either side, capped at 180 days, on /v2/conjunctions and on /v2/separation for any pair with a Solar System body (a fixed target takes the apparent place /v2/positions reports; two fixed targets are never searched). Some close_approach rows become conjunction, for example Uranus-Neptune 1993-11-01, Saturn-Uranus 1988-06-28 and 1988-10-20, Venus-Saturn 2025-04-28 and Venus-Mars 2022-03-16. longitude_conjunction_instant is the nearest crossing and can be days or weeks from the minimum. meta.conventions.coincidence_tolerance_hours keeps its value, 24, and no longer affects any row, as its note now says. With frame=ecliptic, sky_separation_deg and the text built on it are geocentric even with a location. The frame=ecliptic label, combined_apparent_note and the /v2/separation summary now describe the true sky separation, and the partial no_search_result message says the listed pairs had no approach. Dated note.
  • Engine call accounting. meta.budget.engine_calls now counts an apparent pair-longitude evaluation as two calls, and counts the hybrid test this release added for each solar eclipse: about 270 calls an eclipse on /v2/eclipses, and 114 on /v2/phases and /v2/today. At the edge of an accepted request the realized count can exceed the published budget (about 251,000 against 250,000 for the slowest pairs), as it could on 2.0.0. Pre-flight budgets and caps are unchanged.
  • Printed minutes. The site, the paid calendar and the ICS feeds truncated an instant to the minute; they now round it, and print the same minute. Every ICS event, free and paid, starts (DTSTART) on the minute its description prints. The feed revision constants are bumped, so ETags and DTSTAMPs change.
  • One apparent place for a fixed target. /v2/positions rotated a radec target to the date with no aberration; it now takes annual aberration and annual parallax at the nominal 1000 light-years, the place /v2/separation and /v2/rise-set already used, so values move by up to about 20 arcsec, and equatorial.j2000 on a radec row is that apparent direction in the J2000 frame. /v2/rarity dark-walk altitudes for radec targets use the same place. Dated note.
  • meta.frames. Every /v2/conjunctions and /v2/separation response listed six frames, including equatorial_j2000, ecliptic_j2000 and galactic, and horizontal without a location, though those routes never return a position in them. It now lists equatorial_of_date and ecliptic, plus horizontal with a location, and /v2/positions reads its list off the rows. This is the corrected-description case under Corrections; no data field changes. Dated note.
  • meta.corrections. aberration and topocentric_parallax are true for fixed targets; light_travel_time is true only when a Solar System body is present; proper_motion is true with a named star; scope_note is rewritten; frame=geocentric with a location no longer claims topocentric parallax on /v2/separation or /v2/conjunctions.
  • /v2/conventions. proper_motion applied is true; the light_travel_time note says the Sun and planets, and that the Moon's place is geometric; the tropical_sign definition gives the drift as about 1 deg every 71.6 years, from the measured IAU 2006 rate, where it used the traditional figure.
  • A body passing a fixed target. On /v2/separation it is classified by the basin rule, and its ecliptic differences, which were null, are numbers, with new note text.
  • Retrograde constellations. On /v2/retrogrades, a station's constellation and constellations_crossed were taken for a stand-in observer at latitude 0, longitude 0; they now come from the geocentric place. Between 1700 and 2200 the visible difference is Mercury's April-May 1793 loop.
  • UNKNOWN_BODY. For star: and hr: it gains hint, example and supported, and a bare star name gets a hint.
  • Eclipse conventions. meta.conventions.umbra_enlargement_note credited the 2 percent enlargement to Danjon; it now credits Chauvenet (Astronomical Almanac) and gives NASA's canon differences (about 0.006 umbral, 0.026 penumbral). meta.method.paths_of_totality and meta.method.hybrid_eclipses describe the new behavior, and the century hybrid list is complete. not_visible_from_location was emitted for an eclipse type the request did not ask for and is now emitted only for the type requested; both next_visible members are still published. A hybrid row's label reads "Solar eclipse (hybrid)" on /v2/eclipses, and a hybrid solar_eclipse event's reads "Hybrid solar eclipse" on /v2/today; kind and global_kind stay the engine's.
  • The precession period. On /v2/cycles axial_precession, period_days was 9467280, the traditional 360 x 72 years, and is now 9413068, the measured turn at the IAU 2006 rate (1,296,000 / 50.28796195 x 365.25); period_text named the traditional round figure and now reads "about 25,772 years (IAU 2006 rate)"; description reads "once in about 25,772 years"; state.rate_arcsec_per_year was 50.0 and is now 50.288; state.equinox_note gives the rate as about 50.3 arcseconds a year; meta.conventions.precession_years is 25772. Dated note.
  • Great conjunction description. Under include=definition, its closing clause framed the trigon by signs and gave an 800-year return; it now ends "the beat, the 243-degree step, and the slowly turning trigon." Text only.
  • Opposition's surface. /v2/enums event_kinds published opposition as reachable on /v2/conjunctions, which answers kind=opposition with 400. Its surface is now /v2/planet-events, which emits it, with the label template "{body} at opposition, {value} from the Sun".
  • Planet-events refusals. The BODY_NOT_SUPPORTED_HERE detail told a caller asking for a superior planet that /v2/conjunctions reports oppositions; it is now given only for the Sun, the Moon, Pluto and fixed targets, and the refusal's hint, example and supported name the seven planets. A kinds refusal said "is not an apparition event kind" for an unknown token and now says "is not an event kind this endpoint reports."; an empty kinds said to omit it for every kind and now says to omit it for each body's default kinds, since constellation_entry is returned only when listed; supported lists all twelve kinds. /v2/seasons include refusals list solar_terms. The codes are unchanged.
  • Companion note. /v2/retrogrades companion_event.convention_note said /v2/conjunctions gives the geocentric instant of an outer planet's opposition; it now names /v2/planet-events (kinds=opposition). An inner planet's note names /v2/conjunctions with the pair and the max_separation_deg it needs.
  • OpenAPI. /v2/planet-events body and bodies offered every engine body and radec targets, which the route refuses, and now list the seven planets. /v2/eclipses count is documented as 1 to 50 and the include pattern with its leniency, the limits the endpoint always enforced.

Documentation

  • This page: the additive list names newly accepted values with two examples and says a new CSV field is a column after every existing one; a caveat covers callers already sending an ignored parameter; the Corrections section lists corrected values, reclassified rows, corrected catalogs and corrected descriptions; and this changelog is new.
  • Served descriptions: the /v2 index summaries of /v2/seasons and /v2/planet-events name the solar terms and the Mars-to-Neptune events and constellation entries; /v2/phases meta.method.events describes the quarter walker without the engine's function names; and under frame=ecliptic, /v2/conjunctions meta.method.frame and /v2/separation data.definition define separation_deg and point to sky_separation_deg.
  • API Accuracy: named stars; phase and season instants on the apparent longitude; pre-1972 delta-T; pair-event accuracy; new-moon rows against JPL Horizons DE441 in TT and UT from 1700 to 2200; the Sun-relative planet events and constellation entries against JPL Horizons; and a dated note for each correction this changelog links there.
  • Endpoint reference: the named-star grammar, the ecliptic frame for stars and radec targets, the basin rule and sky_separation_deg, the field definitions with meta.conventions.label_origin, the planet-events bodies and kinds, the seasons include list, the eclipse include list, and regenerated samples.
  • OpenAPI (/v2/openapi.json and the RapidAPI 3.0.3 file): the star grammar only on the routes that take it; the time, tz, time_format and at descriptions; the TropicalSign, Constellation and LabelOrigin schemas; the warning enum gains fixed_pair_minima and hybrid_eclipse.
  • MCP: the server instructions carry the astronomy-only scope sentence and name the Mars-to-Neptune events and constellation entries; astro_planet_events no longer promises stations and points to astro_planet_board and /v2/retrogrades; the MCP page publishes a generated table of the routes the 11 tools reach; the date description names jd: and the instant list; astro_positions says the labels stay geocentric; astro_eclipses states the path size, about 65 to 90 KB per eclipse.
  • /v2/attribution and NOTICE.md name the star sources.

All API documentation

  • Developer hub: What to build and where every tool lives: API, MCP server, widgets.
  • Overview and playground: What the API answers, the plans, and a live try-it panel.
  • Endpoint reference: Every route, its parameters and a real captured response.
  • Keys and plans: How to get a key, what each plan raises, and how quotas are counted.
  • Error codes: The registered problem documents, one entry per code.
  • Accuracy and residuals: Measured error against reference ephemerides, and the conventions every response states.
  • Versioning policy: What is frozen, what may change, and how a change is announced.
  • Status and limits: Rate windows, range caps and the current service position.
  • MCP server: The same engine as tools an AI assistant can call directly.
  • Version 1 (frozen): The original seven endpoints, still served and never changing shape.
  • Terms of use: What you may do with the answers, including in commercial products.