Quick start
Create a key, send one link sample, read it back, and get an SNR forecast for it. It takes about five minutes and works on the Free plan.
You need a Constellation account and either curl or Python 3.9+ with httpx.
1. Create an API key
Sign in on the account page, choose New key, and select these scopes:
| Scope | Lets the key |
|---|---|
telemetry:write | Send telemetry |
topology:read | Read your fleet back |
predictions:read | Request forecasts |
Copy the secret when it is shown; it is shown once. Keep it in your environment:
- curl
- Python
export CONSTELLATION_API_KEY="cos_live_..."
export CONSTELLATION_API_KEY="cos_live_..."
import os
from datetime import datetime, timezone
import httpx
api = httpx.Client(
base_url="https://api.constellation.space",
headers={"x-api-key": os.environ["CONSTELLATION_API_KEY"]},
)
The Free plan includes 10 forecasts a month. Telemetry writes and topology reads are not capped.
2. Send a link sample
Write one link record stamped with the current time. entity_id is the link's ID; you use it again in steps 3 and 4.
- curl
- Python
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
curl -X POST https://api.constellation.space/telemetry \
-H "x-api-key: $CONSTELLATION_API_KEY" \
-H "Content-Type: application/json" \
-d '[{"measurement": "link", "time": "'"$NOW"'",
"tags": {"entity_id": "sat-1-gs-1"},
"fields": {"snr_db": 12.5, "elevation_deg": 45, "azimuth_deg": 180,
"slant_range_km": 1200, "frequency_ghz": 20}}]'
now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
ack = api.post("/telemetry", json=[{
"measurement": "link",
"time": now,
"tags": {"entity_id": "sat-1-gs-1"},
"fields": {"snr_db": 12.5, "elevation_deg": 45, "azimuth_deg": 180,
"slant_range_km": 1200, "frequency_ghz": 20},
}])
print(ack.status_code, ack.json())
A 202 means the record was accepted:
{"write_id": "e523302c-5335-4757-bceb-7542a132e74d", "accepted_count": 1, "rejected_count": 0, "rejected_indices": null}
Use only letters, digits, hyphens, and underscores in link IDs. Forecast requests currently reject other characters, such as : and ..
3. Read it back
Topology returns the newest sample for every entity that reported inside the freshness window.
- curl
- Python
curl -G https://api.constellation.space/topology \
-H "x-api-key: $CONSTELLATION_API_KEY" \
-d measurement=link -d freshness_seconds=900
fleet = api.get("/topology", params={"measurement": "link", "freshness_seconds": 900}).json()
print(fleet["entities"])
Your link appears in entities with the fields you sent:
{
"measurement": "link",
"entity_id": "sat-1-gs-1",
"observed_at": "2026-10-05T02:58:09Z",
"tags": { "producer_account_id": "acct_..." },
"fields": {
"azimuth_deg": 180.0,
"elevation_deg": 45.0,
"frequency_ghz": 20.0,
"slant_range_km": 1200.0,
"snr_db": 12.5
}
}
If your link is missing, the sample is older than freshness_seconds; send it again with the current time.
4. Forecast it
- curl
- Python
curl -G https://api.constellation.space/predictions \
-H "x-api-key: $CONSTELLATION_API_KEY" \
-d model_family=snr -d link_ids=sat-1-gs-1
forecast = api.get("/predictions", params={"model_family": "snr", "link_ids": "sat-1-gs-1"}).json()
for item in forecast["items"]:
print(item["horizon_minutes"], item["value"]["snr_db_p50"])
print(forecast["entity_errors"])
The response has one item per horizon (1, 3, and 5 minutes). This is the 1-minute item:
{
"link_id": "sat-1-gs-1",
"horizon_minutes": 1,
"predicted_at": "2026-10-05T02:59:01.062874Z",
"model_version": "snr-baseline-v1",
"value": {
"snr_db_p50": 12.5,
"snr_db_p10": 12.166304,
"snr_db_p90": 12.833696,
"data_age_seconds": 1.063
}
}
snr_db_p50is the predicted SNR in dB;p10andp90bound the 80% interval.- The current release,
snr-baseline-v1, carries the last observedsnr_dbforward, so the median matches what you sent. - Read
entity_errorsas well asitems. A link without usable recent telemetry is listed there with a code, not returned as a zero forecast.
That is the whole loop. The same three calls work for every link you operate.
Use an AI agent instead
Connect Claude Code to the MCP server with the same key:
claude mcp add --transport http constellation https://api.constellation.space/mcp \
--header "Authorization: Bearer $CONSTELLATION_API_KEY"
Then ask: "Send a link sample for sat-1-gs-1 with snr_db 12.5, elevation 45 degrees, and slant range 1,200 km at the current time, then forecast it." The agent calls send_telemetry and get_predictions for you. Other clients: Connect an AI agent.
If something fails
| Response | Cause | Fix |
|---|---|---|
401 auth_failed | Key missing, mistyped, or revoked | Check the x-api-key header, or create a new key |
403 insufficient_scope | The key lacks the scope named in required | Create a key with all three scopes from step 1 |
400 invalid_query_parameter, field link_ids | The link ID contains a character other than letters, digits, -, or _ | Rename the link and send the sample again |
200 with your link in entity_errors | No usable recent sample for the link | Send a sample with the current time, then retry |
429 quota_exceeded | This month's forecasts are used | Wait for resets_at, or upgrade on the account page |
429 rate_limited or auth_lockout | Too many requests or failed keys | Wait the Retry-After delay; see Errors and limits |
Next steps
- Stream telemetry continuously with the fleet agent, which buffers to disk and keeps the key off your applications.
- Send everything each model reads. Models lists the full SNR input set and what demand forecasting needs.
- Prefer a UI? Open the console as a guest to explore the demo fleet, or see the console overview.