Introducing the AQUA-SMART Public API, Black Sea Data for Your App
AQUA-SMART Product Team, e-AQUAF Project (BSB01009)

Why a Public API?
AQUA-SMART has spent two years building a live observatory of the Black Sea: in-situ buoys in Romania, Bulgaria and Georgia streaming dissolved oxygen, pH, salinity and temperature; satellite layers from Copernicus Marine and NASA PACE for sea surface temperature, chlorophyll-a, salinity and currents; FAO FishStat production baselines for seven Black Sea countries; AIS vessel tracks; eddy and bloom event histories.
We get the same question every week from researchers, app developers and aquaculture operators: "Can I pull this into my own dashboard / model / mobile app?"
The answer is now yes. The AQUA-SMART Public REST API (v1) exposes the platform's live and historical data as plain JSON, behind a simple API key. This guide is everything you need to start.
Get access: at the bottom of this article (and on every page of the docs) is a Request API access button. Fill in the short form, we review by hand, and you get a personal key by email, usually within 2 business days. No credit card, no plan to pick, no surprise bills. The API is free for non-commercial and commercial use under CC BY 4.0 attribution.
What's in the API
The v1 surface covers everything the dashboard reads:
| Endpoint | What it returns |
|---|---|
GET /v1/stations | List of monitoring stations with coordinates and metadata |
GET /v1/stations/:id | Full station record + most-recent observation |
GET /v1/stations/:id/observations | Time-series of in-situ readings (DO, pH, salinity, SST) with optional from/to and limit |
GET /v1/stations/:id/indicators | Daily indicators: WQI, thermal anomaly, bloom risk, DO mg/L, DO saturation %, SSH, eddy flag, health score (1–5) |
GET /v1/stations/snapshot | NEW, One-shot latest snapshot for every station (WQI, mortality risk, bloom %, DO sat, eddy/bloom watch, health 1–5) |
GET /v1/stations/:id/feeding-guide | NEW, Ration %, timing and rules based on DO supersaturation, NH₃ pH threshold, and SST envelopes |
GET /v1/stations/:id/diver-safety | NEW, Safe/caution/unsafe rating from waves, currents, Kd490 visibility, temperature, jellyfish risk |
GET /v1/stations/:id/bloom-events | NEW, Detected chl-a bloom events (≥3 µg/L for ≥3 days) with eddy lag and WQI drop |
GET /v1/stations/daily-story | NEW, Today's AI-written Black Sea ocean narrative with WQI snapshot |
POST /v1/push-tokens · GET/PUT /v1/push-tokens/preferences · DELETE /v1/push-tokens | NEW, Mobile push notification registration & preferences (user JWT, not API key) |
POST /v1/citizen-science · GET /v1/citizen-science/my | NEW, Submit and list field observations with optional photo upload (user JWT) |
GET /v1/organizations | Aquaculture organizations in the network |
GET /v1/projects | Active and completed projects |
GET /v1/openapi.json | Machine-readable OpenAPI 3.0 schema |
Two auth modes. Public/read endpoints use
X-API-Key. The four push-token routes and the two citizen-science routes use a Supabase user JWT inAuthorization: Bearer <token>so each request is bound to the logged-in mobile-app user.
Every response is JSON. Pagination is cursor-based via limit + cursor query parameters where applicable. Timestamps are ISO-8601 UTC. Coordinates are WGS84 (longitude, latitude in decimal degrees).
Quick Start
Once you have a key, a complete first call is one line:
curl -H "X-API-Key: aqf_YOUR_KEY_HERE" \
"https://nohfpjovcatvjbzebwus.supabase.co/functions/v1/public-api/v1/stations"
Response (abridged):
{
"data": [
{
"id": "ro-mangalia-01",
"name": "Mangalia Bay",
"country": "Romania",
"latitude": 43.811,
"longitude": 28.583,
"active": true
},
{ "id": "bg-burgas-01", "name": "Burgas Bay", "country": "Bulgaria", ... },
{ "id": "ge-batumi-01", "name": "Batumi", "country": "Georgia", ... }
],
"meta": { "count": 3 }
}
That's it. You're talking to the same data source that powers the live dashboard.
Authentication
Every request must include your API key in the X-API-Key HTTP header:
X-API-Key: aqf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- Never put your key in client-side code (browser JS, mobile app binaries, GitHub repos). Anyone who sees it can use your quota. Always call our API from your own backend or a proxy you control.
- Keys can be rotated. Email us if you want a fresh key, old ones are revoked instantly.
- Keys are personal. Don't share them across teams; request one per integration.
- The API is read-only. You cannot modify data through it.
If your key is missing, malformed, expired or revoked you get a 401 Unauthorized with a JSON error body explaining what's wrong.
Base URL & Versioning
https://nohfpjovcatvjbzebwus.supabase.co/functions/v1/public-api
The version is part of the path (/v1/...). Breaking changes ship as a new major version (/v2/...) with at least 6 months of overlap. Additive, non-breaking changes (new fields, new endpoints) ship to /v1/ continuously. Subscribe to release notes by emailing info@eaquaf.eu.
Endpoint Reference
Stations
GET /v1/stations
GET /v1/stations/:id
Returns the list of monitoring buoys with location, country, and metadata. Valid station_id values today are ro (Danube Delta), bg (Varna) and ge (Batumi).
Observations (time-series)
GET /v1/stations/:id/observations?from=2026-05-01&to=2026-05-31&limit=500
Returns raw readings as { observed_at, parameter, value, unit, source, depth_m }, parameters include sst, oxygen, ph, salinity, chlorophyll, ssh, waves, currents_u/v, nitrate, kd490. Filter with ?parameter=sst. Default limit is 500, max 1000. Records are sorted newest-first.
Indicators (derived metrics)
GET /v1/stations/:id/indicators?from=2026-05-01&to=2026-05-31
Daily computed rows: observed_on, wqi (0–100), thermal_anomaly, bloom_risk, do_mgl, do_saturation_pct (García & Gordon 1992), ssh_cm, eddy_active and a 1–5 health_score.
Organizations & projects
GET /v1/organizations
GET /v1/projects
Reference data for the Black Sea aquaculture network, useful if you're building tools for matchmaking, supply-chain mapping or reporting.
OpenAPI
GET /v1/openapi.json
The full machine-readable spec. Use it with Postman, Insomnia, or generators like openapi-generator / swagger-codegen to scaffold typed clients in TypeScript, Python, Go, Java, Swift, Kotlin, etc.
v1.1, Mobile-ready endpoints
The latest release adds eight endpoints designed for mobile apps and decision-support widgets, plus six user-scoped routes (push tokens + citizen science) authenticated with a Cloud JWT instead of an API key.
# One-shot snapshot for every station, WQI, mortality risk, eddy/bloom flags, health 1–5
curl -H "X-API-Key: $KEY" "https://nohfpjovcatvjbzebwus.supabase.co/functions/v1/public-api/v1/stations/snapshot"
# Today's AI-written Black Sea narrative (cached daily)
curl -H "X-API-Key: $KEY" "https://nohfpjovcatvjbzebwus.supabase.co/functions/v1/public-api/v1/stations/daily-story"
# Aquaculture decision-support
curl -H "X-API-Key: $KEY" "https://nohfpjovcatvjbzebwus.supabase.co/functions/v1/public-api/v1/stations/ro/feeding-guide"
curl -H "X-API-Key: $KEY" "https://nohfpjovcatvjbzebwus.supabase.co/functions/v1/public-api/v1/stations/bg/diver-safety"
curl -H "X-API-Key: $KEY" "https://nohfpjovcatvjbzebwus.supabase.co/functions/v1/public-api/v1/stations/bg/bloom-events?year=2026"
User-scoped routes (Authorization: Bearer JWT):
# Register a mobile push token
curl -X POST "https://nohfpjovcatvjbzebwus.supabase.co/functions/v1/public-api/v1/push-tokens" \
-H "Authorization: Bearer $USER_JWT" \
-H "Content-Type: application/json" \
-d '{ "fcm_token": "abc...", "platform": "android" }'
# Submit a citizen-science field observation (optional photo as base64)
curl -X POST "https://nohfpjovcatvjbzebwus.supabase.co/functions/v1/public-api/v1/citizen-science" \
-H "Authorization: Bearer $USER_JWT" \
-H "Content-Type: application/json" \
-d '{ "station_id": "ge", "water_colour": "brown-green", "foam": true, "visibility_cm": 35 }'
The /v1/stations/:id/indicators payload was extended in the same release: every daily row now also carries do_mgl, do_saturation_pct (García & Gordon 1992), ssh_cm, eddy_active and health_score (1–5) so a mobile sparkline has everything it needs in one call.
Changelog
| Date | Version | What changed |
|---|---|---|
| June 2026 | v1.1 | 8 new endpoints, snapshot, feeding-guide, diver-safety, bloom-events, daily-story, push-tokens (POST/GET/PUT/DELETE), citizen-science (POST/GET). Added do_mgl, do_saturation_pct, eddy_active, health_score to indicators. Introduced mobile-app JWT auth for user-scoped routes. |
| May 2026 | v1.0 | Initial release, stations, observations, indicators, organizations, projects, OpenAPI spec. API-key auth only. |
Code Samples
JavaScript / Node
const res = await fetch(
"https://nohfpjovcatvjbzebwus.supabase.co/functions/v1/public-api/v1/stations/ro/observations?parameter=oxygen&limit=24",
{ headers: { "X-API-Key": process.env.AQUASMART_API_KEY } }
);
if (!res.ok) throw new Error(`API error ${res.status}`);
const { data } = await res.json();
console.log("Latest DO:", data[0].value, data[0].unit);
Python (requests)
import os, requests
BASE = "https://nohfpjovcatvjbzebwus.supabase.co/functions/v1/public-api"
HEADERS = {"X-API-Key": os.environ["AQUASMART_API_KEY"]}
r = requests.get(f"{BASE}/v1/stations/ro/observations",
headers=HEADERS, params={"parameter": "oxygen", "limit": 24})
r.raise_for_status()
for row in r.json()["data"]:
print(row["observed_at"], row["value"], row["unit"])
R (httr)
library(httr); library(jsonlite)
res <- GET(
paste0("https://nohfpjovcatvjbzebwus.supabase.co/functions/v1/public-api/v1/stations/ro/indicators"),
query = list(from = "2026-05-01", to = "2026-05-31"),
add_headers(`X-API-Key` = Sys.getenv("AQUASMART_API_KEY"))
)
stop_for_status(res)
content(res, "parsed")
Rate Limits
To keep the platform healthy we apply two rolling-window limits, per API key:
| Window | Default limit |
|---|---|
| Short burst | 60 requests / minute |
| Sustained | 5,000 requests / day |
Every response carries the standard headers X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. When you exceed the limit you get 429 Too Many Requests with a Retry-After header indicating how many seconds to wait.
If your project genuinely needs more throughput (e.g. an academic backfill, a public-facing app), just say so in the access request, we lift caps case-by-case for free.
Error Codes
| Status | Meaning | What to do |
|---|---|---|
200 | Success | , |
400 | Bad request, malformed parameters | Check the JSON details field |
401 | Missing / invalid API key | Verify the X-API-Key header |
403 | Revoked or scope-denied key | Request a new key |
404 | Resource not found | Double-check the station/project ID |
429 | Rate limit exceeded | Honor Retry-After and back off |
500 | Internal error | Retry with exponential backoff; report if persistent |
All error responses share the shape:
{ "error": "rate_limited", "message": "Try again in 42 seconds.", "details": {} }
Data Licensing & Citation
All data exposed through the API is published under Creative Commons Attribution 4.0 (CC BY 4.0). You're free to use it commercially, derivative works are encouraged, redistribution is allowed, provided you attribute correctly:
Source: AQUA-SMART Black Sea Observatory, e-AQUAF Project (BSB01009), co-funded by the Interreg NEXT Black Sea Basin Programme.
For scientific papers, please add the platform URL: https://eaquaf.eu.
Reliability & Status
The API runs on the same managed infrastructure as the dashboard, with continuous health monitoring (in-situ ingest, cron jobs, alerting). Planned maintenance is communicated by email to all key holders at least 48 hours in advance. There is no public status page yet, if you depend on the API in production, write to info@eaquaf.eu and we'll add you to the operational notifications list.
What's Next
A few things on the near-term roadmap:
- WebSocket / SSE streams for live observation push (today you poll)
- Per-user JWT auth so mobile apps can authenticate end-users directly (today only server-to-server keys)
- Forecast endpoints exposing the same 48-hour mortality risk and harvest-window models the dashboard uses
- GraphQL gateway for richer, single-round-trip queries
If any of these are blockers for your project, tell us in the access request, that's how we prioritise.
Get Started
Click Request API access below. We'll review and email your key. You can also download this whole guide as a branded PDF to share with your team.
Request API access
Tell us about your project and we'll review your request and email your personal API key, usually within 2 business days. Free for academic and commercial use under CC BY 4.0.
