API

Tasqyn API · Reference

Flood probability, one HTTP call ahead.

The Tasqyn API turns hydro-meteorological observations into a calibrated probability of spring flooding a month or more ahead for snowmelt-driven river basins. One trained XGBoost model, an honest walk-forward score of ROC-AUC 0.91, a 90% confidence interval where the ensemble is available, and live multi-basin forecasting from open weather data.

REST · JSON over HTTPS 6 basins 1–6 month horizon Model tasqyn-xgboost-1.1.1

Every response is plain JSON. There are no SDKs to install — the examples on this page use curl, but any HTTP client works. If you only need one thing, you almost certainly want GET /forecast/live: give it a basin and it returns the next few months of flood probability, calibrated, with confidence intervals, using live weather it fetches for you.

Base URL

All endpoints are served from a single host and addressed by path. There is no resource prefix — paths such as /predict sit at the root.

POST https://tasqyn-api.onrender.com/predict

Running the service yourself? The reference server is a FastAPI app (uvicorn api.main:app) that listens on http://127.0.0.1:8000 by default and ships interactive Swagger UI at /docs and a raw schema at /openapi.json.

Run locally
pip install -e ".[api]"
uvicorn api.main:app --reload

# interactive docs
open http://127.0.0.1:8000/docs

Authentication

The public preview is open: no key is required, and cross-origin requests are allowed from any origin so the web app and mobile client can call it directly.

Production deployments authenticate with a bearer token. Send it in the Authorization header; requests without a valid key receive 401. Treat keys as secret — never ship them in client-side code you don't control.

iThe preview has no rate limit, but it is best-effort. For anything you depend on, run your own instance from the open-source service — it is a single FastAPI file with swappable in-memory stores.
Authenticated request
curl https://tasqyn-api.onrender.com/basins \
  -H "Authorization: Bearer $TASQYN_KEY"

Errors

Tasqyn uses conventional HTTP status codes. 2xx means success; 4xx means the request was rejected (a bad body, an unknown basin); 5xx means the model or an upstream data source was unavailable. Error bodies follow FastAPI's shape with a single detail field.

StatusMeaning
200Success.
400The observation window could not be scored (e.g. empty or malformed).
404Unknown basin_id. Call /basins for valid ids.
422Request body failed schema validation.
502Live weather (Open-Meteo) was unavailable for a live forecast.
503Model artifacts are not loaded — the service is degraded.
404 Not Found
{
  "detail": "Unknown basin 'volga'. Known: petropavl,
            nur-sultan, atbasar, kokshetau, oral, kurgan"
}
503 Service Unavailable
{ "detail": "Model artifacts are not available." }

Versioning

The API surface is versioned independently of the model. The service version (1.1.0) tracks the HTTP contract; the model version (tasqyn-xgboost-1.1.1) tracks the weights and calibration. Every forecast echoes the exact model_version that produced it, so a stored prediction is always reproducible.

Additive changes (new fields, new endpoints) ship without a version bump. Breaking changes to the HTTP contract increment the service version.

GET /health
{
  "status": "ok",
  "model_loaded": true,
  "calibrated": true,
  "model_version": "tasqyn-xgboost-1.1.1",
  "n_features": 44,
  "n_basins": 6
}

Core object

The Forecast object

A forecast is the model's answer for one basin in one month: a calibrated flood probability, a human risk band, a prediction interval and the provenance metadata that tells you how much to trust it. /predict returns one; /forecast/live returns several.

AttributeType
flood_probabilityfloatCalibrated probability of flooding, 0–1. The number to act on.
raw_probabilityfloatPre-calibration model score, 0–1. For inspection only.
riskobjectHuman band: label (low/moderate/high) and a hex color.
confidence_intervalobject90% interval from a 15-model bootstrap: lower, upper, std. May be null.
thresholdfloatDecision threshold used for is_flood_predicted.
is_flood_predictedbooltrue when flood_probability ≥ threshold.
in_flood_seasonboolWhether the month is in the basin's snowmelt season.
reliabilityobjectTrust metadata — see Reliability.
provenancestringHow the inputs were obtained: user-supplied, observed (live weather) or climatology-filled.
basin_id / basin_labelstringWhich basin the forecast is for. May be null for an unlabelled /predict call.
year / monthintThe month the forecast applies to.
model_versionstringExact model that produced the forecast.
generated_atstringISO-8601 timestamp.
Forecast
{
  "basin_id": "petropavl",
  "basin_label": "Petropavl · Ishim (Esil)",
  "year": 2024,
  "month": 4,
  "flood_probability": 0.82,
  "raw_probability": 0.913,
  "confidence_interval": {
    "lower": 0.55,
    "upper": 0.93,
    "std": 0.11
  },
  "risk": { "label": "high", "color": "#ef4444" },
  "threshold": 0.5,
  "is_flood_predicted": true,
  "in_flood_season": true,
  "provenance": "observed",
  "reliability": {
    "in_distribution": true,
    "out_of_range_features": [],
    "river_level_imputed": false,
    "transfer": false,
    "note": "in-distribution anchor basin"
  },
  "model_version": "tasqyn-xgboost-1.1.1",
  "generated_at": "2024-03-01T09:12:44Z"
}

Core object

The Basin object

A basin is a river gauge Tasqyn can forecast. One basin — Petropavl on the Ishim (Esil) — is the anchor: the model is trained on its 1995–2024 record. The others are hydrologically similar snowmelt basins scored by transfer from live weather, and are flagged accordingly in every forecast.

AttributeType
idstringStable identifier, e.g. petropavl.
namestringGauge / town name.
riverstringRiver, e.g. Ishim (Esil).
countrystringISO country, KZ or RU.
latitude / longitudefloatPoint used to query Open-Meteo.
flood_seasonint[]Months when snowmelt flooding is plausible, e.g. [3,4,5].
is_anchorbooltrue only for Petropavl.
notesstringFree-text context.
Basin
{
  "id": "atbasar",
  "name": "Atbasar",
  "river": "Zhabay",
  "country": "KZ",
  "latitude": 51.8,
  "longitude": 68.33,
  "flood_season": [3, 4],
  "is_anchor": false,
  "notes": "Severe 2024 spring floods."
}
iThe six basins: petropavl, nur-sultan (Astana), atbasar, kokshetau, oral, kurgan.

Core object

The Observation object

One month of hydro-meteorological readings for a single basin. You supply a chronological list of these to /predict; the model derives lags, rolling windows and seasonal terms from the window, so at least 13 months is recommended. Any field may be null — missing river levels are imputed and flagged.

FieldType
yearintRequired. 1990–2100.
monthintRequired. 1–12.
river_level_meanfloat?Mean river level (cm).
river_level_maxfloat?Max river level (cm).
snow_coverfloat?Snow-cover index — the dominant signal.
soil_moisturefloat?Soil-moisture index.
temperaturefloat?Mean temperature (°C).
MonthlyObservation
{
  "year": 2024,
  "month": 3,
  "river_level_mean": 412.0,
  "river_level_max": 505.0,
  "snow_cover": 78.0,
  "soil_moisture": 0.34,
  "temperature": -4.2
}
GET/health

Liveness and readiness in one call. Reports whether the model artifacts are loaded, whether the isotonic calibrator is present, and how many features and basins are available. Returns status: "ok" when ready and "degraded" otherwise.

Returns

A HealthResponse with status, model_loaded, calibrated, model_version, n_features and n_basins.

Request
curl https://tasqyn-api.onrender.com/health
200 OK
{
  "status": "ok",
  "model_loaded": true,
  "calibrated": true,
  "model_version": "tasqyn-xgboost-1.1.1",
  "n_features": 44,
  "n_basins": 6
}
GET/basins

List every river basin Tasqyn can forecast. Use the returned id values with /forecast/live or as the optional basin_id on /predict.

Returns

An array of Basin objects.

Request
curl https://tasqyn-api.onrender.com/basins
200 OK
[
  {
    "id": "petropavl",
    "name": "Petropavl",
    "river": "Ishim (Esil)",
    "country": "KZ",
    "is_anchor": true,
    "flood_season": [3,4,5]
  },
  /* … 5 more basins … */
]
POST/predict

Score the most recent month in a supplied observation window. This is the low-level endpoint: you bring your own data, Tasqyn engineers the features, runs the model and returns one calibrated Forecast. The result is also appended to /history.

Body parameters

historyObservation[]required
Chronologically ordered observations. The last entry is the month scored; at least 13 months is recommended so lags and rolling windows are well-defined.
basin_idstringoptional
A basin id from /basins. Used to label the forecast and to set the reliability flags (anchor vs transfer). Returns 404 if unknown.

Returns

A single Forecast, or 503 if the model is not loaded, or 400 if the window cannot be scored.

Request
curl https://tasqyn-api.onrender.com/predict \
  -H "Content-Type: application/json" \
  -d '{
    "basin_id": "petropavl",
    "history": [
      { "year": 2023, "month": 4, "snow_cover": 12,
        "temperature": 6.1, "river_level_max": 430 },
      "… 12+ earlier months …",
      { "year": 2024, "month": 3, "snow_cover": 78,
        "temperature": -4.2, "river_level_max": 505 }
    ]
  }'
200 OK · Forecast
{
  "basin_id": "petropavl",
  "year": 2024, "month": 3,
  "flood_probability": 0.82,
  "risk": { "label": "high", "color": "#ef4444" },
  "is_flood_predicted": true,
  "provenance": "user-supplied",
  "model_version": "tasqyn-xgboost-1.1.1"
}
GET/forecast/live

The high-level endpoint, and the one most callers want. Give it a basin and a horizon; Tasqyn fetches live weather from the open Open-Meteo ERA5 archive (no key), builds the observation window for you, and returns a calibrated forecast for each of the next horizon months — each a full Forecast with a confidence interval.

Query parameters

basin_idstringdefault "petropavl"
A basin id from /basins. 404 if unknown.
horizonintdefault 3 · 1–6
How many months ahead to forecast.

Returns

A LiveForecastResponse: the resolved basin, the horizon, and a forecasts array. Returns 502 if live weather is unavailable. When the live window can't be filled from observations, missing months fall back to climatology and the forecast's provenance is climatology-filled.

Request
curl "https://tasqyn-api.onrender.com/forecast/live?\
basin_id=oral&horizon=3"
200 OK · LiveForecastResponse
{
  "basin": { "id": "oral", "river": "Ural (Zhayyq)" },
  "horizon": 3,
  "forecasts": [
    {
      "month": 4,
      "flood_probability": 0.71,
      "confidence_interval": { "lower": 0.4, "upper": 0.9 },
      "risk": { "label": "high" },
      "reliability": { "transfer": true }
    },
    /* … months 5, 6 … */
  ]
}
GET/history

Return the most recent forecasts the service has produced, newest first. The store is an in-memory ring buffer (the reference build keeps the last 200), so this is a convenience for the app rather than a system of record.

Query parameters

limitintdefault 20
Maximum number of items to return.

Returns

A HistoryResponse with items (each a Forecast plus an id) and a count.

Request
curl "https://tasqyn-api.onrender.com/history?limit=5"
200 OK
{
  "items": [
    { "id": "a3f1…", "basin_id": "oral",
      "flood_probability": 0.71 }
  ],
  "count": 1
}
DELETE/history

Clear the in-memory forecast history. Returns a small acknowledgement.

Returns

{ "cleared": true }.

Request
curl -X DELETE https://tasqyn-api.onrender.com/history
200 OK
{ "cleared": true }
POST/push/register

Register a device push token so Tasqyn can alert it when a basin's risk turns high. Tokens are stored by token value, so re-registering the same device is idempotent.

Body parameters

tokenstringrequired
An Expo, FCM or APNs push token.
basin_idstringoptional
Subscribe the device to a specific basin.
platformstringoptional
One of ios, android, web.

Returns

{ "registered": true, "total_devices": n }.

Request
curl https://tasqyn-api.onrender.com/push/register \
  -H "Content-Type: application/json" \
  -d '{
    "token": "ExponentPushToken[xxx]",
    "basin_id": "petropavl",
    "platform": "android"
  }'
200 OK
{ "registered": true, "total_devices": 1 }

The model

Model card

Tasqyn is a gradient-boosted decision-tree classifier (XGBoost) trained on the Petropavl record of the Ishim (Esil), 1995–2024. It predicts whether a given month will flood from leakage-free engineered features: lags, rolling statistics, first differences and cyclical month encoding over five raw monthly inputs (river level mean/max, snow cover, soil moisture, temperature). Rolling windows are shifted by a month so a month can never peek at itself — a property a dedicated test locks in.

We report the honest score, not the flattering one. On a naive split the model reaches ROC-AUC 1.00, which is meaningless on so few events; we keep it only as a cautionary number. The headline figures below come from leave-one-year-out walk-forward validation, where the model is retrained for each held-out year and never sees the future.

MetricValue
ROC-AUC0.91Walk-forward, leave-one-year-out (2018–2024).
PR-AUC0.72Fairer than ROC at a ~17% base rate.
Recall10 / 14Floods caught across held-out years.
False alarms1 / 70Non-flood months wrongly flagged.
F1 (optimal)0.80At the tuned decision threshold.
Top feature0.53snow_cover_lag4 — snowpack 4 months before the flood.

That top feature is the result we trust most: with no hydrology coded in, the model independently rediscovered that a spring flood depends most on the snow that fell about four months earlier — the textbook snowmelt mechanism.

Honest metrics · walk-forward
{
  "protocol": "leave-one-year-out walk-forward",
  "held_out_years": [2018, , 2024],
  "n_samples": 84,
  "n_floods": 14,
  "base_rate": 0.167,
  "roc_auc": 0.914,
  "pr_auc": 0.717,
  "f1_at_optimal": 0.80,
  "confusion_matrix": [[69, 1], [4, 10]]
}
iFigures, ROC/PR curves and the confusion matrix are on the overview page.

The model

Risk bands

Every forecast carries a human-facing risk band derived from the calibrated probability, so a UI can colour it without re-implementing thresholds. Bands are inclusive of their lower bound.

LabelProbabilityColor
low< 0.33#22c55e green
moderate0.33 – < 0.66#f59e0b amber
high≥ 0.66#ef4444 red
!The band is for display. The boolean is_flood_predicted uses the separate threshold field, which a caller can tune to trade recall against false alarms.
risk
{
  "label": "high",
  "color": "#ef4444"
}

The model

Reliability & calibration

Probabilities are calibrated with isotonic regression fitted on out-of-sample scores, and the 90% interval comes from a 15-model bootstrap ensemble — so the numbers mean what they say and carry their own uncertainty.

Because the model is trained on one anchor basin, Tasqyn is explicit about when a forecast is a stretch. Every forecast carries a reliability object you should surface to users.

FieldType
in_distributionbooltrue when inputs are in range and it's the anchor basin.
transferbooltrue for non-anchor basins scored by transfer — treat as indicative.
out_of_range_featuresstring[]Inputs beyond the training range; non-empty means extrapolation.
river_level_imputedbooltrue when missing river levels were filled in.
notestringPlain-language summary, ready to show.
reliability · transfer basin
{
  "in_distribution": false,
  "transfer": true,
  "out_of_range_features": ["snow_cover"],
  "river_level_imputed": true,
  "note": "transfer basin — indicative only"
}
iCalibration status is reported live at /health via the calibrated flag.