← Back to API Docs

FlowFabric API - Getting Started

Everything you need to understand and use FlowFabric in 10 minutes.


What is FlowFabric?

FlowFabric is a high-performance REST API for accessing National Water Model (NWM) forecasts, reanalysis data, and hydrologic analysis datasets. Built for hydrologic modeling, flood mapping, water resource planning, and real-time operational systems.

The Problem FlowFabric Solves

The FlowFabric Solution

For Users

Get streamflow predictions and historical flows for any catchment in <8 seconds. No data downloads, no processing delays—just efficient streaming data queries.

For Teams

Production-grade infrastructure with authentication, metering, cost estimation, and sub-second query performance. Designed for internal tools, dashboards, and enterprise applications. 2-3x faster than competitors, 50-70% cheaper per query.


Real-World Use Cases

1. Flood Forecasting Dashboards

Scenario: A water utility needs to update forecast streamflow on a dashboard every 30 minutes.

FlowFabric Solution:

# Update 50 critical gages in <500ms
import httpx

gages = [101, 1001, 10001, 100001, 1000001]  # 5 critical locations
response = httpx.post(
    "https://flowfabric-api.lynker-spatial.com/v1/datasets/nwm-forecast/streamflow",
    headers={"Authorization": f"Bearer {TOKEN}"},
    json={
        "issue_time": "latest",
        "scope": "features",
        "feature_ids": gages
    }
).json()

# Render on dashboard immediately (not waiting for downloads)

Cost: ~$0.0001 per query (50 locations) Latency: 150-400ms total (API + network) Alternative (NOAA API): 30-45 seconds, free but too slow for dashboards


2. Hydrologic Model Parameterization

Scenario: Research team running 10,000 model simulations needs historical streamflow for calibration.

FlowFabric Solution:

# Export 10 years of data for 100 features to S3 (processed offline)
estimate = httpx.post(
    "https://flowfabric-api.lynker-spatial.com/v1/datasets/nwm-reanalysis/streamflow?estimate=true",
    headers={"Authorization": f"Bearer {TOKEN}"},
    json={
        "start_time": "2013-01-01",
        "end_time": "2023-12-31",
        "scope": "features",
        "feature_ids": ["8318793", "8318787", "8318775", "8318785", "8318789", "8318801", "8318819", "8318813", "8318825", "8318841", "8318835", "8318829", "8318827", "8318843", "8318847", "8318851", "8318845", "8318849", "8318861", "8318859", "8318853", "8318855", "8318857", "8318867", "8318863", "8318865", "8318869", "8318871", "8318873", "8318879", "8318877", "8318881", "8318883", "8318885", "8318887", "8318889", "8318891", "8318893", "8318895", "8318897", "8318899", "8318901", "8318903", "8318905", "8318907", "8318909", "8318911", "8318913", "8318915", "8318917"]
    }
).json()

print(f"Cost: ${estimate['estimated_cost']} | Size: {estimate['estimated_gb']}GB")

# Export (background job)
export = httpx.post(
    "https://flowfabric-api.lynker-spatial.com/v1/datasets/nwm-reanalysis/streamflow",
    headers={"Authorization": f"Bearer {TOKEN}"},
    json={
        "start_time": "2013-01-01",
        "end_time": "2023-12-31",
        "scope": "features",
        "feature_ids": ["8318793", "8318787", "8318775", "8318785", "8318789", "8318801", "8318819", "8318813", "8318825", "8318841", "8318835", "8318829", "8318827", "8318843", "8318847", "8318851", "8318845", "8318849", "8318861", "8318859", "8318853", "8318855", "8318857", "8318867", "8318863", "8318865", "8318869", "8318871", "8318873", "8318879", "8318877", "8318881", "8318883", "8318885", "8318887", "8318889", "8318891", "8318893", "8318895", "8318897", "8318899", "8318901", "8318903", "8318905", "8318907", "8318909", "8318911", "8318913", "8318915", "8318917"],
        "mode": "export"
    }
).json()

print(f"Download at: {export['download_url']}")  # S3 link ready in 30-60s

Cost: ~$0.50-1.00 (100 features × 10 years) Time: 30-60 seconds to generate + background download Alternative (Manual BigQuery): ~$5-10 in compute costs, requires SQL knowledge, 2-3 hours setup


3. Real-Time Rating Curve Queries

Scenario: NWS office needs current water surface elevation for 500 stream gages.

FlowFabric Solution:

# Get latest stage and translate to elevation
response = httpx.post(
    "https://flowfabric-api.lynker-spatial.com/v1/datasets/nwm-analysis/stage:query",
    headers={"Authorization": f"Bearer {TOKEN}"},
    json={
        "issue_time": "latest",
        "scope": "features",
        "feature_ids": ["8318793", "8318787", "8318775", "8318785", "8318789", "8318801", "8318819", "8318813", "8318825", "8318841"]  # 10 valid NHM reaches
    }
)

df = pd.read_parquet(io.BytesIO(response.content))
print(df.head())
#    feature_id  timestamp  stage
# 0         1   2026-01-09  0.75
# 1        10   2026-01-09  1.21
# ...

Performance: 250-600ms for 500 gages Cost: ~$0.001 per query Alternative (NOAA API): 45-90 seconds per gage (would need 450+ API calls), rate-limited


Key Concepts

Datasets

All available datasets are listed dynamically in the API. Browse the full catalog:

curl https://flowfabric-api.lynker-spatial.com/v1/datasets

Available Datasets:

Each supports streamflow queries, historical analysis, and export modes.

Data Format

All responses use Arrow IPC (columnar binary format): - 50-70% smaller than JSON for same data (binary encoding) - Zero-copy deserialization - instant load in Python, R, JavaScript - Perfect for big data - works seamlessly with pandas, polars, DuckDB - Streaming support - read data while still downloading

Performance comparison (1,000 streamflow records): - Arrow IPC: 45KB, 1ms to deserialize - JSON: 120KB, 50ms to deserialize - CSV: 95KB, 150ms to parse

Authentication

Sign in via SSO at https://flowfabric-api.lynker-spatial.com/oauth/login — your session is set automatically. For programmatic access, self-issue an API key at POST /v1/me/keys. All requests require:

Authorization: Bearer <your-token>

Quick Start (5 minutes)

1. Sign In

Go to https://flowfabric-api.lynker-spatial.com/oauth/login and authenticate with your Lynker Spatial account. You'll be redirected back to the docs with your session active.

For scripts or CI, self-issue an API key: POST /v1/me/keys (requires an active SSO session).

2. Try the Interactive Docs

Open https://flowfabric-api.lynker-spatial.com/docs — if you signed in via SSO you're already authenticated. To use a Bearer token directly, click Authorize and paste it.

3. First Query (Python & R)

Python:

import httpx
import pyarrow.ipc as ipc
import io

TOKEN = "your-token-here"

# Estimate query cost
estimate = httpx.post(
  "https://flowfabric-api.lynker-spatial.com/v1/datasets/nwm/forecast/streamflow?estimate=true",
    headers={"Authorization": f"Bearer {TOKEN}"},
    json={
        "issue_time": "2026010618",
        "scope": "features",
        "feature_ids": ["101", "1001"]
    }
).json()

print(f"Query will return {estimate['estimated_rows']} rows, {estimate['estimated_bytes']} bytes")

# Execute query
response = httpx.post(
    "https://flowfabric-api.lynker-spatial.com/v1/datasets/nwm/forecast/streamflow",
    headers={"Authorization": f"Bearer {TOKEN}"},
    json={
        "issue_time": "2026010618",
        "scope": "features",
        "feature_ids": ["101", "1001"],
        "mode": "sync"
    }
)

# Parse Arrow response
reader = ipc.open_stream(io.BytesIO(response.content))
df = reader.read_pandas()
print(df.head())

R:

library(httr)
library(arrow)

TOKEN <- "your-token-here"

# Estimate query cost
estimate <- POST(
  "https://flowfabric-api.lynker-spatial.com/v1/datasets/nwm/forecast/streamflow?estimate=true",
  add_headers(Authorization = paste("Bearer", TOKEN)),
  body = list(
    issue_time = "2026010618",
    scope = "features",
    feature_ids = c("8318793", "8318787")
  ),
  encode = "json"
)

estimate_json <- content(estimate, as = "parsed")
cat(sprintf("Query will return %d rows, %d bytes\n", 
            estimate_json$estimated_rows, 
            estimate_json$estimated_bytes))

# Execute query
response <- POST(
  "https://flowfabric-api.lynker-spatial.com/v1/datasets/nwm/forecast/streamflow",
  add_headers(Authorization = paste("Bearer", TOKEN)),
  body = list(
    issue_time = "2026010618",
    scope = "features",
    feature_ids = c("8318793", "8318787"),
    mode = "sync"
  ),
  encode = "json"
)

# Parse Arrow response
df <- read_ipc(response$content)
head(df)

4. Next Steps


Common Tasks

Query a Specific Stream Gauge

Python:

# Get latest forecast for USGS gauge 01022500
response = httpx.post(
    "https://flowfabric-api.lynker-spatial.com/v1/datasets/nwm/forecast/streamflow",
    headers={"Authorization": f"Bearer {TOKEN}"},
    json={
        "issue_time": "latest",
        "scope": "features",
        "feature_ids": ["8318793"],  # NHM feature ID
        "mode": "sync"
    }
)

R:

response <- POST(
  "https://flowfabric-api.lynker-spatial.com/v1/datasets/nwm/forecast/streamflow",
  add_headers(Authorization = paste("Bearer", TOKEN)),
  body = list(
    issue_time = "latest",
    scope = "features",
    feature_ids = c("8318793"),  # NHM feature ID
    mode = "sync"
  ),
  encode = "json"
)
df <- read_ipc(response$content)

Export Large Query

Python:

response = httpx.post(
    "https://flowfabric-api.lynker-spatial.com/v1/datasets/nwm/reanalysis/streamflow",
    headers={"Authorization": f"Bearer {TOKEN}"},
    json={
        "start_time": "2010-01-01",
        "end_time": "2020-12-31",
        "scope": "catchments",
        "region": "conus",
        "mode": "export"  # Returns S3 download link instead of streaming
    }
)

export_result = response.json()
print(f"Download at: {export_result['download_url']}")

R:

response <- POST(
  "https://flowfabric-api.lynker-spatial.com/v1/datasets/nwm/reanalysis/streamflow",
  add_headers(Authorization = paste("Bearer", TOKEN)),
  body = list(
    start_time = "2010-01-01",
    end_time = "2020-12-31",
    scope = "catchments",
    region = "conus",
    mode = "export"
  ),
  encode = "json"
)

export_result <- content(response, as = "parsed")
cat(sprintf("Download at: %s\n", export_result$download_url))

Estimate Cost Before Querying

Python:

estimate = httpx.post(
  "https://flowfabric-api.lynker-spatial.com/v1/datasets/nwm/reanalysis/streamflow?estimate=true",
    headers={"Authorization": f"Bearer {TOKEN}"},
    json={
        "start_time": "2000-01-01",
        "end_time": "2023-12-31",
        "scope": "region",
        "region": "conus"
    }
).json()

print(f"Cost: ${estimate['estimated_cost']}")
print(f"Recommended mode: {estimate['recommended_mode']}")  # sync or export

R:

estimate <- POST(
  "https://flowfabric-api.lynker-spatial.com/v1/datasets/nwm/reanalysis/streamflow?estimate=true",
  add_headers(Authorization = paste("Bearer", TOKEN)),
  body = list(
    start_time = "2000-01-01",
    end_time = "2023-12-31",
    scope = "region",
    region = "conus"
  ),
  encode = "json"
)

estimate_json <- content(estimate, as = "parsed")
cat(sprintf("Cost: $%s\n", estimate_json$estimated_cost))
cat(sprintf("Recommended mode: %s\n", estimate_json$recommended_mode))

Troubleshooting

Problem Solution
401 Unauthorized Token expired or invalid. Sign in again at https://flowfabric-api.lynker-spatial.com/oauth/login or re-issue an API key at POST /v1/me/keys
400 Bad Request Check dataset_id, feature_ids, and date formats. Use /docs to validate payloads.
503 Service Unavailable Catalog service down. Check https://status.lynker-spatial.com
Query exceeds limits Use mode: export or split into smaller requests. See rate limits below.

Limits & Pricing

Query size limits and pricing vary by dataset and plan. Before executing large queries:

  1. Always estimate first: Use the ?estimate=true query parameter to see query size and cost
  2. Check limits: Docs at https://flowfabric-api.lynker-spatial.com/docs show mode recommendations
  3. Contact support: For bulk exports or custom arrangements
# Check estimated cost and recommended mode
estimate = httpx.post(
  "https://flowfabric-api.lynker-spatial.com/v1/datasets/{dataset}/streamflow?estimate=true",
    headers={"Authorization": f"Bearer {TOKEN}"},
    json={...}  # your query parameters
).json()

print(f"Estimated cost: {estimate.get('cost')}")
print(f"Recommended mode: {estimate.get('recommended_mode')}")

For Developers: Next Steps

Local Development

  1. Clone repo: git clone https://github.com/lynker-spatial/flowfabric-api
  2. Install: pip install -r requirements.txt
  3. Configure: cp .env.example .env
  4. Run: uvicorn app.main:app --reload
  5. Docs: http://localhost:8000/docs

Architecture & Contributing

The repo README covers how to add new datasets, create adapters, add endpoints, and run the test suite.


Support