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 -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"}'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, lon | number, required | Terminal or ground-site position in degrees. |
| label | string | Your name for the site, up to 60 characters. |
| start | ISO 8601 | Window start, from 6 hours ago to 24 hours ahead. Defaults to now. |
| policy | "trained" | "rules" | The learned policy (default) or the hand-set baseline. |
| rainMmH | number | Override the live rain forecast with your own rate. |
| stormMmH | number | Add a what-if storm on top of the forecast. |
| kp | 0-9 | Override NOAA's live planetary Kp index. |
Response
{
"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.