Quick Start
Get up and running in 3 steps:
1. Get your API key
Sign up to get your API key instantly. Then purchase credits or redeem a voucher to get started.
2. Make your first query
Query NO₂ air quality data for Paris, January 2023:
curl "https://api.jiskta.com/api/v1/climate/query?\ lat_min=48.5&lat_max=49.2&\ lon_min=2.0&lon_max=2.8&\ time_start=2023-01-01&time_end=2023-02-01&\ variables=no2" \ -H "X-API-Key: sk_live_your_key_here"
3. Parse the response
{
"status": "success",
"query_time_ms": 18,
"tiles_scanned": 2,
"credits_used": 2,
"credits_remaining": 198,
"output": "Rows matched: 18200\nMin: 1.23\nMax: 42.5\nAverage: 9.87\n"
} Add format=csv to get raw hourly data instead:
{
"status": "success",
"query_time_ms": 22,
"tiles_scanned": 2,
"credits_used": 2,
"credits_remaining": 196,
"format": "csv",
"aggregate": "hourly",
"output": "lat,lon,time,no2\n48.6500,2.1500,2023-01-01T00:00:00Z,12.34\n48.6500,2.1500,2023-01-01T01:00:00Z,11.87\n..."
} Authentication
All API requests require an API key. You can pass it in two ways:
Header (recommended)
X-API-Key: sk_live_your_key_here
Query parameter
GET /api/v1/climate/query?api_key=sk_live_your_key_here&...
Security note: API keys are hashed with SHA-256 before storage. We never store your raw key.
Credits
Each query costs credits based on the data it touches:
credits = tiles × time_months × variables
| Factor | Description | Example |
|---|---|---|
tiles | Number of 5°×5° data tiles that cover your query area and exist on disk. Only tiles with actual data count — ocean / uncovered areas are free. | Paris: 2 tiles, Netherlands: 2 tiles, Belgium: 4 tiles |
time_months | Number of calendar months in your time range | Jan–Mar 2023: 3 months |
variables | Number of CAMS/ERA5 variables queried | no2,t2m: 2 variables |
Use dry_run=true to check the exact credit cost before executing a query — it returns the tile count and cost without deducting anything.
Tile boundaries and country size
Data is stored in 5°×5° latitude/longitude tiles. A country that straddles a 5° boundary uses more tiles than its geographic size alone would suggest. For example, Belgium (lat 49.5°–51.6°) crosses the 50° parallel, so it spans two tile rows (lat 45–50 and lat 50–55) × two tile columns = 4 tiles, while the Netherlands (lat 51°–53.7° mainland) fits within a single tile row = 2 tiles.
For named areas with overseas territories (e.g. France, Netherlands), the API automatically uses polygon-based filtering so you only pay for tiles that actually intersect the area's boundaries — not for empty ocean tiles within the bounding box.
Your remaining balance is returned in every response as credits_remaining. If you don't have enough credits, the API returns 402 Payment Required.
Query Endpoint
GET /api/v1/climate/query
Query climate data for a geographic region and time range.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
lat_min | float | required* | Minimum latitude (-90 to 90). *Not required when using lat/lon point shorthand. |
lat_max | float | required* | Maximum latitude (-90 to 90) |
lon_min | float | required* | Minimum longitude (-180 to 180) |
lon_max | float | required* | Maximum longitude (-180 to 180) |
lat | float | optional | Point query latitude. Use with lon instead of bbox params. Snaps to the nearest 0.1° grid point. See Point Query. |
lon | float | optional | Point query longitude. Use with lat. |
time_start | string | required | Start time. Accepts YYYY-MM-DD, YYYY-MM, YYYY-MM-DDTHH:MM:SSZ, or Unix timestamp (seconds) |
time_end | string | required | End time. Same formats as time_start. Both bounds are inclusive. |
pollutants | string | optional | Comma-separated list of variables to query. Default: no2.CAMS (0.1°): no2, pm2p5, pm10, o3ERA5 (0.25°): t2m, tp, blh, u10, v10Derived ERA5: wind_speed, wind_dir — computed from u10+v10ODIAC (0.01°): odiac_co2 — fossil-fuel CO₂ in gC/m²/month, global, 2020–2023CAMS GHG Inversion (1°): ghg_co2 — CO₂ surface flux kgC/m²/month; ghg_ch4 — CH₄ flux kg/m²/s; ghg_n2o — N₂O flux kgN/m²/month. Global, monthly, 2016–present (~3 month lag). Aliases: atmospheric_co2, atmospheric_ch4, methaneMODIS (0.005°): modis_urban — urban fraction annual; modis_burned_area — monthly wildfire burned fraction, 2020–presentMix freely in one request: variables=no2,t2m,blh. See ERA5 Variables. |
format | string | optional | stats (default) — summary statistics. csv — data rows. geojson — GeoJSON FeatureCollection. ndjson — one JSON object per line. See Output Formats. |
aggregate | string | optional | Controls time/space aggregation. Per-point: hourly (default), daily, monthly, annual, seasonal, trend, stddev, max, min, cumulative. Spatial averages: area_hourly, area_daily, area_monthly, diurnal. See Output Formats. |
threshold | float | optional | µg/m³ threshold for exceedance analysis. When set, returns hours-above-threshold per grid point instead of values. See Exceedance. |
percentile | float | optional | Percentile to compute (1–100) per grid point over the queried period. E.g. percentile=95 for the 95th percentile. See Percentile. |
round | integer | optional | Decimal places for output values: 0–6. Default is 4. |
unit | string | optional | ppb — convert gas concentrations from µg/m³ to parts-per-billion (no2 ×0.5226, o3 ×0.5013). Particulates and ERA5 variables are unaffected. |
no_header | boolean | optional | true — omit the CSV header row. Useful when appending to an existing file. |
missing | string | optional | null — emit rows with an empty value field for time/space cells that have no data, instead of skipping them. |
dry_run | boolean | optional | true — return credit cost and estimated row count without executing the query. Does not deduct credits. |
area | string | optional | Named area shortcut with polygon boundary masking. E.g. area=paris, area=belgium, area=france. Replaces explicit lat_min/lat_max/lon_min/lon_max and applies polygon filtering for CSV output — only grid cells inside the real boundary are returned. Accepts bbox= for backward compatibility. See Named Areas & Polygon Masking. |
sort_by | string | optional | Column name to sort CSV/ndjson output by. E.g. sort_by=no2_mean, sort_by=lat. Unknown column names are ignored. See Sort Output. |
sort_dir | string | optional | asc (default) or desc. Controls sort direction when sort_by is set. |
compress | string | optional | gzip — compress the response with gzip. Sets Content-Encoding: gzip. Most HTTP clients decompress automatically. See Gzip Compression. |
facility | string | optional | E-PRTR inspire_hash ID. Pins the query to the verified GPS coordinates of a registered EU industrial facility — replaces lat/lon or bbox. Use GET /api/v1/facilities to look up IDs. Enables return_divergence when combined with aggregate=trend. |
return_divergence | boolean | optional | Requires facility + aggregate=trend. When true, compares the satellite-derived CAMS NO₂ trend against the facility's E-PRTR self-reported NOₓ emissions trend. Returns a divergence object in the response with direction (consistent, divergent, or strongly_divergent), score, cams_no2_slope (µg/m³/yr), and eprtr_nox_slope (tonnes/yr). See Trend Analysis. |
Response
{
"status": "success",
"query_time_ms": 18,
"tiles_scanned": 2,
"credits_used": 2,
"credits_remaining": 6498,
"format": "csv", // present only for format=csv or threshold queries
"aggregate": "hourly", // present only for format=csv or threshold queries
"output": "..." // see Output Formats below
} The output field contains the query result. Its format depends on the format and aggregate parameters — see Output Formats below.
Output Formats
The format and aggregate parameters together control the shape of the output field. All modes cost the same credits — only the queried tile count matters.
| Mode | Parameters | Output rows | Use case |
|---|---|---|---|
| Stats | format=stats (default) | 4 scalar lines | Quick min / max / avg summary |
| Hourly | format=csv | 1 per grid point × hour | Raw time series, full resolution |
| Daily | format=csv&aggregate=daily | 1 per grid point × day | Day-by-day means |
| Monthly | format=csv&aggregate=monthly | 1 per grid point × month | Month-by-month means |
| Annual | format=csv&aggregate=annual | 1 per grid point × year | Year-over-year comparison |
| Seasonal | format=csv&aggregate=seasonal | 4 per grid point (DJF/MAM/JJA/SON) | Climatological season means |
| Trend | format=csv&aggregate=trend | 1 per grid point | OLS slope (value/year), R², n months |
| Std Dev | format=csv&aggregate=stddev | 1 per grid point | Temporal mean and sample standard deviation |
| Area hourly | aggregate=area_hourly | 1 per hour | Regional mean time series |
| Area daily | aggregate=area_daily | 1 per day | Regional daily trend |
| Area monthly | aggregate=area_monthly | 1 per month | Regional monthly trend |
| Diurnal | aggregate=diurnal | 24 (one per hour-of-day) | Typical daily cycle |
| Exceedance | threshold=N | 1 per grid point | Hours above a µg/m³ limit |
| Percentile | percentile=N | 1 per grid point | P50, P95, P98 … per location |
| GeoJSON | format=geojson | same rows as CSV | Map-ready output for Mapbox / Leaflet / QGIS |
| NDJSON | format=ndjson | same rows as CSV | One JSON object per line — streams into Python/pandas pipelines |
| Max | format=csv&aggregate=max | 1 per grid point | Temporal peak value (WHO compliance, extreme events) |
| Min | format=csv&aggregate=min | 1 per grid point | Temporal minimum value |
| Cumulative | format=csv&aggregate=cumulative | 1 per grid point × day | Running cumulative sum — ideal for precipitation totals (tp) |
format=stats (default)
Returns aggregate statistics over the entire query region and time range. Useful for quick summaries.
Rows matched: 18200 Min: 1.230000 Max: 42.500000 Average: 9.870000
format=csv & aggregate=hourly (default for csv)
Returns one row per grid point per hour. Time is ISO 8601 UTC.
curl "https://api.jiskta.com/api/v1/climate/query?\ lat_min=48.5&lat_max=49.0&lon_min=2.0&lon_max=2.5&\ time_start=2023-01-01&time_end=2023-01-02&\ variables=no2&format=csv&aggregate=hourly" \ -H "X-API-Key: sk_live_your_key_here"
lat,lon,time,no2 48.5500,2.0500,2023-01-01T00:00:00Z,8.32 48.5500,2.0500,2023-01-01T01:00:00Z,7.14 48.5500,2.1500,2023-01-01T00:00:00Z,9.01 ...
format=csv & aggregate=daily
Returns the daily mean per grid point. 24× fewer rows than hourly.
curl "https://api.jiskta.com/api/v1/climate/query?\ lat_min=48.5&lat_max=49.0&lon_min=2.0&lon_max=2.5&\ time_start=2023-01-01&time_end=2023-01-31&\ variables=no2&format=csv&aggregate=daily" \ -H "X-API-Key: sk_live_your_key_here"
lat,lon,date,no2_mean 48.5500,2.0500,2023-01-01,8.93 48.5500,2.0500,2023-01-02,7.65 48.5500,2.1500,2023-01-01,9.12 ...
format=csv & aggregate=monthly
Returns the monthly mean per grid point. Great for trend analysis across months.
curl "https://api.jiskta.com/api/v1/climate/query?\ lat_min=48.5&lat_max=49.0&lon_min=2.0&lon_max=2.5&\ time_start=2023-01&time_end=2023-12&\ variables=no2&format=csv&aggregate=monthly" \ -H "X-API-Key: sk_live_your_key_here"
lat,lon,year_month,no2_mean 48.5500,2.0500,2023-01,9.87 48.5500,2.0500,2023-02,8.34 48.5500,2.1500,2023-01,10.23 ...
Row limit
Queries that would return more than 500,000 rows are rejected with 400 Bad Request and a message suggesting to use aggregate=daily or aggregate=monthly, or to reduce the time range.
Point Query
Use lat and lon instead of the four bbox params to query a single grid point. The coordinates are automatically snapped to the nearest 0.1° grid point.
# Hourly NO₂ at the Eiffel Tower, January 2023 curl "https://api.jiskta.com/api/v1/climate/query?\ lat=48.8566&lon=2.3522&\ time_start=2023-01-01&time_end=2023-01-31&\ variables=no2&format=csv" \ -H "X-API-Key: sk_live_your_key_here"
lat,lon,time,no2 48.8500,2.3500,2023-01-01T00:00:00Z,12.34 48.8500,2.3500,2023-01-01T01:00:00Z,11.87 ...
Grid snapping: The data grid has cell centres at …, −0.05°, 0.05°, 0.15°, 0.25°, … The API snaps your coordinates to the nearest cell centre so you always get exactly one point back, regardless of how precisely you specify the coordinates.
Exceedance Analysis
Set threshold to a µg/m³ value to get a per-grid-point count of hours above that threshold over the queried period. Useful for air quality index calculations and health exposure studies.
# Hours of NO₂ above 25 µg/m³ in Paris, January 2023 curl "https://api.jiskta.com/api/v1/climate/query?\ lat_min=48.5&lat_max=49.0&lon_min=2.0&lon_max=2.5&\ time_start=2023-01-01&time_end=2023-01-31&\ variables=no2&threshold=25" \ -H "X-API-Key: sk_live_your_key_here"
lat,lon,hours_above,total_hours,pct_above 48.5500,2.0500,22,744,2.96 48.5500,2.1500,31,744,4.17 48.6500,2.0500,18,744,2.42 ...
| Column | Description |
|---|---|
hours_above | Number of hours the pollutant exceeded the threshold |
total_hours | Total hours in the queried period for this grid point |
pct_above | Percentage of hours above the threshold (0–100) |
Area Mean (Spatial Average)
Use aggregate=area_hourly, area_daily, or area_monthly to get a single spatially-averaged time series for the entire queried bounding box. Instead of one row per grid point, you get one row per time step — ideal for regional trend analysis.
# Daily spatial average for Paris bounding box, January 2023 curl "https://api.jiskta.com/api/v1/climate/query?\ lat_min=48.5&lat_max=49.0&lon_min=2.0&lon_max=2.5&\ time_start=2023-01-01&time_end=2023-01-31&\ variables=no2&format=csv&aggregate=area_daily" \ -H "X-API-Key: sk_live_your_key_here"
date,no2_mean 2023-01-01,9.41 2023-01-02,8.73 2023-01-03,11.20 ...
| aggregate value | Time column | Rows per pollutant |
|---|---|---|
area_hourly | time (ISO 8601) | 1 per hour |
area_daily | date (YYYY-MM-DD) | 1 per day |
area_monthly | year_month (YYYY-MM) | 1 per month |
Annual Aggregate
Use aggregate=annual with format=csv to get the annual mean per grid point. Useful for comparing year-over-year trends across a region.
# Annual mean per grid point, 2023 full year curl "https://api.jiskta.com/api/v1/climate/query?\ lat_min=48.5&lat_max=49.0&lon_min=2.0&lon_max=2.5&\ time_start=2023-01-01&time_end=2023-12-31&\ variables=no2&format=csv&aggregate=annual" \ -H "X-API-Key: sk_live_your_key_here"
lat,lon,year,no2_mean 48.5500,2.0500,2023,8.72 48.5500,2.1500,2023,9.10 48.6500,2.0500,2023,7.95 ...
Seasonal Aggregate
Use aggregate=seasonal with format=csv to get the climatological mean per grid point for each of the four meteorological seasons: DJF (Dec–Feb), MAM (Mar–May), JJA (Jun–Aug), SON (Sep–Nov). Seasons are computed from the full period in the query range.
# Seasonal NO₂ means for Paris, 2020–2023 curl "https://api.jiskta.com/api/v1/climate/query?\ lat_min=48.5&lat_max=49.0&lon_min=2.0&lon_max=2.5&\ time_start=2020-01-01&time_end=2023-12-31&\ variables=no2&format=csv&aggregate=seasonal" \ -H "X-API-Key: sk_live_your_key_here"
lat,lon,season,no2_mean 48.5500,2.0500,DJF,14.32 48.5500,2.0500,MAM,10.87 48.5500,2.0500,JJA,7.54 48.5500,2.0500,SON,12.16 48.6500,2.0500,DJF,13.91 ...
Trend Analysis
Use aggregate=trend with format=csv to compute a per-grid-point linear regression over the monthly means in your query range. Returns the OLS slope (value change per year), intercept, R², and n (number of months with data). Only cells with at least 3 data months are emitted.
# NO₂ long-term trend for Paris, 2015–2023 curl "https://api.jiskta.com/api/v1/climate/query?\ lat_min=48.5&lat_max=49.0&lon_min=2.0&lon_max=2.5&\ time_start=2015-01-01&time_end=2023-12-31&\ variables=no2&format=csv&aggregate=trend" \ -H "X-API-Key: sk_live_your_key_here"
lat,lon,slope,intercept,r2,n 48.5500,2.0500,-0.523847,1061.234567,0.8812,108 48.5500,2.1500,-0.491203,995.672341,0.8534,108 48.6500,2.0500,-0.507621,1028.451892,0.8701,108 ...
The slope is in µg/m³/year (or ppb/year when unit=ppb). A negative slope indicates improving air quality over the period.
Facility-pinned trend & divergence analysis
Add facility=<inspire_hash> to anchor a trend query to the verified GPS coordinates of a registered E-PRTR industrial facility. Add return_divergence=true to cross-validate the satellite-derived trend against the facility's annual self-reported NOₓ emissions from the European Environment Agency's E-PRTR registry.
# BASF Ludwigshafen — 10-year NO₂ trend + divergence vs E-PRTR NOₓ curl "https://api.jiskta.com/api/v1/climate/query?\ facility=12626246128710077482&\ time_start=2013-01-01&time_end=2023-12-31&\ variables=no2&format=csv&aggregate=trend&\ return_divergence=true" \ -H "X-API-Key: sk_live_your_key_here"
{
"status": "success",
"query_time_ms": 34,
"tiles_scanned": 1,
"credits_used": 1,
"credits_remaining": 9999,
"output": "lat,lon,slope,intercept,r2,n\n49.5150,8.4250,-0.312,24.891,0.763,132",
"divergence": {
"direction": "consistent",
"score": 0.12,
"cams_no2_slope": -0.312,
"eprtr_nox_slope": -4820.5
}
} | Divergence field | Type | Description |
|---|---|---|
direction | string | consistent — satellite and E-PRTR trends agree. divergent — significant disagreement. strongly_divergent — opposing trends (satellite improves, self-reported worsens, or vice versa). |
score | float | Normalised divergence magnitude (0 = perfect agreement). Higher means more disagreement. |
cams_no2_slope | float | OLS slope from satellite CAMS NO₂ data (µg/m³/year). Negative = improving. |
eprtr_nox_slope | float | OLS slope from E-PRTR self-reported NOₓ emissions (tonnes/year). Negative = improving. Only NOₓ is currently cross-validated. |
Use GET /api/v1/facilities to search for facility inspire_hash IDs by name, country, or GPS. The Query Builder Facilities tab also supports interactive search with a one-click Use in Climate Query action.
Standard Deviation
Use aggregate=stddev with format=csv to compute the temporal mean and sample standard deviation per grid point over the query range. Returns one row per grid point. Only cells with at least 2 valid data points are emitted.
# NO₂ mean and variability for Paris, 2023 curl "https://api.jiskta.com/api/v1/climate/query?\ lat=48.85&lon=2.35&\ time_start=2023-01&time_end=2023-12&\ variables=no2&format=csv&aggregate=stddev" \ -H "X-API-Key: sk_live_your_key_here"
lat,lon,no2_mean,no2_stddev 48.8500,2.3500,16.4821,8.2134
The stddev column is the sample standard deviation (divided by n−1). High stddev indicates high temporal variability — useful for identifying locations with episodic pollution events vs. constant baseline exposure.
Diurnal Profile
Use aggregate=diurnal to get the average value per hour-of-day (0–23), spatially averaged over the bounding box. Shows the typical daily cycle of a pollutant over your chosen period.
# Hourly-of-day NO₂ profile for Paris, January 2023 curl "https://api.jiskta.com/api/v1/climate/query?\ lat_min=48.5&lat_max=49.0&lon_min=2.0&lon_max=2.5&\ time_start=2023-01-01&time_end=2023-01-31&\ variables=no2&format=csv&aggregate=diurnal" \ -H "X-API-Key: sk_live_your_key_here"
hour,no2_mean 0,12.79 1,11.43 ... 12,14.20 ... 23,13.55
Percentile Analysis
Add percentile=N (1–100) to compute the Nth percentile of all hourly values per grid point over the queried period. Commonly used for regulatory compliance (e.g., the 98th percentile for NO₂ limit value assessments).
# 95th percentile NO₂ per grid point, January 2023 curl "https://api.jiskta.com/api/v1/climate/query?\ lat_min=48.5&lat_max=49.0&lon_min=2.0&lon_max=2.5&\ time_start=2023-01-01&time_end=2023-01-31&\ variables=no2&percentile=95" \ -H "X-API-Key: sk_live_your_key_here"
lat,lon,p95 48.5500,2.0500,23.82 48.5500,2.1500,21.47 48.6500,2.0500,19.63 ...
The aggregate field in the response is set to percentile_N (e.g. percentile_95).
GeoJSON Output
Set format=geojson to receive a GeoJSON FeatureCollection instead of CSV. Each feature is a Point with coordinates [lon, lat] and all data values in properties. Compatible with Mapbox, Leaflet, QGIS, and any GeoJSON-aware tool.
# Monthly mean in GeoJSON format curl "https://api.jiskta.com/api/v1/climate/query?\ lat_min=48.5&lat_max=49.0&lon_min=2.0&lon_max=2.5&\ time_start=2023-01&time_end=2023-01&\ variables=no2&format=geojson&aggregate=monthly" \ -H "X-API-Key: sk_live_your_key_here"
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": { "type": "Point", "coordinates": [2.05, 48.55] },
"properties": { "year_month": "2023-01", "no2_mean": 9.87 }
},
...
]
} Note: GeoJSON output is supported for all per-point modes (hourly, daily, monthly, annual, exceedance, percentile). Area and diurnal aggregates have no spatial dimension and are returned as CSV regardless of the format parameter.
NDJSON Output
Set format=ndjson to receive Newline-Delimited JSON — one JSON object per line. Compatible with all aggregate modes. Each line is a complete, self-describing JSON object. Ideal for streaming into Python/pandas without loading a full CSV into memory, and for REST pipelines that prefer JSON.
curl "https://api.jiskta.com/api/v1/climate/query?\ lat=48.85&lon=2.35&\ time_start=2023-01&time_end=2023-03&\ variables=no2&format=ndjson&aggregate=monthly" \ -H "X-API-Key: sk_live_..."
{"lat":48.8500,"lon":2.3500,"year_month":"2023-01","no2_mean":25.67}
{"lat":48.8500,"lon":2.3500,"year_month":"2023-02","no2_mean":36.93}
{"lat":48.8500,"lon":2.3500,"year_month":"2023-03","no2_mean":23.37} Numeric values are emitted as JSON numbers (no quotes). Timestamp/date columns are strings. Empty cells become null.
Max / Min Aggregate
Use aggregate=max or aggregate=min with format=csv to get the temporal peak or minimum value per grid point over the queried period. Returns one row per grid point — useful for regulatory compliance (WHO daily limits), extreme event analysis, and ranking locations by worst-case pollution.
# Peak NO₂ per location in Paris, full year 2023 curl "https://api.jiskta.com/api/v1/climate/query?\ area=paris&time_start=2023-01&time_end=2023-12&\ variables=no2&format=csv&aggregate=max&sort_by=no2_max&sort_dir=desc" \ -H "X-API-Key: sk_live_..."
lat,lon,no2_max 48.8500,2.3500,87.4321 48.8500,2.4500,84.1209 48.7500,2.3500,79.8834
Column name: {var}_max for max, {var}_min for min. Works with all variables and multi-variable queries.
Cumulative Sum
Use aggregate=cumulative with format=csv to get a running cumulative sum per grid point. Returns one row per grid point × day, with the value increasing over time. Primarily designed for tp (total precipitation) to compute seasonal or annual totals.
# Cumulative precipitation in Paris, Jan 2023 curl "https://api.jiskta.com/api/v1/climate/query?\ lat=48.85&lon=2.35&\ time_start=2023-01-01&time_end=2023-01-31&\ variables=tp&format=csv&aggregate=cumulative" \ -H "X-API-Key: sk_live_..."
lat,lon,date,tp_cumsum 48.8500,2.3500,2023-01-01,0.0012 48.8500,2.3500,2023-01-02,0.0031 48.8500,2.3500,2023-01-03,0.0055 ...
Named Areas & Polygon Masking
Pass area=name instead of explicit lat_min/lat_max/lon_min/lon_max to use a predefined area. Named areas apply polygon boundary masking for CSV output — grid cells outside the real geographic boundary are filtered out.
The area index is powered by OpenStreetMap administrative boundaries at all admin levels (2=country, 4=province/region, 6=department, 8=city/municipality). Use GET /api/v1/areas?q=name to discover area IDs and bboxes. You can also look up by OSM relation ID: area=osm:71525.
# Discover areas matching a name curl "https://api.jiskta.com/api/v1/areas?q=paris" # Look up Brussels by OSM relation ID curl "https://api.jiskta.com/api/v1/areas?osm=54094" # Find areas overlapping a bounding box curl "https://api.jiskta.com/api/v1/areas?bbox=48,49,2,3"
{
"status": "ok",
"source": "libarea",
"count": 1,
"areas": [
{ "id": 123, "name": "Paris", "admin_level": 8, "parent": "Île-de-France",
"osm_id": 71525, "lat_min": 48.81, "lat_max": 48.90,
"lon_min": 2.22, "lon_max": 2.47 }
]
} # Query by name (polygon-masked, only cells inside Paris city boundary) curl "https://api.jiskta.com/api/v1/climate/query?\ area=paris&time_start=2023-01&time_end=2023-12&\ variables=no2&format=csv&aggregate=monthly" \ -H "X-API-Key: sk_live_..." # Query by OSM relation ID (exact polygon boundary) curl "https://api.jiskta.com/api/v1/climate/query?\ area=osm:54094&time_start=2023-01&time_end=2023-12&\ variables=no2&format=csv&aggregate=monthly" \ -H "X-API-Key: sk_live_..." # Belgium - only cells inside Belgian polygon (not rectangle) curl "https://api.jiskta.com/api/v1/climate/query?\ area=belgium&time_start=2023-01&time_end=2023-01&\ variables=no2&format=csv&aggregate=monthly" \ -H "X-API-Key: sk_live_..."
Response includes:
{
"status": "success",
"area": "belgium",
"area_polygon": {
"type": "MultiPolygon",
"coordinates": [[ [[3.31,49.51],[2.52,49.51],... ] ]]
},
"output": "lat,lon,year_month,no2_mean\n..."
} Name normalization: Names are case-insensitive and treat spaces, underscores, and hyphens identically. area=new-york, area=New York, and area=new_york are all equivalent.
Note: Polygon masking applies to format=csv output. For format=stats, statistics cover all grid points within the queried tiles. Credits are based on tiles that intersect the area polygon — overseas territories are included if they have data tiles, but empty ocean tiles within the bounding box are excluded.
The bbox= parameter is still accepted as a backward-compatible alias for area=.
Data source: OpenStreetMap administrative boundaries (© ODbL contributors) via a pre-built io_uring binary index (areas_v2.bin). Covers all European admin levels 2–8 from the Europe OSM extract.
| Name | Type | Source |
|---|---|---|
paris | City (level 8) | OSM commune boundary |
london | City (level 8) | OSM Greater London boundary |
berlin | City (level 4) | OSM city-state boundary |
amsterdam | City (level 8) | OSM municipality boundary |
brussels | City (level 8) | OSM municipality boundary |
france | Country (level 2) | OSM national boundary |
germany | Country (level 2) | OSM national boundary |
belgium | Country (level 2) | OSM national boundary |
osm:<relation_id> | Any area | Direct OSM relation ID lookup |
All OSM administrative relations at admin_level 2, 4, 6, or 8 for Europe are indexed. Use /api/v1/areas?q=name to discover any area. | ||
Area Discovery API
Use GET /api/v1/areas to discover named areas and their bounding boxes. No authentication required.
| Param | Type | Description |
|---|---|---|
q | string | Name search (case-insensitive, underscore/hyphen/space equivalent). Returns first match. |
bbox | string | Comma-separated lat_min,lat_max,lon_min,lon_max. Returns up to 50 areas overlapping this rectangle. |
osm | integer | OSM relation ID. Returns the matching area. |
id | integer | Direct internal area_id lookup. |
# Search by name curl "https://api.jiskta.com/api/v1/areas?q=amsterdam" # All areas overlapping Paris bbox curl "https://api.jiskta.com/api/v1/areas?bbox=48.8,48.9,2.2,2.5" # Look up by OSM relation ID curl "https://api.jiskta.com/api/v1/areas?osm=54094"
{
"status": "ok",
"source": "libarea",
"count": 3,
"areas": [
{ "id": 1, "name": "Amsterdam", "admin_level": 8, "parent": "North Holland",
"osm_id": 271110, "lat_min": 52.27, "lat_max": 52.43,
"lon_min": 4.73, "lon_max": 5.08 },
{ "id": 2, "name": "North Holland", "admin_level": 4, "parent": "Netherlands",
"osm_id": 47772, "lat_min": 52.27, "lat_max": 53.17,
"lon_min": 4.46, "lon_max": 5.31 }
]
} Pass the returned id to the area= parameter via area=osm:<osm_id>, or use the area name directly.
POST with GeoJSON Mask
For custom polygon boundaries (industrial zones, watersheds, administrative districts), use the POST /api/v1/climate/query endpoint with a JSON body. Include a mask field containing a GeoJSON Polygon or MultiPolygon geometry. All other query parameters can be passed in the JSON body or as URL query parameters.
# Query only within a custom polygon (GeoJSON lon,lat order)
curl -X POST "https://api.jiskta.com/api/v1/climate/query" \
-H "X-API-Key: sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"lat_min": 48.7, "lat_max": 49.0,
"lon_min": 2.2, "lon_max": 2.5,
"time_start": "2023-01", "time_end": "2023-01",
"variables": "no2", "format": "csv", "aggregate": "monthly",
"mask": {
"type": "Polygon",
"coordinates": [[[2.30,48.80],[2.45,48.80],[2.45,48.95],[2.30,48.95],[2.30,48.80]]]
}
}' import requests, json
mask = {
"type": "Polygon",
"coordinates": [[[2.30,48.80],[2.45,48.80],[2.45,48.95],[2.30,48.95],[2.30,48.80]]]
}
resp = requests.post(
"https://api.jiskta.com/api/v1/climate/query",
headers={"X-API-Key": "sk_live_..."},
json={
"lat_min": 48.7, "lat_max": 49.0,
"lon_min": 2.2, "lon_max": 2.5,
"time_start": "2023-01", "time_end": "2023-01",
"variables": "no2", "format": "csv", "aggregate": "monthly",
"mask": mask,
}
)
print(resp.json()["output"]) GeoJSON coordinate order: GeoJSON uses [longitude, latitude] order (opposite of most climate conventions). Coordinates outside the query bbox are silently ignored — only points within both the bbox and the polygon are returned.
Supported geometry types: Polygon and MultiPolygon. The outer ring of each polygon is used for masking; holes (inner rings) are not supported in this version.
Credits: Charged by bounding box tiles, not polygon area. A polygon that covers 10% of a tile still charges for the full tile.
Sort Output
Add sort_by=column&sort_dir=asc|desc to sort CSV or ndjson output by any column. Sort is applied after all data is computed. Uses a stable sort, so rows with equal values preserve their original order.
# Top 10 most polluted cells in Paris, January 2023 curl "https://api.jiskta.com/api/v1/climate/query?\ area=paris&time_start=2023-01&time_end=2023-01&\ variables=no2&format=csv&aggregate=monthly&\ sort_by=no2_mean&sort_dir=desc" \ -H "X-API-Key: sk_live_..."
If sort_by refers to a column that doesn't exist in the output (e.g. wrong variable name), the output is returned unsorted — no error is raised.
Gzip Compression
Add compress=gzip to receive a gzip-compressed response. The server sets Content-Encoding: gzip. Most HTTP clients (curl, Python requests, fetch) decompress automatically. Large daily/hourly CSV outputs typically compress 5–10× (e.g. 2 MB → 200 KB).
# curl decompresses automatically with --compressed curl --compressed "https://api.jiskta.com/api/v1/climate/query?\ area=france&time_start=2023-01&time_end=2023-12&\ variables=no2&format=csv&aggregate=daily&compress=gzip" \ -H "X-API-Key: sk_live_..."
Conditional GET (304 Not Modified)
Every query response includes an ETag header (a hash of the query parameters). On repeat requests, send the ETag back in an If-None-Match header — if the data hasn't changed, the server responds with 304 Not Modified and an empty body, saving bandwidth and credits.
# First request — save the ETag
ETAG=$(curl -sI "https://api.jiskta.com/api/v1/climate/query?\
lat=48.85&lon=2.35&time_start=2023-01&time_end=2023-01&variables=no2&format=stats" \
-H "X-API-Key: sk_live_..." | grep -i etag | awk '{print $2}')
# Repeat request — 304 if data unchanged (deducts 0 credits)
curl -si "https://api.jiskta.com/api/v1/climate/query?\
lat=48.85&lon=2.35&time_start=2023-01&time_end=2023-01&variables=no2&format=stats" \
-H "X-API-Key: sk_live_..." \
-H "If-None-Match: $ETAG"
# → HTTP/2 304 (empty body, 0 credits used) Historical data (validated reanalysis, 2013–2023) is immutable — ETags are stable indefinitely. For interim reanalysis periods (recent months), data may be upgraded to validated reanalysis during monthly sync; in that case the ETag will change.
CAMS Air Quality Variables
Copernicus Atmosphere Monitoring Service (CAMS) reanalysis. 0.1° spatial resolution, hourly values, European coverage, 2013–2025.
| Variable | Parameter value | Unit | Description |
|---|---|---|---|
| Nitrogen Dioxide | no2 | µg/m³ | Combustion byproduct. Key indicator for urban traffic and industrial pollution. |
| Fine Particulate Matter | pm2p5 | µg/m³ | Particles <2.5µm. Core metric for public health and air quality indices. |
| Coarse Particulate Matter | pm10 | µg/m³ | Particles <10µm. Regulatory standard for dust and industrial emissions. |
| Ozone | o3 | µg/m³ | Surface ozone. Critical for photochemical smog and vegetation impact. |
ERA5 Meteorological Variables
ECMWF ERA5 global atmospheric reanalysis, European domain (30°N–70°N, 25°W–40°E). 0.25° spatial resolution, hourly values, 2013–2026.
| Variable | Parameter value | Unit | Description |
|---|---|---|---|
| 2m Temperature | t2m | K | Air temperature at 2 metres. Correlates with O₃ formation; detect thermal inversions. |
| Total Precipitation | tp | m/h | Total precipitation. Model wet deposition and pollutant wash-out events. |
| Boundary Layer Height | blh | m | Atmospheric boundary layer height. Controls vertical dispersion of pollutants. |
| U Wind Component (10 m) | u10 | m/s | Eastward wind at 10 m. Analyse pollutant transport direction. |
| V Wind Component (10 m) | v10 | m/s | Northward wind at 10 m. Combine with u10 for full wind vectors. |
| Wind Speed (derived) | wind_speed | m/s | Horizontal wind speed = √(u10² + v10²). Computed on-the-fly from ERA5 tiles — no extra credits vs requesting u10+v10 separately. |
| Wind Direction (derived) | wind_dir | degrees | Meteorological FROM direction: 0°=N, 90°=E, 180°=S, 270°=W. Computed from u10/v10; not available with aggregate=trend. |
Wind speed and direction example
curl "https://api.jiskta.com/api/v1/climate/query?\ lat=48.85&lon=2.35&\ time_start=2023-01-01&time_end=2023-12-31&\ variables=wind_speed,wind_dir,no2&format=csv&aggregate=monthly" \ -H "X-API-Key: sk_live_your_key_here"
lat,lon,year_month,wind_speed,wind_dir,no2_mean 48.8500,2.3500,2023-01,3.1209,245.8,13.2847 48.8500,2.3500,2023-02,2.8941,212.4,11.9302 ...
curl "https://api.jiskta.com/api/v1/climate/query?\n lat_min=48.5&lat_max=49.0&lon_min=2.0&lon_max=2.5&\n time_start=2023-01-01&time_end=2023-01-31&\n variables=t2m,blh&format=csv&aggregate=daily" \ -H "X-API-Key: sk_live_your_key_here"
lat,lon,date,t2m_mean,blh_mean 48.5500,2.0500,2023-01-01,275.4,342.1 48.5500,2.0500,2023-01-02,274.8,289.7 ...
Tip: Query CAMS, ERA5, and VIIRS variables together — variables=no2,t2m,viirs — credits are charged as tiles × months × variables.
VIIRS Nighttime Lights
NASA Black Marble VNP46A3 monthly composite radiance. 0.005° spatial resolution (~500 m), monthly values, 2020–present, global coverage.
| Variable | Parameter value | Unit | Description |
|---|---|---|---|
| Nighttime Radiance | viirs | nW/cm²/sr | Monthly cloud-free composite. Snow-free composite (AllAngle_Composite_Snow_Free). Higher values = more artificial light. 0 = ocean/no-data. |
- Resolution: 0.005° (~500 m) — 20× finer than CAMS
- Coverage: global land areas, 2020-01 to present
- Format: same as CAMS/ERA5 — use
format=csv,aggregate=monthly, orformat=stats - Point queries (
lat=/lon=) snap to nearest 0.005° cell
Example: city radiance time series
curl "https://api.jiskta.com/api/v1/climate/query?\ lat=48.85&lon=2.35&\ time_start=2020-01&time_end=2024-12&\ variables=viirs&format=csv&aggregate=monthly" \ -H "X-API-Key: sk_live_your_key_here"
lat,lon,year_month,viirs_mean 48.8500,2.3500,2020-01,64.3 48.8500,2.3500,2020-02,61.8 ...
Example: combine with air quality
curl "https://api.jiskta.com/api/v1/climate/query?\ lat_min=48.5&lat_max=49.0&lon_min=2.0&lon_max=2.5&\ time_start=2023-01&time_end=2023-12&\ variables=viirs,no2&format=csv&aggregate=monthly" \ -H "X-API-Key: sk_live_your_key_here"
GHSL Human Settlement
JRC Global Human Settlement Layer (GHSL) — population density and built-up surface area at 0.005° spatial resolution (~500 m). Epoch-based (not monthly): 2010, 2015, 2020, 2025. Global coverage. Queries for any date are automatically snapped to the nearest epoch.
| Variable | Parameter value | Unit | Description |
|---|---|---|---|
| Population density | ghsl_pop | persons/cell | GHS-POP: resident population count per 0.005° cell (R2023A release). Epochs: 2010, 2015, 2020, 2025. |
| Built-up surface (total) | ghsl_built | m² | GHS-BUILT-S: total built-up surface area per cell (residential + non-residential). |
| Built-up surface (non-res) | ghsl_built_nres | m² | GHS-BUILT-S-NRES: non-residential built-up surface area per cell (offices, industry, infrastructure). |
- Resolution: 0.005° (~500 m) — same grid as VIIRS
- Coverage: global, epochs 2010 / 2015 / 2020 / 2025
- Epoch snapping: a query for
time_start=2023-01returns the 2020 epoch (nearest floor). A query fortime_start=2024-01returns the 2025 epoch (nearest). - Format: use
format=statsorformat=csv&aggregate=monthly. Since GHSL is epoch-based, all monthly rows within an epoch return the same value.
Example: population density in Paris region
curl "https://api.jiskta.com/api/v1/climate/query?\ lat_min=48.7&lat_max=49.0&lon_min=2.2&lon_max=2.5&\ time_start=2020-01&time_end=2020-01&\ variables=ghsl_pop&format=stats" \ -H "X-API-Key: sk_live_your_key_here"
Example: built-up surface trend (2010 vs 2020)
# 2010 epoch curl "https://api.jiskta.com/api/v1/climate/query?\ lat_min=48.7&lat_max=49.0&lon_min=2.2&lon_max=2.5&\ time_start=2010-01&time_end=2010-01&\ variables=ghsl_built&format=stats" \ -H "X-API-Key: sk_live_your_key_here" # 2020 epoch curl "https://api.jiskta.com/api/v1/climate/query?\ lat_min=48.7&lat_max=49.0&lon_min=2.2&lon_max=2.5&\ time_start=2020-01&time_end=2020-01&\ variables=ghsl_built&format=stats" \ -H "X-API-Key: sk_live_your_key_here"
MODIS Land Cover
NASA MODIS MCD12Q1 annual land cover product — urban/built-up fraction at 0.005° spatial resolution (~500 m). Annual from 2013 to present. Global coverage.
| Variable | Parameter value | Unit | Description |
|---|---|---|---|
| Urban fraction | modis_urban | fraction (0–1) | Urban and built-up land cover fraction from IGBP classification. 0 = no urban, 1 = fully urbanised cell. Annual composites. |
- Resolution: 0.005° (~500 m) — same grid as VIIRS and GHSL
- Coverage: global, annual, 2013–present
- Querying a sub-annual time range returns the year's annual value for all months in that range
- Combine with GHSL for a cross-validated urbanisation picture: GHSL (building footprints) vs MODIS (spectral land use classification)
Example: urban expansion in a growing city
curl "https://api.jiskta.com/api/v1/climate/query?\ lat_min=23.0&lat_max=24.0&lon_min=113.0&lon_max=114.0&\ time_start=2013-01&time_end=2023-12&\ variables=modis_urban&format=csv&aggregate=monthly" \ -H "X-API-Key: sk_live_your_key_here"
Example: urbanisation vs air quality
curl "https://api.jiskta.com/api/v1/climate/query?\ lat_min=23.0&lat_max=24.0&lon_min=113.0&lon_max=114.0&\ time_start=2015-01&time_end=2023-12&\ variables=modis_urban,no2&format=csv&aggregate=monthly" \ -H "X-API-Key: sk_live_your_key_here"
MODIS Burned Area (Wildfire)
NASA MODIS MCD64A1 monthly burned area composites — burned fraction per 0.005° cell (~500 m) derived from Terra+Aqua combined monthly composites. Global coverage from 2020 to present.
| Variable | Parameter value | Unit | Description |
|---|---|---|---|
| Burned area fraction | modis_burned_area | fraction (0–1) | Fraction of each 0.005° cell detected as burned during the calendar month. 0 = unburned, 1 = fully burned. Sparse: only months/tiles with detected fire events are stored. |
- Resolution: 0.005° (~500 m) — same grid as VIIRS and GHSL
- Coverage: global, monthly, 2020–present (~2 month lag for LAADS DAAC release)
- Sparse tiles: tiles with no detected fires are not stored. Querying a fire-free area returns no rows (not an error)
- Source: NASA LAADS DAAC MCD64A1 Collection 6.1 (CC BY 4.0)
- Use cases: wildfire risk exposure screening, CSRD ESRS E1 physical climate risk, reinsurance, supply chain disruption modelling
Example: wildfire exposure for a site in California
curl "https://api.jiskta.com/api/v1/climate/query?\ lat_min=37.0&lat_max=38.0&lon_min=-122.0&lon_max=-121.0&\ time_start=2020-01&time_end=2024-12&\ variables=modis_burned_area&format=csv&aggregate=monthly" \ -H "X-API-Key: sk_live_your_key_here"
Example: fire events near an EU supply chain facility
curl "https://api.jiskta.com/api/v1/climate/query?\ lat_min=37.5&lat_max=39.5&lon_min=20.0&lon_max=23.0&\ time_start=2021-01&time_end=2023-12&\ variables=modis_burned_area&format=csv&aggregate=monthly" \ -H "X-API-Key: sk_live_your_key_here"
Data Coverage
GET /api/v1/coverage
Returns the full month-by-month data availability index for CAMS and VIIRS. No authentication required.
curl https://api.jiskta.com/api/v1/coverage
{
"status": "ok",
"validated_lag_months": 18,
"interim_lag_months": 4,
"coverage": {
"nitrogen_dioxide": {
"2023-01": { "type": "reanalysis", "downloaded_at": "2026-02-01T06:12:04Z", "size_mb": 187.4 },
"2025-10": { "type": "interim_reanalysis", "downloaded_at": "2026-02-01T06:18:31Z", "size_mb": 191.2 }
}
}
} The interactive visual calendar is available on the Coverage page (no login required).
Health Check
GET /health
Check if the API is running. No authentication required.
{
"status": "healthy",
"service": "climate-api",
"auth_enabled": true
} Tile Availability
GET /api/v1/tiles
Returns which tiles (lat/lon grid cells, variables, and years) are available on disk for a given bounding box. Useful for checking data coverage before building a query. No authentication required.
| Parameter | Type | Required | Description |
|---|---|---|---|
lat_min, lat_max, lon_min, lon_max | float | optional | Bounding box filter. Default: full coverage (all tiles returned). |
variables | string | optional | Comma-separated variable filter. Default: all variables. |
curl "https://api.jiskta.com/api/v1/tiles?lat_min=48&lat_max=53&lon_min=0&lon_max=5&variables=no2"
{
"tiles": [
{
"lat": 50, "lon": 0,
"variable": "no2",
"dir": "tiles_v3",
"years": ["2013", "2014", ..., "2025"]
},
...
]
} OpenAPI Specification
GET /api/v1/openapi.json
Machine-readable OpenAPI 3.0.3 specification. Use it to auto-generate clients, import into Postman/Insomnia, or explore with Swagger UI. No authentication required.
curl https://api.jiskta.com/api/v1/openapi.json
Point Enrichment — NUTS3, Water Risk & Natura 2000
GET /api/v1/enrich
Given a geographic coordinate, returns the NUTS3 administrative region that contains it — along with region metadata, Eurostat socioeconomic indicators (GDP per capita, population density, respiratory mortality), WRI Aqueduct 4.0 global water risk scores (baseline water stress, water depletion, flood risk, and drought risk), and Natura 2000 protected area proximity (EU only). Useful for labelling grid query results, geocoding location inputs, or enriching tabular data with regional, water risk, and biodiversity context.
| Parameter | Type | Required | Description |
|---|---|---|---|
lat | float | ✓ | Latitude (decimal degrees, WGS-84) |
lon | float | ✓ | Longitude (decimal degrees, WGS-84) |
year | integer | Reference year for Natura 2000 time series (2004–2024). Omit for the latest (2024) network. Useful for CSRD baseline assessments or research into how the protected area network evolved over time. |
curl "https://api.jiskta.com/api/v1/enrich?lat=52.52&lon=13.40"
curl "https://api.jiskta.com/api/v1/enrich?lat=51.5&lon=5.4&year=2010"
{
"nuts3_id": "DE300",
"nuts3_name": "Berlin",
"country": "DE",
"lat": 52.5,
"lon": 13.4,
"gdp_pps": 48700.00,
"pop_density_km2": 4311.60,
"resp_deaths_nr": null,
"water_stress": {
"bws": 2, "bws_score": 2.14, "bws_label": "Low-Medium",
"bwd": 1, "bwd_score": 1.32, "bwd_label": "Low",
"rfr": 3, "rfr_score": 3.21, "rfr_label": "Medium-High",
"drr": 1, "drr_score": 1.43, "drr_label": "Low",
"source": "WRI Aqueduct 4.0 (2023)"
},
"natura_2000": {
"in_natura": true,
"dist_km": 0.0,
"sitecode": "BE2400008",
"sitename": "Zoniënwoud",
"sitetype": "SPA / Special Area of Conservation",
"year_used": 2024,
"year_spa": 1995,
"year_sac": 2003,
"source": "EEA Natura 2000 (2024)",
"attribution": "European Environment Agency"
}
} {
"lat": 31.23, "lon": 121.47,
"water_stress": {
"bws": 5, "bws_score": 4.92, "bws_label": "Extremely High",
"bwd": 4, "bwd_score": 4.17, "bwd_label": "High",
"rfr": 4, "rfr_score": 4.31, "rfr_label": "High",
"drr": 2, "drr_score": 1.88, "drr_label": "Low-Medium",
"source": "WRI Aqueduct 4.0 (2023)"
}
} | Field | Source | Description |
|---|---|---|
nuts3_id | NUTS 2021 | EU NUTS3 region code (e.g. DE300). EU only. |
nuts3_name | NUTS 2021 | Official region name |
country | NUTS 2021 | ISO 2-letter country code |
gdp_pps | Eurostat nama_10r_3gdp | GDP per capita in PPS (EU27=100), most recent year available. null if not published at NUTS3 level for this region. |
pop_density_km2 | Eurostat demo_r_d3dens | Population per km², most recent year. null if unavailable. |
resp_deaths_nr | Eurostat hlth_cd_aro | Absolute respiratory disease deaths (ICD-10 J chapter). Sparse — only countries that report at NUTS3 level. |
water_stress.bws | WRI Aqueduct 4.0 | Baseline Water Stress integer category 1–5 (Low … Extremely High). bws_score: continuous float (v2 index only). ESRS E3-1 §27. Global. |
water_stress.bwd | WRI Aqueduct 4.0 | Baseline Water Depletion integer category 1–5. bwd_score: continuous float (v2 index only). ESRS E3-1 §27. Global. |
water_stress.rfr | WRI Aqueduct 4.0 | Riverine Flood Risk integer category 1–5. rfr_score: continuous float (v2 index only). ESRS E3-3 §38. Global. |
water_stress.drr | WRI Aqueduct 4.0 | Drought Risk integer category 1–5. drr_score: continuous float (v2 index only). ESRS E3-3 §38. Global. |
natura_2000.in_natura | EEA Natura 2000 (2004–2024) | Whether the coordinate is inside a Natura 2000 protected area (SPA or SAC/SCI) in the requested year. EU only; field absent for non-EU points. ESRS E4-2 §30. |
natura_2000.dist_km | EEA Natura 2000 (2004–2024) | Distance in km to the nearest Natura 2000 boundary for the requested year. 0.0 if inside. Supports ESRS E4-5 §40 material site biodiversity footprint disclosure. EU only. |
natura_2000.year_used | EEA Natura 2000 time series | The actual year of the N2K raster used (floor to nearest available year in 2004–2024). Confirms which network snapshot was applied. |
natura_2000.year_spa | EEA N2K Backbone | Year the containing or nearest site was first submitted as an SPA. 0 if unknown. Present when N2KS v2 sites index is loaded. |
natura_2000.year_sac | EEA N2K Backbone | Year the site was first submitted as a SAC/SCI. 0 if unknown. Present when N2KS v2 sites index is loaded. |
natura_2000.sitecode | EEA Natura 2000 | EU standard site code of the containing site (when in_natura: true), e.g. BE2400008. Present when N2KS sites index is loaded. |
natura_2000.sitename | EEA Natura 2000 | Official name of the containing Natura 2000 site, in the national language. Present when in_natura: true. |
natura_2000.sitetype | EEA Natura 2000 | Human-readable designation: Special Protection Area (SPA), Special Area of Conservation (SAC), or SPA / Special Area of Conservation. Present when in_natura: true. |
natura_2000.nearest_sitecode | EEA Natura 2000 | Site code of the nearest Natura 2000 site (when in_natura: false). Present when N2KS sites index is loaded and a site is found within 100 km. |
natura_2000.nearest_sitename | EEA Natura 2000 | Official name of the nearest Natura 2000 site. Present when in_natura: false. |
natura_2000.nearest_sitetype | EEA Natura 2000 | Designation of the nearest site (SPA / SAC / SPA+SAC). Present when in_natura: false. |
natura_2000.border_cell | EEA Natura 2000 (CSRD) | true when the point is outside but within 15 km of a Natura 2000 boundary. Supports ESRS E4-5 §40 material site proximity disclosure. Only emitted when true; omitted when false. |
natura_2000.year_confidence | EEA N2K Backbone (CSRD) | Confidence of the year_spa/year_sac designation date. "exact" = year confirmed from EEA Backbone (post-2004 submission); "baseline_proxy" = site existed before the earliest Backbone record (year set to 2004 as conservative proxy — CSRD-safe); "unknown" = no designation date available. Only emitted when N2KS v2 sites index is loaded and a nearest site was found. |
natura_2000.proximity_band | EEA Natura 2000 (CSRD) | Categorical distance band for ESRS E4 materiality assessment. One of: "inside", "0-1km", "1-5km", "5-15km", "15-50km", ">50km". EU only. Always present alongside in_natura. |
natura_2000.spatial_confidence | EEA Natura 2000 (CSRD) | Reliability of the inside/outside classification at 0.1° raster resolution. "likely_inside" = classified inside (0.1° cell may straddle the boundary — verify for CSRD); "high" = clearly outside (dist ≥ 5 km or no Natura 2000 neighbours); "uncertain" = within 5 km border zone (cell may be mis-classified); "uncertain_lean_inside" = in border zone but surrounded by Natura 2000 cells (likely inside). EU only. |
natura_2000.neighbors_in_natura | EEA Natura 2000 (CSRD) | Count of the 8 surrounding 0.1° raster cells that are inside Natura 2000 for the requested year. Ranges 0–8. Supports spatial confidence calibration: ≥6 neighbours suggests the point is on the Natura 2000 network even if the exact cell is classified outside. EU only; present when a site is found. |
natura_2000.csrd_evidence | EEA Natura 2000 (CSRD) | Structured ESRS E4 evidence block. Present when the point is inside Natura 2000 or within 15 km (border_cell). Fields: e4_trigger (string, e.g. "inside" or "border_cell"), proximity_band, spatial_confidence, verification_recommended (boolean — true when independent GIS verification is advisable before reporting). Omitted when the point is clearly outside. |
natura_2000.n_habitats_annex1 | EEA Natura 2000 Annex I (CSRD) | Total count of Annex I habitat types listed for the containing or nearest Natura 2000 site. Annex I habitats are legally protected EU habitat types under the Habitats Directive (92/43/EEC). Relevant for ESRS E4-2 §30 biodiversity and ecosystem services disclosures. Present when the N2KH habitat index is loaded and a site is found. |
natura_2000.has_priority_habitat | EEA Natura 2000 Annex I (CSRD) | true if the site contains at least one priority habitat type (marked with * in the Habitats Directive Annex I — e.g. alluvial forests 91E0*, Mediterranean maquis, bog woodlands). Priority habitats trigger enhanced CSRD/ESRS E4 disclosure obligations under §44 and may require Environmental Impact Assessment. Present alongside n_habitats_annex1. |
natura_2000.habitat_codes | EEA Natura 2000 Annex I (CSRD) | Array of up to 5 representative Annex I habitat type codes (integers) listed for the site, sorted ascending. Full list available on EUNIS (e.g. 3150 = natural eutrophic lakes, 6230 = species-rich Nardus grasslands). Capped at 5 in the API response; n_habitats_annex1 gives the total count. Present when the N2KH habitat index is loaded. |
natura_2000.n_species_annex2 | EEA Natura 2000 Annex II (CSRD) | Total count of Habitats Directive Annex II species listed for the containing or nearest Natura 2000 site. Annex II species are plants and animals whose conservation requires the designation of Special Areas of Conservation (SACs). Omitted when the site has no Annex II species records. Relevant for ESRS E4-4 §38 biodiversity impact disclosures. |
natura_2000.species_groups | EEA Natura 2000 Annex II (CSRD) | Array of species group letters present at the site. Values: "B" = Birds (Annex I of the Birds Directive), "M" = Mammals, "R" = Reptiles, "A" = Amphibians, "F" = Fish, "I" = Invertebrates, "P" = Plants. Example: ["M","R","F","I","P"]. Omitted when n_species_annex2 is absent. Together with n_species_annex2, this supports the CSRD biodiversity sensitivity screening required under ESRS E4 for sites inside or near Natura 2000. |
Coverage: NUTS3 — EU only (1,514 regions). Water risk — global (WRI Aqueduct 4.0, catchment polygons rasterized to 0.1°). Natura 2000 — EU only, time series 2004–2024 (21 annual rasters, 27,295 sites, 0.1° rasterized with distance transform). The ?year= parameter selects the network snapshot; omit for latest (2024). Points outside EU still receive water risk data. No credits are deducted. No authentication required.
Natura 2000 CSRD fields (proximity_band, spatial_confidence, neighbors_in_natura, csrd_evidence, n_habitats_annex1, has_priority_habitat, habitat_codes, n_species_annex2, species_groups) require both the N2K sites index and the Annex I/II habitat+species binary to be deployed on the server. All fields are present in the production API at api.jiskta.com.
E-PRTR Facilities
GET /api/v1/facilities
Search ~60,000 verified EU industrial facilities from the European Pollutant Release and Transfer Register (E-PRTR). Each facility has a verified GPS coordinate, E-PRTR sector classification, and annual self-reported emissions data (NOₓ, PM10, PM2.5, CO₂) for 2007–2022. No authentication required.
| Parameter | Type | Required | Description |
|---|---|---|---|
q | string | optional* | Text search on facility name or operator. Case-insensitive, partial match. E.g. q=BASF, q=ThyssenKrupp. |
country | string | optional | Filter by ISO 2-letter country code. E.g. country=DE. Can be combined with q. |
lat | float | optional* | Latitude for radius search. Requires lon and radius_km. |
lon | float | optional* | Longitude for radius search. |
radius_km | float | optional | Search radius in kilometres around lat/lon. Default: 25 km. |
id | string | optional* | Direct lookup by inspire_hash (64-bit integer as string). Returns the single matching facility or 404. |
emissions | boolean | optional | true — include annual NOₓ / PM10 / PM2.5 / CO₂ emission data (2007–2022) in the response. Increases response size. Default: false. |
* At least one of q, lat/lon, or id is required.
# Search by name + country curl "https://api.jiskta.com/api/v1/facilities?q=BASF&country=DE" # Radius search — facilities within 20 km of Ghent, Belgium curl "https://api.jiskta.com/api/v1/facilities?lat=51.05&lon=3.72&radius_km=20" # Direct ID lookup with emissions history curl "https://api.jiskta.com/api/v1/facilities?id=12626246128710077482&emissions=true"
{
"status": "ok",
"count": 2,
"facilities": [
{
"inspire_hash": "12626246128710077482",
"name": "BASF SE Ludwigshafen",
"operator": "BASF SE",
"country": "DE",
"sector": "Chemical industry",
"lat": 49.517,
"lon": 8.423,
"nox_emissions": {
"2015": 12450, "2016": 11820, "2017": 10930,
"2018": 10210, "2019": 9870, "2020": 8640,
"2021": 8120, "2022": 7890
}
},
...
]
} | Response field | Description |
|---|---|
inspire_hash | Stable 64-bit integer ID (string). Use as the facility= parameter in climate queries. |
name | Official facility name from E-PRTR |
operator | Legal operator / parent company name |
country | ISO 2-letter country code |
sector | E-PRTR sector classification (e.g. Energy, Metal, Chemical, Mineral, Waste) |
lat / lon | Verified GPS coordinates (WGS-84). These are the coordinates used when facility= is passed to a climate query. |
nox_emissions | Annual self-reported NOₓ emissions in tonnes, keyed by year (2007–2022). Only present when emissions=true. |
Use case: Look up a facility's inspire_hash, then use it in a climate query to get satellite-verified air quality trends — optionally cross-validated against the self-reported emissions via divergence analysis. The Query Builder Facilities tab provides an interactive search UI with one-click query integration.
Water Risk Grid
GET https://api.jiskta.com/api/v1/water-risk
Returns WRI Aqueduct 4.0 (2023) water risk scores for every 0.1° grid cell in a bounding box. Useful for researchers correlating water risk with air quality or economic data, and for CSRD portfolio screening across large geographic areas.
Auth: X-API-Key header required. Credits: 0 — static dataset, no time dimension.
Max bbox: 50°×50° (250,000 cells). Resolution: 0.1°.
| Param | Required | Description |
|---|---|---|
lat_min, lat_max, lon_min, lon_max | ✅ | Bounding box (WGS-84). Max 50°×50°. |
format | — | json (default) or csv |
labels | — | true — include text labels (e.g. "Extremely High") alongside numeric scores |
vars | — | Comma-separated: bws, bwd, rfr, drr (default: all four) |
curl "https://api.jiskta.com/api/v1/water-risk?lat_min=25&lat_max=26&lon_min=55&lon_max=56&format=json&labels=true" \ -H "X-API-Key: sk_live_..."
{
"status": "ok",
"source": "WRI Aqueduct 4.0 (2023)",
"n_cells": 100,
"has_scores": true,
"cells": [
{ "lat": 25.05, "lon": 55.15,
"bws": 5, "bws_score": 4.92, "bws_label": "Extremely high",
"bwd": 5, "bwd_score": 5.11, "bwd_label": "Extremely high",
"rfr": 4, "rfr_score": 4.31, "rfr_label": "High",
"drr": null, "drr_score": null, "drr_label": "No data" },
...
]
} curl "https://api.jiskta.com/api/v1/water-risk?lat_min=48&lat_max=52&lon_min=2&lon_max=8&format=csv" \ -H "X-API-Key: sk_live_..."
lat,lon,bws,bws_score,bwd,bwd_score,rfr,rfr_score,drr,drr_score 48.05,2.05,,,,,,,, 48.05,2.15,2,2.140,1,1.320,3,3.210,, 48.05,2.25,2,2.140,2,1.980,3,3.210,2,1.880 ...
Score legend: null / empty = no catchment data. 1 = Low, 2 = Low-Medium, 3 = Medium-High, 4 = High, 5 = Extremely High. When has_scores: true (v2 index), each indicator also includes a continuous raw score (bws_score, bwd_score, rfr_score, drr_score) on a 1.0–5.0+ float scale — more useful than integer categories for Pearson correlation, ranking within same category, and precise threshold analysis. Raw scores are absent with the v1 index. Relevant for CSRD ESRS E3-1 §27 (water stress) and E3-3 §38 (flood & drought risk).
Geocoding
GET /api/v1/geocode
High-performance global geocoding built on the Jiskta open-data engine — 467 million housenumbers, 62.7 million streets, 394,000 cities, 1.85 million postcodes sourced from OpenStreetMap. Supports forward geocoding (address → coordinates) and reverse geocoding (coordinates → address). 0.01 credits per call.
The engine uses io_uring O_DIRECT for zero-RAM lookups against a 1.6 GB binary index served from local NVMe — no external API calls, no rate limits, typically <1 ms response time.
| Parameter | Type | Required | Description |
|---|---|---|---|
q | string | one of q or lat+lon | Address string for forward geocoding. Format: Street Name [Number], City[, Postcode][, Country]. Country can be ISO2 code or full name. |
lat | float | with lon | Latitude for reverse geocoding (WGS-84) |
lon | float | with lat | Longitude for reverse geocoding (WGS-84) |
curl "https://api.jiskta.com/api/v1/geocode?q=Rue+de+Rivoli+15,+Paris" \ -H "X-API-Key: sk_live_your_key"
curl "https://api.jiskta.com/api/v1/geocode?lat=48.8599&lon=2.3477" \ -H "X-API-Key: sk_live_your_key"
{
"lat": 48.859932,
"lon": 2.347700,
"confidence": 1.00,
"street": "Rue de Rivoli",
"city": "Paris",
"postcode": "75001",
"country": "FR",
"source": "housenumber",
"fallback": ""
} | Field | Description |
|---|---|
lat / lon | Result coordinates (WGS-84) |
confidence | Match quality: 1.0 = exact housenumber, 0.7 = street centroid, 0.5 = postcode centroid, 0.3 = city centroid, 0.1 = country centroid |
source | What was matched: housenumber, street_centroid, postcode_centroid, city_centroid, country_centroid, reverse_stub |
fallback | Why a lower confidence level was used: no_housenumber, no_street, no_city, no_country, non_latin, or empty string on exact match |
street | Matched street name |
city | Matched city name |
postcode | Matched postcode |
country | ISO 2-letter country code |
Coverage: Global — 467M housenumbers from OpenStreetMap across all continents. Dense coverage in Western Europe, North America, and Australia. Sparser in parts of Asia and Africa where OSM housenumber data is less complete. Response time: typically <1 ms (io_uring O_DIRECT from local NVMe). Auth: X-API-Key header required. Credits: 0.01 per call.
Tabular Join — Spatial Aggregation
Aggregates raster climate data to EU administrative regions and returns per-region statistics along with optional cross-dataset computations (correlation, regression, top-N). Coverage is restricted to the EU domain (lat 30–72°N, lon 25°W–45°E). Ideal for ESG reporting, epidemiological analysis, policy dashboards, and academic research.
{
"region": "DE",
"time_range": { "start": "2022-01", "end": "2022-12" },
"datasets": [
{ "name": "no2", "source": "cams_no2" },
{ "name": "pm25", "source": "cams_pm2p5" }
],
"compute": [
{ "op": "mean", "input": "no2", "output": "no2_mean" },
{ "op": "mean", "input": "pm25", "output": "pm25_mean" },
{ "op": "pearson_r", "x": "no2", "y": "pm25",
"output": "r_no2_pm25", "include_n": true }
]
} Request fields
| Field | Type | Required | Description |
|---|---|---|---|
region | string | ✓ * | Named EU region preset: "DE", "FR", "EU", ISO2 country code, etc. Use region or bbox. |
bbox | object | ✓ * | Bounding box: lat_min, lat_max, lon_min, lon_max. Must be within EU domain (lat 30–72°N, lon 25°W–45°E). |
time_range | object | ✓ | Date range: start and end (YYYY-MM-DD or YYYY-MM) |
datasets | array | ✓ | List of raster sources. Each entry: name (your alias) + source (see table below) |
resolution | string | — | "nuts3" (default) | "country" |
compute | array | — | Cross-dataset operations (see below) |
Dataset sources
CAMS sources auto-route to EU 0.1° tiles inside Europe and Global 0.75° tiles outside. Use explicit cams_eu_* / cams_global_* aliases to override.
| Source | Variable | Unit | Coverage |
|---|---|---|---|
| 📡 CAMS Air Quality — auto-routing | |||
cams_no2 | Nitrogen dioxide | µg/m³ | EU 0.1° / Global 0.75° (auto) |
cams_pm2p5 | PM2.5 particles | µg/m³ | EU 0.1° / Global 0.75° (auto) |
cams_pm10 | PM10 particles | µg/m³ | EU 0.1° / Global 0.75° (auto) |
cams_o3 | Ozone | µg/m³ | EU 0.1° / Global 0.75° (auto) |
| 📡 CAMS Air Quality — explicit EU 0.1° (2013–present) | |||
cams_eu_no2 | Nitrogen dioxide | µg/m³ | EU only, 0.1° |
cams_eu_pm2p5 | PM2.5 particles | µg/m³ | EU only, 0.1° |
cams_eu_pm10 | PM10 particles | µg/m³ | EU only, 0.1° |
cams_eu_o3 | Ozone | µg/m³ | EU only, 0.1° |
| 🌍 CAMS Air Quality — explicit Global 0.75° (2020–present) | |||
cams_global_no2 / global_no2 | Nitrogen dioxide | µg/m³ | Global, 0.75° |
cams_global_pm2p5 / global_pm2p5 | PM2.5 particles | µg/m³ | Global, 0.75° |
cams_global_pm10 / global_pm10 | PM10 particles | µg/m³ | Global, 0.75° |
cams_global_o3 / global_o3 | Ozone | µg/m³ | Global, 0.75° |
| 🌤 ERA5 Meteorology (EU domain, 0.25°, 2013–present) | |||
era5_t2m / t2m | 2m temperature | K | EU domain, 0.25° |
era5_blh / blh | Boundary layer height | m | EU domain, 0.25° |
era5_tp / tp | Total precipitation | m/h | EU domain, 0.25° |
era5_u10 / u10 | 10m U-wind | m/s | EU domain, 0.25° |
era5_v10 / v10 | 10m V-wind | m/s | EU domain, 0.25° |
| 🌃 VIIRS Nighttime Lights (global, 0.005°, 2020–present) | |||
viirs_radiance / viirs / nightlights | Nighttime radiance | nW/cm²/sr | Global, 0.005° |
| 🏭 Human Settlement (global snapshots) | |||
ghsl_pop | Population density | persons/cell | Global |
ghsl_built | Built-up surface (total) | m² | Global |
ghsl_built_nres | Built-up surface (non-res) | m² | Global |
modis_urban | Urban land cover fraction | fraction 0–1 | Global |
| 🔥 MODIS Burned Area (global, 0.005°, 2020–present) | |||
modis_burned_area | Monthly burned area fraction | fraction 0–1 | Global, 0.005° (~500 m). MODIS MCD64A1 (CC BY 4.0). Monthly, 2020–present. Sparse. |
| 🌍 Fossil-Fuel CO₂ Emissions | |||
odiac_co2 | Fossil-fuel CO₂ emissions | gC/m²/month | Global, 0.01° (~1 km). ODIAC2024 (CC BY 4.0). 2020–2023. |
| 🌿 CAMS GHG Inversion — Greenhouse Gas Surface Flux (1°, global, monthly, 2016–present) | |||
ghg_co2 / atmospheric_co2 | CO₂ net surface flux | kgC/m²/month | Global, 1°. Biogenic + ocean + fossil net flux. ECMWF CAMS Inversion (CarbonTracker-EU). Copernicus open data. ESRS E1. |
ghg_ch4 / atmospheric_ch4 / methane | CH₄ surface flux | kg/m²/s | Global, 1°. Total methane emissions (agriculture, wetlands, fossil). ECMWF CAMS Inversion. Copernicus open data. ESRS E1. |
ghg_n2o | N₂O surface flux | kgN/m²/month | Global, ~0.7°. Nitrous oxide (agriculture, soils). ECMWF CAMS Inversion. Copernicus open data. ESRS E1. |
| 💧 WRI Aqueduct 4.0 Water Risk (static, global, 0.1°) | |||
aqueduct_bws / water_stress | Baseline Water Stress | score 1–5 | Global. Low (1) … Extremely High (5). ESRS E3-1 §27. |
aqueduct_bwd / water_depletion | Baseline Water Depletion | score 1–5 | Global. Low (1) … Extremely High (5). ESRS E3-1 §27. |
aqueduct_rfr / flood_risk | Riverine Flood Risk | score 1–5 | Global. Low (1) … Extremely High (5). ESRS E3-3 §38. |
aqueduct_drr / drought_risk | Drought Risk | score 1–5 | Global. Low (1) … Extremely High (5). ESRS E3-3 §38. |
Compute operations
| op | Fields | Output | Description |
|---|---|---|---|
mean | input | scalar | Mean across all NUTS3 units |
sum | input | scalar | Sum across all NUTS3 units |
min / max | input | scalar | Min / max value |
count | input | integer | Number of NUTS3 units with data |
pearson_r | x, y, include_n | object: r, r2, n | Cross-region Pearson correlation |
top_n | input, n, direction | array | Top/bottom N regions by value |
{
"status": "success",
"query_time_ms": 2,
"credits_used": 6,
"credits_remaining": 14800,
"n_units": 255,
"spatial_resolution": "nuts3",
"n_raster_cols": 2,
"no2_mean": 9.62,
"pm25_mean": 8.69,
"r_no2_pm25": { "r": 0.553, "r2": 0.306, "n": 255, "interpretation": "moderate positive" },
"units": [
{ "nuts3_id": "FR101", "nuts3_name": "Paris", "country": "FR",
"no2": 24.56, "pm25": 14.8, "n_cells": 1 },
...
]
} Credits: 1 credit per dataset per tile scanned. Germany (4 tiles) with 2 CAMS datasets for 12 months costs 96 credits.
Coverage: EU domain (lat 30–72°N, lon 25°W–45°E). CAMS EU tiles at 0.1°, ERA5 at 0.25°, 2013–present. NUTS3 resolution (1,514 regions) or country resolution. Max 500 tile reads per request — use country-level queries or reduce time range for larger regions.
Redeem Voucher
POST /api/v1/redeem
Redeem a voucher code to add credits to your account. Requires authentication via X-API-Key header.
Request
curl -X POST https://api.jiskta.com/api/v1/redeem \
-H "X-API-Key: sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"code": "WELCOME-2025-ABC"}' Request Body
{
"code": "WELCOME-2025-ABC"
} Response
{
"status": "success",
"credits_added": 500,
"credits_remaining": 500,
"description": "Welcome bonus"
} | Status | Meaning |
|---|---|
200 | Voucher redeemed successfully |
401 | Invalid or missing API key |
404 | Invalid voucher code |
409 | Voucher already redeemed |
410 | Voucher has expired |
Buy Credits
POST /api/v1/checkout
Create a Paddle checkout session to purchase credits. A Paddle payment overlay opens on the dashboard — Paddle handles VAT and billing for all countries automatically.
For EU B2B customers: include vat_number to enable reverse charge (0% VAT). Pre-fill these from your Billing Details on the dashboard.
Request Body
{
"package": "starter",
"api_key_id": "your-api-key-uuid",
"company_name": "Acme Corp",
"vat_number": "NL123456789B01",
"billing_country": "NL"
} | Field | Type | Required | Description |
|---|---|---|---|
package | string | ✅ | One of: starter (€10), professional (€50), enterprise (€200) |
api_key_id | string | ✅ | Your API key UUID (from account setup) |
company_name | string | — | Legal company name — printed on the Paddle invoice |
vat_number | string | — | EU VAT ID (e.g. NL123456789B01). Paddle validates against VIES and applies reverse charge (0% VAT) for valid EU B2B numbers. |
billing_country | string | — | ISO 3166-1 alpha-2 country code (e.g. NL, DE, FR). Used by Paddle for correct tax calculation. |
Response
{
"transaction_id": "txn_01abc..."
} The dashboard uses this transaction_id to open the Paddle checkout overlay via Paddle.js. Credits are added automatically after successful payment.
Regenerate API Key
If your API key is compromised or you need a fresh key, you can regenerate it from the Dashboard.
How it works
- Click "Regenerate Key" on the dashboard
- Your old key is immediately deactivated — any requests using it will return
401 - A new
sk_live_key is generated - Your credit balance is fully preserved (transferred to the new key)
- The new key is shown once — copy it and store it safely
Technical Details (Supabase RPC)
For advanced integrations, the regeneration is powered by a Supabase RPC function called from the authenticated client:
const { data, error } = await supabase.rpc('regenerate_api_key');
// Returns: [{ raw_key: "sk_live_...", new_prefix: "sk_live_abcd" }] Important: This function requires an authenticated Supabase session. It uses auth.email() to identify the user, so it can only regenerate your own key.
Python SDK
Install with pip:
⭐ GitHub
pip install jiskta pip install "jiskta[pandas]" # optional — returns pandas DataFrames
Daily data over Paris, 2023
from jiskta import JisktaClient
client = JisktaClient(api_key="sk_live_your_key_here")
# Daily NO₂ and PM2.5 over Paris, full year 2023
df = client.query(
lat=(48.7, 49.0),
lon=(2.2, 2.5),
start="2023-01",
end="2023-12",
variables=["no2", "pm2p5"],
aggregate="daily",
)
print(df.head())
# lat lon date no2_mean pm2p5_mean
# 0 48.7500 2.250 2023-01-01 12.34 8.21 Summary statistics and exceedance
# Stats (no DataFrame needed)
result = client.stats(
area="paris",
start="2023-01",
end="2023-12",
variables=["no2"],
)
print(result["output"])
# Rows matched: 8760\nMin: 1.2\nMax: 78.4\nAverage: 14.3
# Hours above WHO guideline (25 µg/m³)
df = client.query(
lat=(48.7, 49.0), lon=(2.2, 2.5),
start="2023-01", end="2023-12",
variables=["no2"],
aggregate="exceedance",
threshold=25.0,
)
print(df.columns.tolist())
# ['lat', 'lon', 'hours_above', 'total_hours', 'pct_above'] Error handling
from jiskta import JisktaClient, AuthError, InsufficientCreditsError, RateLimitError
try:
df = client.query(...)
except AuthError:
print("Invalid API key")
except InsufficientCreditsError:
print("Buy more credits at https://jiskta.com/pricing")
except RateLimitError:
print("Server busy — retry with exponential backoff") Node.js SDK
Install with npm (zero runtime dependencies):
⭐ GitHub
npm install jiskta
Daily data over Paris, 2023
import { JisktaClient } from "jiskta";
// const { JisktaClient } = require("jiskta"); // CommonJS
const client = new JisktaClient("sk_live_your_key_here");
// Daily NO₂ and PM2.5 over Paris, full year 2023
const rows = await client.query({
lat: [48.7, 49.0],
lon: [2.2, 2.5],
start: "2023-01",
end: "2023-12",
variables: ["no2", "pm2p5"],
aggregate: "daily",
});
console.log(rows[0]);
// { lat: 48.75, lon: 2.25, date: '2023-01-01', no2_mean: 12.34, pm2p5_mean: 8.21 } Point query and monthly stats
// Single grid cell — pass a number instead of [min, max]
const rows = await client.query({
lat: 48.85, // snaps to nearest 0.1° grid cell
lon: 2.35,
start: "2023-01",
end: "2023-12",
variables: ["no2"],
aggregate: "monthly",
});
// Summary stats
const result = await client.stats({
lat: [48.7, 49.0], lon: [2.2, 2.5],
start: "2023-01", end: "2023-01",
variables: ["no2"],
});
console.log(result.output);
// "Rows matched: 744\nMin: 5.2\nMax: 38.1\nAverage: 14.3" Error handling
import { JisktaClient, AuthError, InsufficientCreditsError, RateLimitError } from "jiskta";
try {
const rows = await client.query({ ... });
} catch (err) {
if (err instanceof AuthError) console.error("Invalid API key");
else if (err instanceof InsufficientCreditsError) console.error("Buy more credits");
else if (err instanceof RateLimitError) console.error("Retry after a short wait");
} MCP Server
Use Jiskta directly inside AI assistants — Claude Desktop, Cursor, Cline, and any other tool that supports Model Context Protocol. The server runs on your machine and calls the Jiskta API using your key. Zero setup with uvx.
// claude_desktop_config.json (Claude Desktop)
{
"mcpServers": {
"jiskta": {
"command": "uvx",
"args": ["jiskta-mcp"],
"env": { "JISKTA_API_KEY": "sk_live_..." }
}
}
} Once connected, ask things like "What was PM2.5 in Brussels in 2023?" or "Is there industrial pollution risk near this address?"
Available tools: query_climate, query_climate_point, estimate_query_cost, geocode, reverse_geocode, enrich_location, water_risk, find_facilities, get_coverage, spatial_link.
Jupyter Notebooks & Examples
Ready-to-run examples covering air quality analysis, ERA5 climate trends, exceedance mapping, and a Node.js interactive map explorer:
- github.com/jiskta/jiskta-examples — Jupyter notebooks (Python) + Node.js map explorer
- github.com/jiskta/jiskta-python — Python SDK source
- github.com/jiskta/jiskta-node — Node.js SDK source
- github.com/jiskta/jiskta-mcp — MCP server source
cURL
# Hourly PM2.5 in London, March 2023 — save to CSV curl "https://api.jiskta.com/api/v1/climate/query?\ lat_min=51.3&lat_max=51.7&\ lon_min=-0.5&lon_max=0.3&\ time_start=2023-03-01&time_end=2023-04-01&\ variables=pm2p5&format=csv&aggregate=daily" \ -H "X-API-Key: sk_live_your_key_here" \ | python3 -c "import sys,json; print(json.load(sys.stdin)['output'])" > london_pm25_march.csv # Exceedance: hours of NO₂ above WHO guideline (25 µg/m³), Berlin, 2023 curl "https://api.jiskta.com/api/v1/climate/query?\ lat_min=52.3&lat_max=52.7&\ lon_min=13.0&lon_max=13.7&\ time_start=2023-01-01&time_end=2023-12-31&\ variables=no2&threshold=25" \ -H "X-API-Key: sk_live_your_key_here" \ | python3 -c "import sys,json; print(json.load(sys.stdin)['output'])"
Data Quality & Coverage
CAMS data uses a three-tier quality system. ERA5 data is the definitive ECMWF global atmospheric reanalysis and is considered fully validated for all available months.
CAMS EU quality tiers
| Tier | API value | Description | Lag |
|---|---|---|---|
| Validated | reanalysis | Peer-reviewed, bias-corrected against ground station observations. Used as reference data in compliance and ESG reporting contexts. | ~18–24 months after reference period |
| Interim | interim_reanalysis | Passes preliminary QA checks. Suitable for trend analysis and monitoring. Automatically upgraded to validated when ECMWF publishes the full reanalysis. | ~4 months after reference period |
| NRT | nrt_analysis | Near Real-Time CAMS analysis. Covers the gap between interim reanalysis and today (~1–4 month lag window). Available within ~24 hours. Automatically replaced by interim/validated reanalysis when published. | ~24 hours after reference period |
CAMS Global quality tiers
| Tier | API value | Description | Lag |
|---|---|---|---|
| Validated (EAC4) | validated_reanalysis | ECMWF EAC4 global reanalysis. 0.75° resolution, 3-hourly. Highest quality global dataset. | ~7 months after reference period |
| NRT (Global) | nrt_analysis | CAMS Global Atmospheric Composition Forecasts. Fills the 7-month EAC4 lag. Same 0.75° global grid. Automatically replaced by EAC4 when it becomes available. | ~24 hours after reference period |
The data type for a queried month is returned in the API response metadata. Queries work identically regardless of tier — you don't need to specify anything.
Coverage endpoint
GET /api/v1/coverage
Returns the full month-by-month availability index. No authentication required.
curl https://api.jiskta.com/api/v1/coverage
{
"status": "ok",
"validated_lag_months": 18,
"interim_lag_months": 4,
"coverage": {
"nitrogen_dioxide": {
"2023-01": {
"type": "reanalysis",
"downloaded_at": "2026-02-01T06:12:04Z",
"size_mb": 187.4
},
"2025-10": {
"type": "interim_reanalysis",
"downloaded_at": "2026-02-01T06:18:31Z",
"size_mb": 191.2
}
},
"particulate_matter_2.5um": { "..." }
}
} The interactive visual calendar is available on the Data Coverage page.
Automatic monthly sync
A cron job runs on the 1st of each month and:
- Adds any newly available interim months (ECMWF publishes ~4 months after the reference period)
- Replaces interim files with validated reanalysis for months that have now passed the ~18-month lag
- Regenerates the affected QKPT3 tiles automatically
You will never need to re-query historical data — upgrades happen transparently. If you need to know the tier of a specific month before querying, check /api/v1/coverage.
Error Handling
The API uses standard HTTP status codes:
| Status | Meaning | Action |
|---|---|---|
200 | Success | Parse the response data |
400 | Bad Request | Check your query parameters; or result exceeded 500K rows — use aggregate=daily or smaller range |
401 | Unauthorized | Check your API key |
402 | Payment Required | Insufficient credits — buy more |
429 | Too Many Requests | Server under load and your key is above its fair share — retry after 500ms with exponential backoff |
500 | Server Error | Retry after a moment; contact support if persistent |
Error response format
{
"error": "Insufficient credits",
"credits_remaining": 3,
"credits_required": 8
}