SpaceNoneSuchDevelopers

spnh.handover.v1

One call. The best satellite, when to switch, and how hard to push the link.

Send a position. The API plans the next hour against real orbits, the live rain forecast over that spot and NOAA space weather, then returns a handover schedule and modulation plan, compared with what a reactive system would have done.

Quick start

curl
curl -X POST https://spacenonesuch.com/api/v1/handover \
  -H "Authorization: Bearer spnh_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"lat": 40.70, "lon": -73.97, "label": "Brooklyn Navy Yard"}'
JavaScript
const res = await fetch("https://spacenonesuch.com/api/v1/handover", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SPNH_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ lat: 40.70, lon: -73.97, stormMmH: 10 }),
})
const plan = await res.json()
console.log(plan.servingNow, plan.handovers)

Request body

lat, lonnumber, requiredTerminal or ground-site position in degrees.
labelstringYour name for the site, up to 60 characters.
startISO 8601Window start, from 6 hours ago to 24 hours ahead. Defaults to now.
policy"trained" | "rules"The learned policy (default) or the hand-set baseline.
rainMmHnumberOverride the live rain forecast with your own rate.
stormMmHnumberAdd a what-if storm on top of the forecast.
kp0-9Override NOAA's live planetary Kp index.

Response

200 OK, trimmed
{
  "api": "spnh.handover.v1",
  "mode": "live",
  "window": { "from": "...", "to": "...", "stepSec": 30 },
  "policy": { "name": "trained", "version": "..." },
  "servingNow": { "satellite": "IRIDIUM 140", "modulation": "16APSK 3/4", "snrDb": 14.2 },
  "handovers": [{ "at": "...", "from": "IRIDIUM 140", "to": "IRIDIUM 116" }],
  "modulation": [{ "at": "...", "profile": "QPSK 1/2" }],
  "comparison": {
    "predictive": { "outageSec": 0, "handovers": 4 },
    "reactive": { "outageSec": 180, "handovers": 3 },
    "outageAvoidedSec": 180
  },
  "upcomingPasses": [ ... ],
  "rain": { "source": "open-meteo.com 15-minute forecast", "peakMmH": 2.1 },
  "spaceWeather": { "kp": 2.7, "scintillationRisk": "low" }
}

Errors and limits

  • 401Missing, unknown or revoked key.
  • 400Body is not JSON, or a field is out of range. The message names the field.
  • 429Rate limit reached. Check X-RateLimit-Remaining.
  • 502A public data source did not respond. Retry shortly.

Every response carries X-RateLimit-Limit and X-RateLimit-Remaining. Sandbox keys always use the hand-set rules policy; live keys get the trained policy.

What it is, honestly

  • Orbits are real: public Iridium NEXT elements from CelesTrak, propagated with SGP4. They stand in for any LEO fleet.
  • Rain is a real forecast from Open-Meteo; the link model is a simplified ITU-R rain attenuation at 20 GHz.
  • The policy was trained on recorded weeks and tested on days it never saw. The benchmark page shows the result.
  • It plans; it does not steer your antennas. Your system decides whether to follow the plan.
Read the benchmark