Retrograde Station Detection: Stationary Point Edge Cases

11 min read
Torsten Brinkmann
astrologyretrogradestationary pointplanetary stationstransits API

Why naive day-step retrograde detection misses planetary stations, and how to detect zero crossings of longitude velocity for the astrology API.

TL;DR

  • Naive retrograde detection compares ecliptic longitude at t and t+1 day, then sets a flag when the sign of the difference changes. At the exact stationary point that difference shrinks below the day-sample noise floor, producing missed flips and false toggles on the day before and after.
  • The analytic fix is to detect zero crossings of the longitude derivative dλ/dt, not differences in λ itself. The numeric fix is hourly polling within plus or minus 36 hours of any suspected station.
  • The Western Astrology transits endpoint exposes per-planet speed (degrees per day, signed) and isRetrograde. Read speed to detect the apex; the boolean alone hides the precision you need for alerts.
  • Build a station-aware retrograde tracker on the Western Astrology API in 30 minutes.

About the author: Torsten Brinkmann is a Western astrologer and developer advocate with over 16 years of practice combined with a software engineering background. His work covers astrological calculation tools, birth chart APIs, and planetary aspect analysis, with current research on developer integration patterns for natal chart and transit endpoints.

Retrograde alerts are the most-clicked notification surface in any consumer astrology app. They also carry the highest correctness bar, because every reader knows what date the next station is supposed to land on. The naive detection pattern, sampling the geocentric ecliptic longitude of a planet at midnight UTC each day and watching for a sign change in the daily delta, fails at the one moment the alert exists for: the exact stationary point. This guide walks through why the day-step approach drifts, what the longitude derivative actually looks like across an inner-planet station, and how to detect the apex correctly using the Western Astrology API transits endpoint without re-implementing ephemeris math.

Why naive day-step retrograde flags drift at the exact station

A day-step detector computes delta = lambda(t+1d) - lambda(t) and flips a retrograde flag when delta changes sign. This works far from a station, where Mercury moves at roughly 207 arcseconds per hour and the daily delta is around 1.4 degrees. It fails inside the apex window because the longitude shift drops below the sampling resolution. At the very moment Mercury is stationary the planet is changing direction, not standing still in space, but its apparent geocentric longitude rate is essentially zero. A 24-hour delta of one arcminute is indistinguishable from noise, ephemeris rounding, or the choice of midnight UTC versus midnight local. The result is one of two failure modes: the alert fires a day late because the delta on the actual station date was too small to cross zero, or the flag toggles back and forth across the apex when sample-to-sample noise wins over the true rate.

A retrograde flag derived from sign(lambda(t+1d) - lambda(t)) will miss the precise station moment by up to 24 hours, and can produce a false direct-retrograde-direct toggle pattern across the apex. Notification scheduling that depends on this flag will fire on the wrong day.

Ready to build this? The Western Astrology API gives you signed daily speeds for all 10 planets in a single call. See pricing.

What the longitude derivative looks like across the pivot

Across a station the ecliptic longitude lambda(t) traces a smooth maximum or minimum, and its first derivative dλ/dt passes through zero. Plot speed against time and you see a near-linear ramp, not a step: hourly samples in a 72-hour window centred on the apex show speed sliding from positive through zero into negative for a stationary-retrograde, or negative through zero into positive for a stationary-direct. The sign change of speed is the analytic event. The sign change of finite differences in λ is a downstream and lossy estimator of that event.

0.2 arcsec/hour

Apparent geocentric longitude rate of Mercury at the apex of station, against a mean motion of 207 arcseconds per hour. A ratio of about 1000 to 1, which is why daily sampling collapses to noise inside the apex window.

The numbers above are the reason hourly polling beats daily inside plus or minus 36 hours of an expected station. Mercury at apex moves about 0.2 arcseconds per hour. The same planet moves about 207 arcseconds per hour at its mean rate. A finite-difference detector that works fine on a 1.4 degree per day signal becomes useless on a 5 arcsecond per day signal.

How to detect station crossing without sub-day sampling

Detect station by zero crossing of speed, not by sign flips of finite differences in λ. The transits endpoint already returns the planetary speed in degrees per day as a signed scalar, so the detector reduces to watching for a sign change of speed between consecutive samples and bisecting the window when one is found. If you can poll once per hour around expected stations, that is enough resolution to land within five minutes of the true apex. If you can only poll daily, you still want to read speed, not infer it from λ deltas, and accept hour-scale uncertainty on the apex timestamp.

type TransitPlanet = {
  name: string;
  longitude: number;
  speed: number; // degrees per day, signed
  isRetrograde: boolean;
};

// Detects a station between two samples by zero crossing of speed.
// Returns null when no crossing, or a linear-interpolated estimate of the apex time.
function detectStation(
  prev: { t: Date; planet: TransitPlanet },
  curr: { t: Date; planet: TransitPlanet }
): { apex: Date; kind: "direct" | "retrograde" } | null {
  const s0 = prev.planet.speed;
  const s1 = curr.planet.speed;
  if (s0 === 0 || s1 === 0 || Math.sign(s0) === Math.sign(s1)) return null;

  const t0 = prev.t.getTime();
  const t1 = curr.t.getTime();
  const apexMs = t0 + (-s0 / (s1 - s0)) * (t1 - t0);
  return {
    apex: new Date(apexMs),
    kind: s0 < 0 && s1 > 0 ? "direct" : "retrograde",
  };
}

The same logic works for any planet; only the cadence of expected stations changes. Mercury stations roughly three times per year, Venus every 19 months, Mars every 26 months. Outer planets are easier because their speed is smaller and the apex window is wider, which means daily polling is fine.

How RoxyAPI exposes retrograde state via the transits endpoint

The POST /astrology/transits response carries everything a station detector needs in transitPlanets[]: name, longitude (tropical ecliptic, degrees), speed (degrees per day, signed), and isRetrograde (boolean). Read speed for precision, use isRetrograde for headline display only. Below is a station-detection workflow you can run from a cron job or a serverless schedule. The endpoint does not require a natal chart for transit-only mode, but it does accept one for transit-to-natal aspect bonus output.

# Probe Mercury speed at one transit instant.
# No latitude or longitude needed for transit-only mode.
curl -s https://roxyapi.com/api/v2/astrology/transits \
  -H "X-API-Key: $ROXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "date": "2026-02-26",
    "time": "06:18:00",
    "timezone": 0
  }' | jq '.transitPlanets[] | select(.name == "Mercury")'

For coordinate-dependent endpoints the canonical pattern is to call GET /location/search?q={city} first, then feed latitude, longitude, and timezone into the chart call. Transits in transit-only mode skip that step because the geocentric ephemeris does not depend on observer location, only on instant.

Which 2026 inner-planet stations are tightest

Mercury has three retrograde periods in 2026 and Venus has one. Mars takes a break in 2026; the next Mars retrograde lands in 2027. The table below lists the stationary-retrograde and stationary-direct moments to the minute in Universal Time, sourced from the public 2026 ephemeris cycle reported by astronomy and astrology calendar sites. Use these as the polling-window centres for an hourly detector, plus or minus 36 hours.

PlanetDirectionUTC date and timeEcliptic longitude
MercuryStationary retrograde2026-02-26 06:1822 Pisces 34
MercuryStationary direct2026-03-20 19:338 Pisces 30
MercuryStationary retrograde2026-06-29 17:3626 Cancer 15
MercuryStationary direct2026-07-23 22:5716 Cancer 19
MercuryStationary retrograde2026-10-24 07:0220 Scorpio 59
MercuryStationary direct2026-11-13 15:535 Scorpio 02
VenusStationary retrograde2026-10-03 (UTC)8 Scorpio 29
VenusStationary direct2026-11-13 (UTC)22 Libra 52

Each station has a shadow zone, the longitude band the planet covers between entering its pre-retrograde degree and clearing it on the way back out. Mercury in February to March 2026 retrogrades from 22 Pisces 34 down to 8 Pisces 30, then re-traces 8 Pisces 30 to 22 Pisces 34 across the post-shadow window. Apps that surface "shadow start" and "shadow end" alerts use the same speed-zero-crossing logic anchored to the boundary degrees instead of the apex.

FAQ

What is a stationary point in planetary motion?

A stationary point is the instant when the apparent geocentric ecliptic longitude rate of a planet, dλ/dt, equals zero. The planet appears to stop and reverse direction from the geocentric vantage. The two flavours are stationary-retrograde, when speed flips from positive to negative, and stationary-direct, when speed flips from negative to positive.

Why does a day-step retrograde detector miss the apex?

A day-step detector measures the change in longitude over 24 hours and watches for a sign flip. At the apex Mercury moves only about 0.2 arcseconds per hour, so its 24-hour delta can be smaller than ephemeris rounding and clock-window noise. The sign flip then lands a day before or after the true station, or toggles spuriously across the boundary.

How often should I poll the transits endpoint near a station?

Hourly within plus or minus 36 hours of an expected station gets you within a few minutes of the apex via linear interpolation on the speed sign change. Far from any station, daily polling is sufficient for retrograde-state display and gives you a smaller bill. Mid-cycle, the speed signal is high enough that finite differences agree with the analytic derivative.

Does the transits endpoint return signed speed for every planet?

Yes. The transitPlanets[] array returns speed as a signed decimal in degrees per day for all 10 planets, where negative values indicate retrograde motion. The companion isRetrograde boolean is convenient for display, but only speed carries the precision needed to detect zero crossings near a station.

Are station shadow zones detectable from the same data?

Yes. The pre-retrograde shadow starts when the planet first crosses the longitude it will later station-direct at, and the post-retrograde shadow ends when it leaves the apex degree on the way out. Track longitude against the cached station boundaries; the same workflow that detects the apex by speed zero-crossing also detects the boundary entries by longitude crossing.

Conclusion

Retrograde station detection is the highest-precision moment in transit tracking, and the place where naive code most reliably ships wrong dates. Read speed from the transits endpoint and watch for sign changes, not finite differences in longitude. For accuracy claims and ephemeris cross-checks see the methodology page; for the consumer-facing surface around station alerts see the retrograde tracker app feature guide and the Mercury retrograde meaning explainer.