Everything needed to get a station streaming, a spacecraft registered and a query running. The API is versioned at /v1 and the version in the path is the version you pinned, not the version we happen to run.
Register a spacecraft, connect a station and issue your first query in about fifteen minutes.
ReferenceEvery endpoint, every parameter and every error code, generated from the OpenAPI description.
GuideStation connections, the APID dictionary, validation rules and how gaps are recorded.
SupportWhat each HTTP status and each application error code means, and what to do about it.
Three calls and a station configuration. The examples below assume an API token in the environment variable HELION_API_TOKEN.
Registering a spacecraft creates its identity and reserves its COSPAR identifier in the registry. The APID map can be added immediately or uploaded later.
curl -X POST https://api.helionhq.link/v1/spacecraft \
-H "Authorization: Bearer $HELION_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "AURORA-1",
"cospar_id": "2024-013A",
"bus": "hexagon-100",
"orbit": "leo-sso-530km",
"apid_map": [
{ "apid": 0x41, "name": "housekeeping", "period_s": 1.0 },
{ "apid": 0x42, "name": "payload", "period_s": 0.5 }
]
}'
A response of 201 returns the spacecraft identifier, which every later call references.
A station credential is scoped to the spacecraft it may send for and to the ingest endpoint it may reach. It is shown once.
curl -X POST https://api.helionhq.link/v1/station-keys \
-H "Authorization: Bearer $HELION_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"spacecraft_id": "sc_9f3a71c4",
"label": "gs-rey-a",
"endpoints": ["ingest.eu-north.helionhq.link:443"]
}'
The secret is returned once in the response body and is not recoverable afterwards.
Parameters are addressed by spacecraft, APID and parameter name. The response carries both the spacecraft time and the station receive time, because the difference between them is usually the interesting number.
curl -G https://api.helionhq.link/v1/parameters \
-H "Authorization: Bearer $HELION_API_TOKEN" \
--data-urlencode "spacecraft_id=sc_9f3a71c4" \
--data-urlencode "parameter=battery_bus_voltage" \
--data-urlencode "from=2026-01-14T00:00:00Z" \
--data-urlencode "to=2026-01-14T01:00:00Z"
The SDK handles pagination, retries with backoff and token refresh. It is published to PyPI as helion-sdk and requires Python 3.9 or newer.
from helion import Client
client = Client(token=os.environ["HELION_API_TOKEN"])
series = client.parameters.query(
spacecraft_id="sc_9f3a71c4",
parameter="battery_bus_voltage",
start="2026-01-14T00:00:00Z",
end="2026-01-14T01:00:00Z",
limit=1000,
)
for sample in series:
print(sample.sc_time, sample.value, sample.unit, sample.quality)
The reference below is a summary. The complete, machine-readable description is published as OpenAPI 3.1 and can be fetched from the endpoint itself.
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | /v1/spacecraft | List spacecraft visible to the token | Token |
| POST | /v1/spacecraft | Register a spacecraft | Owner |
| GET | /v1/spacecraft/{id} | Spacecraft detail and registry version | Token |
| PUT | /v1/spacecraft/{id}/apid-map | Replace the APID dictionary | Operator |
| GET | /v1/parameters | Query parameter samples | Token |
| GET | /v1/parameters/{name} | Parameter metadata and units | Token |
| GET | /v1/gaps | List telemetry gaps | Token |
| GET | /v1/exports | List export jobs | Token |
| POST | /v1/exports | Create an export job | Operator |
| GET | /v1/windows | Contact windows for a spacecraft | Token |
| POST | /v1/commands | Queue an uplink command | Operator |
| POST | /v1/commands/{id}/approve | Approve a critical command | Approver |
| GET | /v1/alerts | List alert instances and state | Token |
| POST | /v1/alerts/rules | Create an alert rule | Operator |
| GET | /v1/audit | Read the append-only audit log | Owner |
| GET | /v1/station-keys | List station credentials | Owner |
| POST | /v1/station-keys | Issue a station credential | Owner |
List endpoints use cursor pagination. Pass the cursor value from the previous response to fetch the next page. Offsets are supported for small result sets but are not recommended: the underlying data set changes while you page through it.
Write endpoints accept an Idempotency-Key header. Replaying a request with the same key returns the original response and does not create a second resource. Keys are scoped per endpoint and retained for 24 hours.
All timestamps are RFC 3339 in UTC with millisecond precision. Ranges are half-open: from is inclusive and to is exclusive, which lets adjacent queries tile without double-counting a boundary sample.
How the pieces fit together on the station side.
The station opens an outbound TLS 1.3 connection to the ingest endpoint. Mutual TLS is used on the Fleet plan; on other plans a bearer credential in the Authorization header is sufficient. The stream is a length-prefixed sequence of transport frames in the order the antenna received them.
The acknowledgement means the frame is durably stored at the ingest edge. It does not mean the frame is queryable. The gap between the two is the ingest latency, measured and published on the status page as a percentile distribution rather than an average.
The dictionary maps APIDs to parameter sets and declares the secondary header type for each virtual channel. It is versioned: an update creates a new version, and samples already in the archive keep the scale factors that were in force when they were decoded.
Virtual channel identifiers are preserved end to end. If your spacecraft separates housekeeping, payload and beacon traffic into three channels, you can query them independently, and the gap ledger reports gaps per channel rather than in aggregate.
Errors return a JSON body with a stable application code, a human-readable message and, where relevant, the offending field. The code is what you should branch on; the message may change.
| HTTP | Code | Meaning | What to do |
|---|---|---|---|
| 400 | invalid_request | The request body failed schema validation | Check the field named in details |
| 400 | invalid_time_range | from is not earlier than to | Reverse the bounds |
| 401 | unauthenticated | Missing or malformed credential | Set the Authorization header |
| 401 | token_expired | The token passed its expiry | Refresh the token |
| 403 | insufficient_scope | The token lacks the required scope | Request the scope or use an operator token |
| 403 | spacecraft_forbidden | The token has no grant for this spacecraft | Ask an owner for a grant |
| 404 | not_found | No such resource, or no grant to see it | Verify the identifier |
| 409 | idempotency_conflict | The key was used with a different payload | Use a new key |
| 409 | registry_version_conflict | The registry changed since the version you read | Re-read the version and reapply |
| 413 | payload_too_large | Request body above 2 MiB | Use the bulk import endpoint |
| 422 | schema_mismatch | Frames do not match the declared APID map | Inspect the quarantine bucket |
| 422 | units_ambiguous | The parameter exists with more than one unit | Pass the unit explicitly |
| 429 | rate_limited | Too many requests | Read Retry-After and back off |
| 500 | internal_error | An unexpected fault | Retry with backoff, then contact support with the request id |
| 502 | upstream_unavailable | A dependency did not respond | Retry with backoff |
| 503 | maintenance | Planned maintenance in progress | Check the maintenance window on the status page |
| 504 | gateway_timeout | The request exceeded the 30-second budget | Narrow the query range |
{
"code": "schema_mismatch",
"message": "packet length field exceeds frame boundary",
"request_id": "req_01HQ8ZC4K2M7",
"details": {
"spacecraft_id": "sc_9f3a71c4",
"virtual_channel": 0,
"apid": 66,
"frame_length": 1024
}
}
Quote the request_id in a support request. It is the identifier our logs are indexed by, and it saves a round trip.
Retry on 429, 502, 503 and 504 with exponential backoff and jitter. Do not retry a 4xx other than 429 — the request will fail again in the same way. The SDK applies a default policy of four attempts over roughly thirty seconds, and honours the Retry-After header when it is present.
Breaking changes require a new major version in the path. Additive changes, new response fields and new enum values may appear in a minor version at any time, so parse defensively: ignore fields you do not recognise and treat an unknown enum value as unknown rather than as an error.
Limits are applied per token, not per account, and are reported on every response.
| Endpoint class | Operator | Constellation | Fleet | Window |
|---|---|---|---|---|
| Parameter queries | 60 req/min | 240 req/min | Negotiated | 1 minute |
| Gap and window reads | 120 req/min | 480 req/min | Negotiated | 1 minute |
| Export creation | 10 req/h | 60 req/h | Negotiated | 1 hour |
| Command submission | 60 req/min | 240 req/min | Negotiated | 1 minute |
| Station key issue | 5 req/h | 30 req/h | Negotiated | 1 hour |
| Ingest stream | 8.4 Mbit/s | 8.4 Mbit/s | Negotiated | Continuous |
We would rather write the missing page than have you guess. Tell us what you were trying to do and what happened instead.