Metadata-Version: 2.4
Name: ionoflux
Version: 0.1.3
Summary: Official synchronous Python client for the IonoFlux data API
Project-URL: Homepage, https://ionoflux.runvia.app
Project-URL: Repository, https://github.com/hongjunna/ionoflux-python
Project-URL: Documentation, https://github.com/hongjunna/ionoflux-python#readme
Project-URL: Changelog, https://github.com/hongjunna/ionoflux-python/blob/main/CHANGELOG.md
Project-URL: Releases, https://github.com/hongjunna/ionoflux-python/releases
Project-URL: Issues, https://github.com/hongjunna/ionoflux-python/issues
License-Expression: PolyForm-Noncommercial-1.0.0
License-File: LICENSE
License-File: NOTICE
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python :: 3
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: httpx<0.29,>=0.27
Provides-Extra: dev
Requires-Dist: build<2,>=1.2; extra == 'dev'
Requires-Dist: mypy<2,>=1.15; extra == 'dev'
Requires-Dist: pytest<10,>=8; extra == 'dev'
Requires-Dist: readme-renderer[md]<45,>=44; extra == 'dev'
Requires-Dist: ruff<1,>=0.11; extra == 'dev'
Requires-Dist: twine<7,>=6; extra == 'dev'
Description-Content-Type: text/markdown

<a href="https://ionoflux.runvia.app"><img src="https://ionoflux.runvia.app/brand/icon-512.png" width="96" height="96" alt="IonoFlux official logo: sun and radio waves on a blue background"></a>

# IonoFlux Python SDK

Official synchronous Python SDK for [IonoFlux](https://ionoflux.runvia.app), providing space-weather, solar and geomagnetic data, alerts, TEC/MUF maps, ionosonde stations, HF propagation predictions, observed PSK Reporter/WSPRnet reception reports and public data metadata. Requires **Python 3.11+**.

```sh
pip install ionoflux
```

[PyPI](https://pypi.org/project/ionoflux/) · [GitHub](https://github.com/hongjunna/ionoflux-python) · [한국어](https://github.com/hongjunna/ionoflux-python/blob/main/README.ko.md) · [Official site](https://ionoflux.runvia.app) · [Releases](https://github.com/hongjunna/ionoflux-python/releases)

## Quickstart

Sign in at https://ionoflux.runvia.app/account and create a **Server** API key. Python runs outside the browser: a Web key can return 403. The current account flow requires a service URL even for Server keys; notebooks/batches without a website need an appropriate service URL under the existing policy. The SDK cannot issue keys or bypass that requirement.

```sh
export IONOFLUX_API_KEY='your-server-key'
```

```python
from ionoflux import Client

with Client() as client:
    current = client.space_weather.current()
    print(current.data)  # Original JSON fields, including nulls and timestamp strings
    print(current.rate_limit.remaining)
```

Explicit `Client(api_key="...")` overrides the environment. No key is required for `sources()` or `data.status()`; other calls without a key raise `ConfigurationError` with the key page link (https://ionoflux.runvia.app/account). Keep keys out of source control, browsers and shared notebook outputs.

```python
from datetime import UTC, datetime
from ionoflux import Client, RateLimitError

with Client(timeout=10.0, max_retries=1) as client:
    try:
        series = client.solar.flux(
            start=datetime(2026, 10, 1, tzinfo=UTC),
            end=datetime(2026, 10, 2, tzinfo=UTC),
            interval="1h",
            limit=100,
        )
        print(series.data["unit"], series.data["attribution"])
        print(series.data["data"])
    except RateLimitError as error:
        print(error.retry_after, error.rate_limit.reset_seconds)
        # Return control to your application; no automatic daily-quota sleep.
```

## Supported calls

All return `APIResponse[T]`: `.data` (JSON dict, or list of dicts for alerts), `.raw_body` (original bytes), `.status_code`, `.headers` (quota/content-type only), `.rate_limit`. Nested JSON values are flexible (`Any`), not runtime-validated domain models; additive server fields survive. Timestamps remain ISO 8601 strings for lossless JSON; request datetimes must be aware and are converted to UTC. No automatic DataFrame conversion.

| Call | Options beyond shared time-series arguments |
|---|---|
| `client.space_weather.current()` | Current indicators and freshness |
| `client.solar.flux(...)` | SFI |
| `client.solar.sunspots(...)` | `source="swpc"` or `"silso"` |
| `client.solar.xray(...)` | `band="long"` or `"short"` |
| `client.solar.wind(...)` | `parameter="speed"`, `"density"`, `"bt"`, `"bz"` |
| `client.geomagnetic.kp(...)` | `source="swpc"` or `"gfz"`, `include_forecast=True` |
| `client.geomagnetic.ap(...)` | Ap |
| `client.statistics(metric, ...)` | `start`, `end`, metric-specific `source`; 10 metrics in contract |
| `client.alerts.list(...)` | `since`, repeatable `category` list, `limit=50` (1..500) |
| `client.alerts.scales()` | R/S/G levels and outlook |
| `client.sources()` | Public provider catalog and collector freshness |
| `client.data.status(dataset=None)` | Public summary or latest records of a dataset |

Shared time-series arguments: `start=None`, `end=None`, `interval="raw"`, `limit=5000` (1..10000). Omitting dates preserves endpoint-specific **server defaults**. The API keeps the newest limited points then returns them ascending. No cursor or automatic full-history download. `1mo` is a fixed **30-day** bucket. Requests with explicit start and end are limited to 3660 days; query allowance does not imply historical availability. Consult `data.status()` for retention and actual coverage.

## Ionosphere, propagation and observations

Added in 0.1.3. All need a Server key and return a JSON object. Locations (`from_`, `to`, `location`) accept `"lat,lon"`, a Maidenhead locator such as `"PM37"`, or a place name resolved by the server (2..200 characters). Optional `time`/`start` must be timezone-aware and are sent as UTC.

| Call | Options and limits |
|---|---|
| `client.ionosphere.tec(...)` | `time`, `lat`+`lon` together for a point value, `format="json"` or `"geojson"` |
| `client.ionosphere.muf(...)` | same as `tec`, plus `hop_km=3000` (0..4000), `source="model"` or `"ionosonde"` |
| `client.ionosphere.stations(max_age_hours=6)` | 1..48; GIRO ionosonde stations with their latest observation |
| `client.propagation.path(from_, to, frequency, ...)` | `frequency` 1.8..54 MHz, `time` (≤ 72 h ahead), `mode`, `power`, `tx_gain`, `rx_gain`, `noise`, `path` |
| `client.propagation.forecast(from_, to, ...)` | `start`, `hours=24` (1..72), same link options |
| `client.propagation.conditions(location=None, time=None)` | Per-band OPEN/MARGINAL/CLOSED; global day/night summary without `location` |
| `client.observations.live(...)` | `minutes=15` (1..60), `band`, `mode`, `source`, `bbox=(min_lon, min_lat, max_lon, max_lat)`, `limit=2000` (1..5000) |
| `client.observations.paths(from_, to, ...)` | `radius_km=500` (50..1500), `hours=1` (max 6), `band` |
| `client.observations.bands(location=None, ...)` | `radius_km=1000` (50..3000), `role="rx"` or `"tx"`, `minutes=15` (5..60), `mode` |
| `client.observations.callsign(callsign, ...)` | `role="tx"` or `"rx"`, `hours=24` (max 48), `limit=300` (1..1000) |

Link options: `mode` is `"ft8"`, `"ft4"`, `"wspr"`, `"cw"`, `"ssb"`, `"am"`, `"rtty"` or `"psk31"`; `noise` is `"city"`, `"residential"`, `"rural"` or `"quiet"`; `path` is `"auto"`, `"short"` or `"long"`. `band` is an amateur band from `"160m"` to `"6m"`. Observation `mode` is free text such as `"FT8"`. Out-of-range arguments raise `ValueError` before any request is sent, so they do not use quota.

```python
from ionoflux import Client

with Client() as client:
    muf = client.ionosphere.muf(lat=37.57, lon=126.98)
    print(muf.data["muf_mhz"], muf.data["attribution"])

    path = client.propagation.path("Seoul, South Korea", "FN20", 14.074, mode="ft8")
    print(path.data["prediction"]["availability"], path.data["model"]["limitations"])

    heard = client.observations.paths("PM37", "PM95", hours=1, band="20m")
    print(heard.data["a_to_b"]["total_spots"])
```

Know what each value is. `source="model"` TEC/MUF and every `propagation.*` result are **model outputs** from NOAA GloTEC maps (future times use persistence with a storm correction; see the response `model` field). `source="ionosonde"` MUF and `stations()` are **measurements** at stations. `observations.*` are real reception reports: they prove one-way propagation at that moment, but no reports does not prove a closed band. Keep each response's `attribution`; PSK Reporter, WSPRnet, GIRO and GloTEC carry their own terms. The server decides how many points a grid or live map has; ask for a point value or a smaller `bbox`/`limit` when you do not need everything.

## Errors, networking and security

`AuthenticationError` (401), `PermissionDeniedError` (403), `NotFoundError` (404), `ValidationError` (422), `RateLimitError` (429), `ServerError` (5xx) and `RedirectError` (3xx) extend `APIError`. Exceptions carry `status_code`, redacted textual `detail`, `rate_limit` and `retry_after`; no HTTP request object. Other failures use `TransportError`, `ResponseError` or `ConfigurationError`. Locally invalid method arguments raise `ValueError`.

Only GET is implemented. At most 0..3 retries (default 1) apply only to connection failures/timeouts, read failures/timeouts, remote protocol errors and HTTP 502/503/504; 401/403/404/422/429/500, response decoding errors, local protocol/configuration errors and pool timeouts are never retried. A 502/503/504 response with Retry-After or Remaining=0 is returned immediately as ServerError; the SDK does not ignore a server wait instruction. Backoff is 0.25, 0.5, 1 seconds (maximum aggregate sleep 1.75 seconds). Retry-After is exposed, never used to trigger a long wait. HTTPX `timeout` is a finite positive per-operation timeout, **not a total wall-clock deadline**. Every retry may count against the API quota. `X-RateLimit-Reset` is seconds remaining until UTC midnight, not an epoch timestamp. `Retry-After` remains its original string; unknown/malformed numeric quota headers (including more than 20 digits) become `None`.

The default API root is `https://ionoflux.runvia.app/v1`; a custom `base_url` must be HTTPS, end in `/v1`, and contain no credentials, query or fragment. Redirects are never followed. Environment proxies and netrc credentials are disabled (`trust_env=False`). Session/transport lifetime is owned by `Client`; call `close()` or use `with`. The SDK does not log credentials, send them to public metadata routes or put them in URLs. Custom HTTP transports and application-level HTTP debug logging are trusted caller code; do not log raw request headers. Raw success bodies can contain arbitrary server data: treat them as sensitive application data when sharing.

For IonoFlux usage statistics every request also carries fixed origin tags `utm_source=python&utm_medium=sdk&utm_content=<SDK version>` and the `User-Agent: ionoflux-python/<SDK version>` header. They contain no key, user, host or environment data.

## Documentation and verification

[API contract and limits](https://github.com/hongjunna/ionoflux-python/blob/main/docs/api-contract.md) · [Development plan](https://github.com/hongjunna/ionoflux-python/blob/main/docs/development-plan.md) · [Changelog](https://github.com/hongjunna/ionoflux-python/blob/main/CHANGELOG.md)

This synchronous SDK does **not** provide async clients, pandas/DataFrame conversion, bulk/historical spot downloads or map rendering.

v0.1.0 was released on PyPI on 2026-10-20. Its [release record](https://github.com/hongjunna/ionoflux-python/releases/tag/python-v0.1.0) preserves the original artifacts and verification evidence. A bounded nine-request Server-key acceptance passed for current/SFI/Kp/statistics/alerts/scales/public metadata; actual 403/429 responses and every provider/endpoint permutation were not exercised. See [acceptance and limits](https://github.com/hongjunna/ionoflux-python/blob/main/docs/release-review.md). The 0.1.3 ionosphere/propagation/observation calls were checked against the public OpenAPI schema and mocked responses; no new authenticated live acceptance is claimed.

Development tests block network connections and use HTTPX MockTransport. CI checks Linux Python 3.11–3.15, Windows/macOS 3.14, minimum HTTPX 0.27.0, Ruff, mypy, README rendering, wheel/sdist builds and separate clean installations. Publishing uses protected GitHub environments and Trusted Publishing with manual approval; normal PR CI does not publish. Development setup and validation commands are in the [development plan](https://github.com/hongjunna/ionoflux-python/blob/main/docs/development-plan.md).

## SDK code license, API terms and data rights

The SDK code is licensed under **[PolyForm Noncommercial License 1.0.0](https://github.com/hongjunna/ionoflux-python/blob/main/LICENSE)**
(SPDX: `PolyForm-Noncommercial-1.0.0`). The standard text is unmodified. It permits
noncommercial use, including personal/hobby use, and its educational, public-research
and other listed institutional permissions apply in full, regardless of funding or
resulting funding obligations. Do not treat every company-funded university activity
as excluded from the standard code license.

Commercial SDK uses outside those permissions require a separate written license
from the authorized rights holder. For inquiries, contact the operator through the
[official IonoFlux service](https://ionoflux.runvia.app). An inquiry, payment or public
issue is not itself written authorization; do not post keys or confidential contract
details publicly. No unverified company name or legal rights-holder name is asserted.

When redistributing any SDK copy, preserve the license terms (or their official URL)
and the plain-text Required Notice in [NOTICE](https://github.com/hongjunna/ionoflux-python/blob/main/NOTICE):

```text
Required Notice: IonoFlux (https://ionoflux.runvia.app)
```

API access and attribution of API results are managed separately. The
[proposed API terms](https://github.com/hongjunna/ionoflux-python/blob/main/docs/api-terms-proposal.ko.md) remain **unapproved and not in effect**;
adopting the SDK license does not implement those proposed UI/publication obligations,
change current site terms or authorize uploads. The SDK's redistribution notice is
not an automatic requirement to display a credit on every website/paper.

Provider data licenses and attribution remain independent. An SDK commercial license
or API permission cannot grant third-party rights IonoFlux does not hold. Preserve
source/attribution and inspect provider-specific restrictions. This is not an
OSI open-source license; source visibility does not remove the noncommercial terms.

[Adopted policy and rights-holder review](https://github.com/hongjunna/ionoflux-python/blob/main/docs/licensing-review.ko.md) ·
[Bilingual usage notice](https://github.com/hongjunna/ionoflux-python/blob/main/docs/readme-usage-draft.md) ·
[Use cases](https://github.com/hongjunna/ionoflux-python/blob/main/docs/use-cases.ko.md) · [Attribution examples](https://github.com/hongjunna/ionoflux-python/blob/main/docs/attribution-examples.md) ·
[API terms pre-effective review checklist](https://github.com/hongjunna/ionoflux-python/blob/main/docs/api-terms-preflight.ko.md)
