Skip to content

Quickstart — Python SDK

Same walkthrough as the curl quickstart and the TS SDK quickstart, using the Python SDK's shared schemas with httpx for HTTP.

The Python SDK ships typed Pydantic schemas; it doesn't yet ship a generated HTTP client. The pattern most Skynet Python consumers use is httpx + the SDK schemas — that's what we'll do here.

Prereqs

  • Python 3.10+ and a way to install skynet-sdk (in-tree via uv, or a local-checkout install — see Python SDK).
  • pip install httpx pydantic (or use the SDK's already-installed deps).
  • An access token. See Auth.

Setup

import os
import httpx
from skynet_sdk.schemas import (
    Observation,
    ObservationRequest,
    Target,
    FixedPosition,
)
from skynet_sdk.enums import (
    DeviceType,
    InstrumentTaskExecutionState,
)

API = "https://api.skynetgo.org/v1"
TOKEN = os.environ["SKYNET_TOKEN"]

client = httpx.Client(
    base_url=API,
    headers={"Authorization": f"Bearer {TOKEN}"},
    timeout=30.0,
)

Walkthrough

def main() -> None:
    # 1. Who am I?
    me = client.get("/me").raise_for_status().json()
    print(f"Hello, {me['firstName']} {me['lastName']}")

    # 2. List telescopes
    telescopes = client.get("/telescopes", params={"size": 10}).raise_for_status().json()
    print(f"{telescopes['total']} telescopes visible")
    for t in telescopes["items"][:5]:
        print(f"  - {t['name']} (id={t['id']}, slug={t['slug']})")

    # 3. Pick an observing grant
    accounts = client.get(
        "/observing-accounts",
        params={"ownerId": me["id"]},
    ).raise_for_status().json()
    account = accounts["items"][0]
    grant = account["observingGrants"][0]

    # 4. Create a draft observation
    obs = client.post("/observations", json={
        "name": "Py Quickstart M51",
        "ownerId": me["id"],
        "kind": "draft",
        "iterations": 1,
        "priority": 50,
    }).raise_for_status().json()
    obs_id = obs["id"]
    print(f"Draft observation {obs_id} created")

    # 5. Set the target
    client.patch(f"/observations/{obs_id}/target", json={
        "name": "M51",
        "position": {
            "positionType": "fixed",
            "frame": "equatorial",
            "ra": 202.4696,
            "dec": 47.1952,
        },
    }).raise_for_status()

    # 6. Add an imaging request
    client.post(f"/observations/{obs_id}/requests", json={
        "requestType": "opticalImaging",
        "iterations": 1,
        "filterTypes": ["V"],
        "exposureMode": "time",
        "exposureTime": 60.0,
    }).raise_for_status()

    # 7. Attach the grant
    client.post(f"/observations/{obs_id}/observing-grants", json={
        "observingGrantId": grant["id"],
    }).raise_for_status()

    # 8. Publish
    published = client.post(f"/observations/{obs_id}/publish").raise_for_status().json()
    print(f"Published as {published['kind']} (id={published['id']})")


if __name__ == "__main__":
    main()

Why not a generated client?

The TS SDK leans on openapi-typescript to auto-generate typed clients from /openapi.json. There's no equivalent in the Python SDK today — most Python integrations either use httpx directly (as above) or use the WebSocket protocol for streaming work where the schemas are the bigger win than a generated HTTP client.

If a generated Python client would help you, flag it. The OpenAPI spec is already served at /openapi.json so the tooling step is straightforward.

Validating responses with the SDK schemas

When you want type safety on the response side, validate against the SDK's Pydantic models:

raw = client.get(f"/observations/{obs_id}").raise_for_status().json()
observation = Observation.model_validate(raw)
print(observation.name, observation.status)

For polymorphic responses (Device, Instrument, ObservationRequest, …), use the base type's validator — Pydantic picks the concrete subclass from the discriminator:

from skynet_sdk.schemas import Device

devices = client.get("/devices").raise_for_status().json()
for raw_device in devices["items"]:
    device = Device.model_validate(raw_device)  # concrete: Camera, Mount, …
    print(device.device_type, device.id)

Coordinate math

For coordinate transforms and ephemerides, the Python SDK exposes ephem:

from skynet_sdk.ephem import target_position, visibility

These wrap astropy.coordinates for the patterns Skynet uses internally. For ad-hoc coordinate work, use astropy directly.

Next steps