Continuuiti’s REST APIs give you programmatic access to seven analytical surfaces — geocoding, climate risk, land cover, biodiversity, flood depth, damage estimation, and hazard history — for any geographic coordinate worldwide. Base URL: https://api.continuuiti.com/api/v1/. This page is the complete developer reference: every endpoint, every parameter, every response field, with cURL, Python, and Node code examples for every API.
Getting Started
Get your API key
Every Continuuiti Tools account includes a personal API key. To get yours:
- Create an account at tools.continuuiti.com with your business email.
- Open Platform API in the sidebar. Your key (
sk_live_...) is generated automatically, so copy it from there. - Pass it on every request in the
X-API-Keyheader.
You can regenerate the key at any time from the same page. The old key stops working immediately.
402 with your current balance, and an access request is logged for our team automatically. Reading your results is always free.Every Continuuiti API request goes to https://api.continuuiti.com/api/v1/ with an X-API-Key header. Below is the simplest possible request — geocoding an address — that you can run in your terminal right now after generating an API key.
curl -X POST https://api.continuuiti.com/api/v1/geocode/ \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"address": "350 Fifth Avenue, New York, NY"}'
You’ll get a JSON response with the matched coordinates, formatted address, and a structured quality envelope describing how confident the match is:
{
"id": 1,
"original_address": "350 Fifth Avenue, New York, NY",
"formatted_address": "350 5th Ave, New York, NY 10118, USA",
"latitude": "40.7484405",
"longitude": "-73.9856644",
"quality": {"score": 1.0, "status": "PASSED", "issues": [], "match_type": "building"},
"created_at": "2026-04-27T10:19:05Z"
}
Pick the API you need: Climate Risk, Geocoder, LULC, Biodiversity, Damage Estimation, Flood Depth, or Hazard History. The cross-cutting sections below (Authentication, Batch & Async, Sandbox, Errors, Rate Limits) apply to all seven APIs.
Authentication
Every Continuuiti API request requires an API key passed in the X-API-Key HTTP header. There is no OAuth flow, no Bearer token, and no query-parameter authentication — only the header. Generate your API key from the Continuuiti API dashboard.
Header pattern
Add a single header to every request. The same key authenticates against every endpoint across all seven APIs.
curl https://api.continuuiti.com/api/v1/exports/geocoding/reports/ \
-H "X-API-Key: YOUR_API_KEY"
import requests
headers = {"X-API-Key": "YOUR_API_KEY"}
response = requests.get(
"https://api.continuuiti.com/api/v1/exports/geocoding/reports/",
headers=headers,
)
data = response.json()
const response = await fetch(
"https://api.continuuiti.com/api/v1/exports/geocoding/reports/",
{ headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await response.json();
Authentication errors
Requests without an API key, or with an invalid or revoked key, return 401 Unauthorized:
HTTP/2 401
Content-Type: application/json
{"error": "Invalid or missing API key"}
Response fields
Responses contain the documented fields plus a few platform extras: report_id and report_url (a deep link to the report in your dashboard), tools_batch_id and batch_url for batches, and credits_remaining after each submission. Results endpoints return 409 while a job is still running.
Batch & Async Pattern
Several APIs process work asynchronously because each location involves multi-second computation. LULC, Climate Risk, and Flood Depth are always asynchronous; the Geocoder (batches over five addresses), Hazard History (batches), and Carbon Project Screening also use this pattern. They share an identical flow: submit → poll → fetch results. Biodiversity screening is bundled into LULC, so it has no separate job.
The flow
- POST to
/{api}/batch/analyze/with your locations array. Returns202 Acceptedwith abatch_idimmediately — processing has not started yet. - GET
/{api}/batch/{batch_id}/repeatedly to poll progress. Status moves through PROCESSING and ends at COMPLETED, PARTIAL, or FAILED. - GET
/{api}/batch/{batch_id}/results/?page=1&page_size=100to fetch paginated results once the batch reaches a terminal state.
Status values
| Status | Meaning |
|---|---|
PENDING |
Batch created, processing not yet started |
PROCESSING |
Jobs actively running across worker pool |
COMPLETED |
All jobs reached a terminal state successfully |
PARTIAL |
Some jobs failed permanently; some succeeded. Results endpoint returns both |
FAILED |
All jobs failed. Inspect error on the batch record |
Per-module variations: single-location jobs never report PARTIAL (a partly-successful single job is stored as COMPLETED). LULC batches report RUNNING instead of PROCESSING; Geocoder batches have no PARTIAL.
Automatic retry behavior
Async APIs retry transient failures automatically before marking a job as failed. The status response surfaces this so your polling logic doesn’t see a transient FAILED that becomes COMPLETED on retry.
| Field | Where it appears | Meaning |
|---|---|---|
retry_info |
Job status | null on first attempt; when present, contains {"attempt": N, "max_attempts": M} indicating the job is being auto-retried |
retrying |
Batch progress | Count of jobs that failed but have retries remaining (auto-recovering) |
failed |
Batch progress | Count of permanently failed jobs (all retries exhausted) |
While a job is being retried, its status field shows PENDING rather than FAILED, so client polling logic can simply continue.
Polling cadence
Poll every 5 to 10 seconds. Faster polling consumes API quota with no benefit; slower polling delays your application’s response to completion. For long-running batches (LULC at 5,000 locations runs 30+ minutes), consider exponential backoff after the first minute.
import requests
import time
API_BASE = "https://api.continuuiti.com/api/v1"
HEADERS = {"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"}
# 1. Submit batch
response = requests.post(
f"{API_BASE}/climate-risk/batch/analyze/",
headers=HEADERS,
json={
"locations": [
{"lat": 28.6139, "lon": 77.2090, "label": "Delhi"},
{"lat": 19.0760, "lon": 72.8777, "label": "Mumbai"},
],
"scenarios": ["ssp245", "ssp585"],
"horizons": ["baseline", "2050"],
},
)
batch_id = response.json()["batch_id"]
print(f"Submitted batch: {batch_id}")
# 2. Poll until terminal
while True:
status_response = requests.get(f"{API_BASE}/climate-risk/batch/{batch_id}/", headers=HEADERS)
state = status_response.json()
print(f"Status: {state['status']} | Progress: {state['progress']['percent_complete']}%")
if state["status"] in ("COMPLETED", "PARTIAL", "FAILED"):
break
time.sleep(10)
# 3. Fetch results (paginated)
page = 1
while True:
results_response = requests.get(
f"{API_BASE}/climate-risk/batch/{batch_id}/results/",
headers=HEADERS,
params={"page": page, "page_size": 100},
)
payload = results_response.json()
for result in payload["results"]:
print(f"{result['label']}: {result['result']['composite_score']}")
if page >= payload["total_pages"]:
break
page += 1
Retrieving Your Results
Alongside the per-job results endpoints, every module exposes a uniform read-only export API for listing and fetching your stored reports and batches.
/api/v1/exports/{module}/reports/List your reports for a module (paginated and filterable).
/api/v1/exports/{module}/reports/{id}/Fetch one full report with its results.
/api/v1/exports/{module}/batches/List your batches for a module.
/api/v1/exports/{module}/batches/{id}/Fetch one batch and its reports.
{module} is one of lulc, climate-risk, geocoding, damage-curves, flood-depth, or hazard-history. Carbon screening exports use /api/v1/exports/carbon-screening/sites/, /api/v1/exports/carbon-screening/sites/{id}/, and /api/v1/exports/carbon-screening/parcels/{id}/.
Query parameters: status, page, page_size (max 200), created_after, created_before, and ordering.
Sandbox / Dry-Run Mode
Every endpoint that consumes computation supports a sandbox mode that returns deterministic mock data without burning API quota. Use it for integration tests, CI pipelines, frontend development, or any time you need to exercise the API contract without hitting the real compute path. Sandbox mode is the recommended way to validate your integration before going to production.
Sandbox runs are free. No credits are consumed, even at zero balance, and each run is labelled with a Sandbox badge in your dashboard so it never mixes with real results.
How to enable sandbox mode
For POST endpoints, add "dry_run": true to the request body. For GET endpoints (like Hazard History queries), add ?dry_run=true to the query string. The same input always returns the same output, so you can write deterministic snapshot tests against sandbox responses.
curl -X POST https://api.continuuiti.com/api/v1/geocode/batch/ \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"addresses": [{"address": "350 Fifth Avenue, New York, NY"}],
"dry_run": true
}'
import requests
response = requests.post(
"https://api.continuuiti.com/api/v1/climate-risk/batch/analyze/",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"locations": [{"lat": 28.6139, "lon": 77.2090, "label": "Delhi"}],
"scenarios": ["ssp245"],
"horizons": ["2050"],
"dry_run": True,
},
)
batch = response.json()
# Sandbox responses include [DRY RUN] prefix in metadata
assert "[DRY RUN]" in str(batch.get("metadata", {}))
const response = await fetch(
"https://api.continuuiti.com/api/v1/hazard-history/floods/query/?lat=29.76&lon=-95.37&dry_run=true",
{ headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await response.json();
console.log(data.metadata); // includes [DRY RUN] prefix
What sandbox returns
Sandbox responses match the production response shape exactly — same fields, same types, same nesting. Only the values are deterministic mocks. Metadata fields include a [DRY RUN] prefix so you cannot accidentally treat sandbox results as real data:
{
"metadata": {
"method_version": "[DRY RUN] climate_risk_v1.0",
"processing_time_sec": 0.05,
...
}
}
Errors & Status Codes
Continuuiti APIs follow standard HTTP status codes. The response body is always JSON; error responses include an error field with a human-readable message.
HTTP status codes
| Code | Meaning | What to do |
|---|---|---|
200 OK |
Successful sync request | Parse the response body |
201 Created |
Resource created (geocoding, damage estimation) | Parse response; id field is the new resource ID |
202 Accepted |
Async job or batch submitted; processing not started | Begin polling the status endpoint with the returned job_id or batch_id |
400 Bad Request |
Malformed request: missing required field, invalid coordinate, oversized batch (>5,000 items), invalid enum value | Inspect the error field; fix the request and retry. Do not retry blindly |
401 Unauthorized |
API key missing, invalid, or revoked | Verify X-API-Key header; check key in dashboard |
402 Payment Required |
Insufficient credits for the request | Top up credits, then retry. The body includes credits_required and credits_available |
404 Not Found |
Resource not found (job_id, batch_id, geocode id, occupancy code, country code) | Verify the ID is correct; for geocoding, address may have failed to resolve |
405 Method Not Allowed |
Wrong HTTP method for the endpoint | Use the documented method (POST to submit, GET to poll or fetch) |
409 Conflict |
Results requested while the job is still running | Keep polling the jobs endpoint; fetch results once COMPLETED |
429 Too Many Requests |
Rate limit exceeded | Back off and retry. Reduce concurrency |
503 Service Unavailable |
Transient upstream issue (rare) | Retry with exponential backoff. The async retry layer handles most transients automatically |
Error response shape
{
"error": "Invalid request: 'lat' must be between -90 and 90"
}
Rate Limits
The default limits are 60 requests per minute and 500 requests per hour, per account. When you exceed them, the API returns 429 with a Retry-After header indicating how long to wait.
These limits suit typical integration workloads, including batch submissions of up to 5,000 locations per request. Burst patterns and sustained high-volume usage are supported on commercial plans.
For sustained workloads beyond the defaults, custom rate limits, dedicated capacity, or SLA-backed usage agreements, book a demo to discuss your requirements.
Climate Risk API
The Continuuiti Climate Risk API assesses 12 physical climate hazards across SSP2-4.5 and SSP5-8.5 emissions scenarios and four time horizons (baseline, 2030, 2040, 2050) for any geographic coordinate. Returns hazard ratings, composite risk score with confidence, and a top-risks summary aligned with TCFD physical risk disclosure categories.
12 Climate Hazards
Every climate risk assessment evaluates all 12 hazards by default. Pass the optional hazards array to limit a job to specific hazards.
| Hazard | Category | What it measures | Typical units |
|---|---|---|---|
heat_wave |
Temperature | Days exceeding the 95th percentile temperature | days/year |
cold_stress |
Temperature | Days below the 5th percentile temperature | days/year |
temperature_change |
Temperature | Annual mean temperature shift from baseline | °C |
drought |
Precipitation | Standardized precipitation index dry-month frequency | months/year |
extreme_rainfall |
Precipitation | Days exceeding the 99th percentile precipitation | days/year |
precipitation_change |
Precipitation | Annual precipitation shift from baseline | % |
severe_storm |
Compound | Surface wind extremes combined with tropical cyclone exposure | events/year |
wildfire |
Compound | Hot-dry-windy conditions combined with fuel availability | hazard score |
landslide |
Compound | Terrain susceptibility weighted by rainfall triggers | hazard score |
river_flood |
Hydrological | Projected runoff change combined with terrain susceptibility | hazard score |
sea_level_rise |
Hydrological | Projected sea level rise relative to local elevation | meters |
water_stress |
Hydrological | Basin-level water supply-demand stress ratio | stress ratio (0–5) |
Risk Levels
Each hazard returns a categorical rating on a 5-tier scale, plus a numeric score (1–5) for aggregation. The composite risk score is the weighted aggregate across all 12 hazards.
| Rating | Score | Interpretation |
|---|---|---|
| Low | 1 | Minimal climate exposure; no adaptation action required |
| Moderate | 2 | Some exposure; routine monitoring |
| High | 3 | Significant exposure; adaptation planning recommended |
| Severe | 4 | Major exposure; adaptation investment likely required |
| Extreme | 5 | Critical exposure; site-level engineering response warranted |
Scenarios
Climate projections use two Shared Socioeconomic Pathways:
- SSP2-4.5 (moderate emissions): middle-of-the-road pathway, consistent with current policy pledges. Standard central planning assumption for risk assessment and regulatory disclosure.
- SSP5-8.5 (high emissions): fossil-fueled development with limited climate policy. Standard worst-case stress-test scenario for TCFD and IFRS S2 disclosure.
Time Horizons
Four time horizons are available. Each represents a 20-year averaging window centered on the labeled year (e.g., “2050” = 2040–2059 average), capturing the underlying climate trend rather than year-to-year fluctuation.
| Horizon | Period | Use case |
|---|---|---|
baseline |
1980–2010 historical reference | “Current climate” anchor against which futures are compared |
2030 |
2020–2039 average | Near-term planning horizon |
2040 |
2030–2049 average | Medium-term planning horizon |
2050 |
2040–2059 average | Standard TCFD/IFRS S2 long-term horizon |
Endpoints
/api/v1/climate-risk/analyze/Submit a single-location climate risk job. Returns 202 Accepted with a job_id immediately; processing runs asynchronously.
Request body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
lat |
number | Yes | — | Latitude (-90 to 90) |
lon |
number | Yes | — | Longitude (-180 to 180) |
scenarios |
array | No | ["ssp245", "ssp585"] |
SSP scenarios to analyze. Valid values: ssp245, ssp585 |
horizons |
array | No | ["baseline", "2030", "2040", "2050"] |
Time horizons. Valid values: baseline, 2030, 2040, 2050 |
hazards |
array | No | (all 12) | Limit assessment to specific hazards. See 12 hazards table |
Response (202 Accepted)
{
"job_id": "climate_a1b2c3d4e5f6",
"status": "PENDING",
"message": "Climate risk assessment queued for processing"
}
Errors
| Status | Reason |
|---|---|
400 |
Invalid lat/lon range, invalid scenario value, invalid horizon value |
/api/v1/climate-risk/jobs/{job_id}/Get the status and progress of a climate risk job.
Response (200 OK)
{
"job_id": "climate_a1b2c3d4e5f6",
"status": "RUNNING",
"lat": 28.6139,
"lon": 77.2090,
"scenarios": ["ssp245", "ssp585"],
"horizons": ["baseline", "2050"],
"progress": {
"hazards_completed": 7,
"hazards_total": 12,
"current_hazard": "severe_storm"
},
"created_at": "2026-04-27T10:00:00Z",
"updated_at": "2026-04-27T10:02:00Z"
}
Status values: PENDING, RUNNING, COMPLETED, FAILED (see cross-cutting status table).
Errors
| Status | Reason |
|---|---|
404 |
Job not found |
/api/v1/climate-risk/results/{job_id}/Get the full results of a completed climate risk assessment. While the job is still running, this returns 409 with a short status preview ({"error": "Job not completed", "status": "RUNNING"}); poll the jobs endpoint until the job is COMPLETED, then fetch results.
Response (200 OK) — truncated for readability
{
"job_id": "climate_a1b2c3d4e5f6",
"status": "COMPLETED",
"input": {
"lat": 28.6139, "lon": 77.2090,
"scenarios": ["ssp245", "ssp585"],
"horizons": ["baseline", "2050"]
},
"location_context": {
"elevation_m": 212.6,
"distance_to_coast_km": 1150.0,
"terrain_type": "flat",
"land_cover": "built_area"
},
"composite_score": {
"ssp245": {
"baseline": {"score": 1.0, "rating": "Low", "confidence": 0.85},
"2050": {"score": 1.93, "rating": "Moderate", "confidence": 0.82}
},
"ssp585": {
"baseline": {"score": 1.0, "rating": "Low", "confidence": 0.85},
"2050": {"score": 2.12, "rating": "Moderate", "confidence": 0.80}
}
},
"hazards": {
"heat_wave": {
"metric": "heat_events",
"metric_unit": "days/year",
"baseline_value": 12.5,
"risk_matrix": {
"ssp245": {"baseline": "Moderate", "2050": "High"},
"ssp585": {"baseline": "Moderate", "2050": "Severe"}
},
"values": {
"ssp245": {"baseline": 12.5, "2050": 22.3},
"ssp585": {"baseline": 12.5, "2050": 28.7}
},
"context": {"threshold_celsius": 38.5},
"status": "OK"
}
// ... 11 more hazards
},
"top_risks": [
{"hazard": "heat_wave", "scenario": "ssp585", "horizon": "2050", "rating": "Severe"},
{"hazard": "precipitation_change", "scenario": "ssp585", "horizon": "2050", "rating": "High"},
{"hazard": "river_flood", "scenario": "ssp585", "horizon": "2050", "rating": "High"}
],
"metadata": {
"baseline_period": "1980-2010",
"generated_at": "2026-04-27T10:05:00Z",
"method_version": "climate_risk_v1.0",
"processing_time_sec": 180
},
"error": null
}
Errors
| Status | Reason |
|---|---|
202 |
Job still processing; response includes progress object |
404 |
Job not found |
/api/v1/climate-risk/batch/analyze/Submit a batch of up to 5,000 locations for climate risk assessment. Same flow as single-location: 202 Accepted with a batch_id, then poll. See Batch & Async Pattern for the full flow.
Request body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
locations |
array | Yes | — | Array of {lat, lon, label?} objects (max 5,000) |
locations[].lat |
number | Yes | — | Latitude (-90 to 90) |
locations[].lon |
number | Yes | — | Longitude (-180 to 180) |
locations[].label |
string | No | — | User-provided label that round-trips back in the results |
scenarios |
array | No | ["ssp245", "ssp585"] |
Applied to all locations |
horizons |
array | No | ["baseline", "2030", "2050"] |
Applied to all locations |
dry_run |
boolean | No | false |
Return mock results without consuming quota. See Sandbox |
Response (202 Accepted)
{
"batch_id": "batch_climate_a1b2c3d4",
"status": "PROCESSING",
"total_count": 3,
"dry_run": false,
"status_url": "/api/v1/climate-risk/batch/batch_climate_a1b2c3d4/",
"results_url": "/api/v1/climate-risk/batch/batch_climate_a1b2c3d4/results/"
}
Errors
| Status | Reason |
|---|---|
400 |
Missing locations array, exceeds 5,000 location limit, invalid coordinates, invalid scenario/horizon value |
/api/v1/climate-risk/batch/{batch_id}/Get batch status and progress. Same shape as the cross-API batch status pattern.
Response (200 OK)
{
"batch_id": "batch_climate_a1b2c3d4",
"status": "PROCESSING",
"progress": {
"total": 3,
"completed": 2,
"failed": 0,
"partial": 0,
"running": 1,
"pending": 0,
"percent_complete": 66.7
},
"dry_run": false,
"created_at": "2026-04-27T10:00:00Z",
"started_at": "2026-04-27T10:00:05Z",
"completed_at": null
}
/api/v1/climate-risk/batch/{batch_id}/results/Get paginated batch results. Each result item carries the original label and the full per-location climate risk payload.
Query parameters
| Param | Type | Default | Description |
|---|---|---|---|
page |
integer | 1 | Page number |
page_size |
integer | 100 | Results per page (max 500) |
status |
string | — | Filter by job status: COMPLETED, FAILED |
Response Field Reference
The complete reference for every field returned by the climate risk results endpoint.
| Field path | Type | Meaning |
|---|---|---|
composite_score.{scenario}.{horizon}.score |
number (1–5) | Aggregate risk score across all 12 hazards |
composite_score.{scenario}.{horizon}.rating |
string | Categorical rating: Low / Moderate / High / Severe / Extreme |
composite_score.{scenario}.{horizon}.confidence |
number (0–1) | Confidence in the aggregate. Lower with limited input data |
hazards.{hazard}.metric |
string | Underlying metric name (e.g., heat_events) |
hazards.{hazard}.metric_unit |
string | Unit for the metric (e.g., days/year, meters, %) |
hazards.{hazard}.baseline_value |
number | Reference-period value for this hazard at this location |
hazards.{hazard}.risk_matrix.{scenario}.{horizon} |
string | Categorical rating for this hazard at this scenario/horizon |
hazards.{hazard}.values.{scenario}.{horizon} |
number | Numeric metric value at this scenario/horizon |
hazards.{hazard}.context |
object | Hazard-specific context (e.g., threshold_celsius for heat hazards) |
hazards.{hazard}.status |
string | Per-hazard status: OK, NO_DATA, FAILED |
top_risks[] |
array | Top 3–5 highest-rated hazard/scenario/horizon combinations for quick triage |
location_context.elevation_m |
number | Elevation above sea level (meters) |
location_context.distance_to_coast_km |
number | Great-circle distance to nearest coastline |
location_context.terrain_type |
string | flat, hilly, mountainous |
location_context.land_cover |
string | Dominant land cover at the location (e.g., built_area, cropland, forest) |
metadata.baseline_period |
string | Reference period for baseline values (typically 1980-2010) |
metadata.method_version |
string | Versioned methodology identifier |
metadata.processing_time_sec |
number | Server-side processing time |
Code Examples
Submit a single-location climate risk assessment for Delhi under SSP2-4.5 and SSP5-8.5 at 2050:
curl -X POST https://api.continuuiti.com/api/v1/climate-risk/analyze/ \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"lat": 28.6139,
"lon": 77.2090,
"scenarios": ["ssp245", "ssp585"],
"horizons": ["baseline", "2050"]
}'
import requests
response = requests.post(
"https://api.continuuiti.com/api/v1/climate-risk/analyze/",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"lat": 28.6139,
"lon": 77.2090,
"scenarios": ["ssp245", "ssp585"],
"horizons": ["baseline", "2050"],
},
)
job_id = response.json()["job_id"]
const response = await fetch(
"https://api.continuuiti.com/api/v1/climate-risk/analyze/",
{
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
lat: 28.6139,
lon: 77.2090,
scenarios: ["ssp245", "ssp585"],
horizons: ["baseline", "2050"],
}),
}
);
const { job_id } = await response.json();
Worked End-to-End Example
Real integrations stitch APIs together. The example below geocodes an address, submits a climate risk assessment, polls until completion, and prints the top three projected hazards under SSP5-8.5 at 2050. This is the consultant-ICP “first useful run.”
import requests
import time
API_BASE = "https://api.continuuiti.com/api/v1"
HEADERS = {"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"}
# Step 1: Geocode an address to coordinates
address = "350 Fifth Avenue, New York, NY"
geo_response = requests.post(
f"{API_BASE}/geocode/",
headers=HEADERS,
json={"address": address},
)
geo = geo_response.json()
lat, lon = float(geo["latitude"]), float(geo["longitude"])
print(f"Geocoded {address} -> ({lat}, {lon})")
# Step 2: Submit climate risk assessment for SSP5-8.5 at 2050
submit_response = requests.post(
f"{API_BASE}/climate-risk/analyze/",
headers=HEADERS,
json={
"lat": lat,
"lon": lon,
"scenarios": ["ssp585"],
"horizons": ["baseline", "2050"],
},
)
job_id = submit_response.json()["job_id"]
print(f"Submitted climate risk job: {job_id}")
# Step 3: Poll until COMPLETED
while True:
status_response = requests.get(f"{API_BASE}/climate-risk/jobs/{job_id}/", headers=HEADERS)
state = status_response.json()
print(f"Status: {state['status']}")
if state["status"] in ("COMPLETED", "PARTIAL", "FAILED"):
break
time.sleep(10)
# Step 4: Fetch results and print top 3 hazards under SSP5-8.5 at 2050
results = requests.get(f"{API_BASE}/climate-risk/results/{job_id}/", headers=HEADERS).json()
top_3 = results["top_risks"][:3]
print(f"\nTop 3 climate hazards for {address} under SSP5-8.5 at 2050:")
for risk in top_3:
hazard = risk["hazard"]
rating = risk["rating"]
value = results["hazards"][hazard]["values"]["ssp585"]["2050"]
unit = results["hazards"][hazard]["metric_unit"]
baseline = results["hazards"][hazard]["values"]["ssp585"]["baseline"]
delta = value - baseline
print(f" {hazard}: {rating} ({value:.1f} {unit}, {delta:+.1f} from baseline)")
Geocoder API
The Continuuiti Geocoder API converts text addresses to geographic coordinates with a structured quality envelope on every response. Pre-validation rejects clearly unusable input before consuming a provider call. Batch endpoints accept up to 5,000 addresses per submission and complete in a few minutes; a 10,000-address workload (two submissions) takes about ten minutes.
Quality Envelope
Every geocoding response includes a structured quality object so you can route results by confidence. The match_type tier is the most actionable field for downstream filtering.
| Match type | Approx accuracy | Description |
|---|---|---|
building |
~10 m | Matched to a specific building footprint |
address |
~20 m | Matched to a specific house number on a street |
street |
~100 m | Matched to a street; coordinates at street centroid |
city |
~1 km | Matched to a city or suburb centroid |
country |
~100 km | Matched to country only |
| Quality status | Meaning |
|---|---|
PASSED |
Address geocoded successfully; coordinates available |
REJECTED |
Address failed pre-validation; no provider call made (saves quota) |
FAILED |
Address passed pre-validation but no match found |
Garbage Detection (pre-validation)
Before any provider call, the API runs five quality checks to reject obviously unusable input. Rejected requests return 200 OK (not 400) because validation succeeded — the address was correctly identified as unusable.
| Check | Triggers when | Example |
|---|---|---|
too_short |
Address length < 5 characters | “NYC” |
no_alphanumeric |
Zero letters or numbers | “—///—“ |
suspicious_characters |
Contains emoji or characters like <>{} | “123 Main 🚀” |
repeated_chars |
Same character appears 5+ times | “aaaaaaa” |
no_spaces |
No whitespace in addresses >10 characters | “123MainStreetNewYork” |
Endpoints
/api/v1/geocode/Geocode a single address synchronously. Returns 201 Created with coordinates and quality, or 200 OK with a REJECTED quality envelope for garbage input.
Request body: {"address": "string (3–500 chars)"}
{
"id": 1,
"original_address": "350 Fifth Avenue, New York, NY",
"formatted_address": "350 5th Ave, New York, NY 10118, USA",
"latitude": "40.7484405",
"longitude": "-73.9856644",
"quality": {"score": 1.0, "status": "PASSED", "issues": [], "match_type": "building"},
"created_at": "2026-04-27T10:19:05Z"
}
Errors: 400 invalid request (missing address, <3 or >500 chars), 404 address not found by provider, 503 transient provider error after retries.
/api/v1/exports/geocoding/reports/{id}/Retrieve a specific previously geocoded address by ID through the export API. Returns 404 if not found.
/api/v1/geocode/batch/Submit a batch of up to 5,000 addresses for asynchronous geocoding. Same flow as the cross-cutting batch pattern.
| Field | Type | Required | Description |
|---|---|---|---|
addresses |
array | Yes | Array of address objects (max 5,000) |
addresses[].address |
string | Yes | Address string (3–500 chars) |
addresses[].label |
string | No | User-provided label, round-trips back in results |
dry_run |
boolean | No | Return mock coordinates without provider calls. See Sandbox |
/api/v1/geocode/batch/{batch_id}/Get batch status and progress. Batch status values: PENDING, PROCESSING, COMPLETED, FAILED. Per-address status uses PENDING, SUCCESS, REJECTED, FAILED; REJECTED (garbage input) addresses are counted separately and do not fail the batch.
/api/v1/geocode/batch/{batch_id}/results/Get paginated geocoding results. Query params: page (default 1), page_size (default 100, max 500), status (filter by job status).
Response Field Reference
| Field | Type | Meaning |
|---|---|---|
id |
integer | Server-assigned record ID |
original_address |
string | Address as submitted (verbatim) |
formatted_address |
string | Provider’s canonical formatting |
latitude |
string (decimal) | Geographic latitude |
longitude |
string (decimal) | Geographic longitude |
quality.score |
number (0–1) | Confidence score; 0 for REJECTED, null for FAILED |
quality.status |
string | PASSED, REJECTED, or FAILED |
quality.issues |
array | Pre-validation issue codes (see Garbage Detection) |
quality.match_type |
string | Match precision tier |
created_at |
string (ISO 8601) | Timestamp of geocoding |
Code Examples
curl -X POST https://api.continuuiti.com/api/v1/geocode/ \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"address": "350 Fifth Avenue, New York, NY"}'
import requests
response = requests.post(
"https://api.continuuiti.com/api/v1/geocode/",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"address": "350 Fifth Avenue, New York, NY"},
)
data = response.json()
print(data["latitude"], data["longitude"], data["quality"]["match_type"])
const response = await fetch("https://api.continuuiti.com/api/v1/geocode/", {
method: "POST",
headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json" },
body: JSON.stringify({ address: "350 Fifth Avenue, New York, NY" }),
});
const data = await response.json();
console.log(data.latitude, data.longitude, data.quality.match_type);
LULC API
The Continuuiti LULC API analyzes land use and land cover at any location and over any year range from 2016 to the current year. Returns annual satellite composites and land cover classification across 9 classes for areas of interest from 100 m to 10 km. Processing is asynchronous; typical 6-year analysis runs in 5 to 10 minutes.
9 Land Cover Classes
| Class key | Code | Label |
|---|---|---|
water |
0 | Water |
trees |
1 | Trees |
grass |
2 | Grass |
flooded_veg |
3 | Flooded Vegetation |
crops |
4 | Crops |
shrub |
5 | Shrub & Scrub |
built |
6 | Built Area |
bare |
7 | Bare Ground |
snow |
8 | Snow & Ice |
Quality Indicators
| Quality | Meaning |
|---|---|
OK |
≥60% cloud-free coverage; reliable results |
LOW_QUALITY |
<60% cloud-free coverage; use with caution |
FAILED |
Unable to process this year |
Endpoints
/api/v1/lulc/analyze/Submit a single LULC analysis job. Returns 202 Accepted with a job_id.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
lat |
number | Yes | — | Latitude (-90 to 90) |
lon |
number | Yes | — | Longitude (-180 to 180) |
size_m |
integer | No | 1000 | Area-of-interest size in meters (100–10000) |
start_year |
integer | No | 2020 | Start year (min 2016) |
end_year |
integer | No | 2025 | End year (max 2025) |
/api/v1/lulc/jobs/{job_id}/Get job status. Status values: PENDING, RUNNING, COMPLETED, FAILED. Progress object reports years_completed / years_total.
/api/v1/lulc/results/{job_id}/Get full results: per-year RGB composites, classified LULC raster URLs, per-year class percentages, and change-summary deltas across the year range.
{
"job_id": "lulc_a1b2c3d4e5f6",
"status": "COMPLETED",
"input": {"lat": 28.6139, "lon": 77.2090, "size_m": 1000, "start_year": 2020, "end_year": 2025},
"year_status": {"2020": "OK", "2021": "OK", "2022": "LOW_QUALITY", "2023": "OK", "2024": "OK", "2025": "OK"},
"artifacts": {
"rgb_frames": {"2020": {"url": "https://storage.example.com/.../2020.webp", "cloud_free_pct": 95.5}},
"lulc_frames": {"2020": {"url": "https://storage.example.com/.../2020.png"}}
},
"stats": {
"by_year": {
"2020": {"water_pct": 2.5, "trees_pct": 15.3, "built_pct": 45.2}
}
},
"change_summary": {
"from_year": 2020, "to_year": 2025,
"net_change_pp": {"built_pct": 4.3, "trees_pct": -2.1},
"top_increases": [{"class": "built", "label": "Built Area", "change_pp": 4.3}],
"top_decreases": [{"class": "trees", "label": "Trees", "change_pp": -2.1}]
}
}
/api/v1/lulc/batch/analyze/Submit a batch of up to 5,000 locations. Body fields mirror the single-location request, with an additional locations array (each with {lat, lon, label?}) and an optional dry_run flag.
/api/v1/lulc/batch/{batch_id}/Get batch status and progress. Same shape as the cross-API batch status pattern.
/api/v1/lulc/batch/{batch_id}/results/Get paginated batch results. Each result contains the per-location LULC payload.
Response Field Reference
| Field path | Type | Meaning |
|---|---|---|
year_status.{year} |
string | OK, LOW_QUALITY, or FAILED per year |
artifacts.rgb_frames.{year}.url |
string (URL) | Annual RGB satellite composite (WebP) |
artifacts.rgb_frames.{year}.cloud_free_pct |
number | Cloud-free coverage percentage |
artifacts.lulc_frames.{year}.url |
string (URL) | Annual classified LULC raster (PNG) |
stats.by_year.{year}.{class}_pct |
number | Per-class percentage of AOI for the year |
change_summary.net_change_pp.{class}_pct |
number | Percentage-point delta from start to end year |
change_summary.top_increases[] |
array | Classes with biggest gains |
change_summary.top_decreases[] |
array | Classes with biggest losses |
Code Examples
curl -X POST https://api.continuuiti.com/api/v1/lulc/analyze/ \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"lat": 28.6139, "lon": 77.2090, "size_m": 1000, "start_year": 2020, "end_year": 2025}'
import requests
response = requests.post(
"https://api.continuuiti.com/api/v1/lulc/analyze/",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"lat": 28.6139, "lon": 77.2090, "size_m": 1000,
"start_year": 2020, "end_year": 2025},
)
job_id = response.json()["job_id"]
const response = await fetch("https://api.continuuiti.com/api/v1/lulc/analyze/", {
method: "POST",
headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json" },
body: JSON.stringify({
lat: 28.6139, lon: 77.2090, size_m: 1000,
start_year: 2020, end_year: 2025,
}),
});
const { job_id } = await response.json();
Biodiversity API
Biodiversity screening (Hansen deforestation, WDPA protected areas, RESOLVE ecoregions, EUDR indicators) is bundled into every LULC+ analysis. There is no separate endpoint to call. Submit via POST /api/v1/lulc/analyze/ (or the batch variant) and the biodiversity results are included in the report, retrievable via GET /api/v1/exports/lulc/reports/{id}/ (the biodiversity field) once the analysis completes.
Damage Estimation API
The Continuuiti Damage Estimation API computes flood damage estimates for buildings using two independent depth-damage models — one tuned to US construction practices and one tuned to international (continental) construction. Returns monetary loss estimates based on flood depth, building occupancy type, structural characteristics, and replacement value. Synchronous: sub-millisecond per building, 5,000 buildings in under 5 seconds.
33 Building Occupancy Types
Buildings are classified by occupancy code. Pass the code in the occupancy field of damage estimation requests.
| Category | Codes |
|---|---|
| Residential | RES1, RES2, RES3A–RES3F, RES4, RES5, RES6 |
| Commercial | COM1–COM10 |
| Industrial | IND1–IND6 |
| Agriculture | AGR1 |
| Religious | REL1 |
| Government | GOV1, GOV2 |
| Education | EDU1, EDU2 |
Flood Zone Types
| Flood zone | Description |
|---|---|
riverine |
Inland river / fluvial flooding |
coastal_a |
Coastal flood zone (moderate wave action) |
coastal_v |
Coastal flood zone (high-velocity wave action) |
Endpoints
/api/v1/damage/estimate/Compute flood damage for a single building. Returns both US and international model results independently in one response. Provide US-model fields (stories_int, basement, flood_zone, replacement_value) and/or international-model fields (country_iso, floor_area_m2); at least one group required. When you provide stories_int, the other US-model fields (basement, flood_zone, and replacement_value) are required with it.
{
"status": "COMPLETED",
"input": {"depth_ft": 5.0, "occupancy": "RES1", "stories_int": "2", "basement": false,
"flood_zone": "riverine", "replacement_value": 350000,
"country_iso": "USA", "floor_area_m2": 150},
"hazus": {
"damage_ratio": 0.20, "contents_ratio": 0.28,
"structural_loss": 70000.00, "contents_loss": 49000.00,
"total_loss": 119000.00, "depth_in_structure_ft": 4.0
},
"jrc": {
"damage_ratio": 0.6265, "total_loss_eur": 77901.57,
"continent": "north_america", "sector": "residential",
"depth_m": 1.2192, "fallback_used": false
}
}
/api/v1/damage/batch/estimate/Process up to 5,000 buildings in a single request. Body: buildings array (each with the single-estimate fields plus optional label) and optional dry_run.
Response Field Reference
| Field path | Type | Meaning |
|---|---|---|
status |
string | COMPLETED (both models), PARTIAL (one failed), FAILED (both failed) |
hazus.damage_ratio |
number (0–1) | Structural damage as fraction of replacement value |
hazus.contents_ratio |
number (0–1) | Contents damage as fraction of contents value |
hazus.structural_loss |
number | Structural loss in input currency |
hazus.contents_loss |
number | Contents loss in input currency |
hazus.total_loss |
number | Sum of structural and contents loss |
hazus.depth_in_structure_ft |
number | Depth above first floor (depth_ft minus first_floor_height_ft) |
hazus.clamped |
boolean | True if depth exceeded curve range and was clamped |
jrc.damage_ratio |
number (0–1) | Damage as fraction of max sector damage |
jrc.total_loss_eur |
number | Total loss in EUR (2010 base year) |
jrc.depth_m |
number | Flood depth in meters (depth_ft × 0.3048) |
jrc.continent |
string | Continent used for the curve |
jrc.sector |
string | Sector used for the curve |
jrc.fallback_used |
boolean | True if continent/country curve unavailable, global fallback applied |
depth_ft input means flood depth above grade. The engine subtracts the first-floor-height before looking up the damage curve. Defaults: basement = 4.0 ft, RES2 = 3.0 ft, all others (slab-on-grade) = 1.0 ft. Override per-building with first_floor_height_ft.Code Examples
curl -X POST https://api.continuuiti.com/api/v1/damage/estimate/ \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"depth_ft": 5.0, "occupancy": "RES1",
"stories_int": "2", "basement": false, "flood_zone": "riverine",
"replacement_value": 350000,
"country_iso": "USA", "floor_area_m2": 150
}'
import requests
response = requests.post(
"https://api.continuuiti.com/api/v1/damage/estimate/",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"depth_ft": 5.0, "occupancy": "RES1",
"stories_int": "2", "basement": False, "flood_zone": "riverine",
"replacement_value": 350000,
"country_iso": "USA", "floor_area_m2": 150,
},
)
result = response.json()
print(f"US model loss: ${result['hazus']['total_loss']:,.2f}")
print(f"International loss: EUR {result['jrc']['total_loss_eur']:,.2f}")
const response = await fetch("https://api.continuuiti.com/api/v1/damage/estimate/", {
method: "POST",
headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json" },
body: JSON.stringify({
depth_ft: 5.0, occupancy: "RES1",
stories_int: "2", basement: false, flood_zone: "riverine",
replacement_value: 350000,
country_iso: "USA", floor_area_m2: 150,
}),
});
const result = await response.json();
Flood Depth API
The Continuuiti Flood Depth API computes scenario-adjusted flood depths for any geographic coordinate, covering both riverine (inland river) and coastal flooding across four return periods, two emissions scenarios, and three time horizons. Returns depth in meters with quality flags indicating coverage and projection confidence. Single-location queries return in roughly 2 seconds.
Return Periods
| Return period | Annual exceedance probability | Common framing |
|---|---|---|
RP10 |
10% per year | “10-year flood” |
RP50 |
2% per year | “50-year flood” |
RP100 |
1% per year | “100-year flood” (FEMA SFHA) |
RP500 |
0.2% per year | “500-year flood” (extreme) |
Scenarios & Horizons
Six scenario-horizon combinations available per request: ssp245_2030, ssp245_2050, ssp245_2080, ssp585_2030, ssp585_2050, ssp585_2080. Plus a baseline reference for both riverine and coastal.
Coastal Parameters
| Parameter | Values | Default | Description |
|---|---|---|---|
coastal_projection |
5, 50, 95 | 50 | Sea level rise percentile. Use 95 for stress-test analysis |
coastal_subsidence |
nosub, wtsub |
wtsub |
Land subsidence on/off. Significant for delta cities (Jakarta, Bangkok) |
7 Quality Flags
Each riverine and coastal scenario result carries a quality flag (priority order). Use these to route results: normal values are usable as-is; other flags signal coverage gaps or modeling artifacts to handle in your pipeline.
| Flag value | Meaning | Action |
|---|---|---|
no_data |
Location outside all flood model coverage | No flood depth available |
jrc_artifact |
Riverine baseline tiling artifact (identical depths across return periods) | Treat as low-confidence; consider coastal-only assessment |
no_flood |
No flooding at this location and return period | Depth = 0; expected for elevated or dry inland sites |
new_flood_zone |
Currently dry but floods under climate change | Future-only flood exposure; baseline = 0, projected > 0 |
no_jrc_data |
Riverine baseline coverage gap | Coastal-only fallback applied where available |
flood_disappears |
Historical flooding ceases under climate change | Baseline > 0, projected = 0; check methodology for context |
normal |
Standard ratio computation | Use depth value directly |
Endpoints
/api/v1/flood-depth/analyze/Submit a single flood depth analysis. Returns 202 Accepted with a job_id (or 200 OK directly if dry_run: true).
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
lat |
number | Yes | — | Latitude (-90 to 90) |
lon |
number | Yes | — | Longitude (-180 to 180) |
label |
string | No | "" |
User-provided label |
coastal_projection |
integer | No | 50 | 5, 50, or 95 (SLR percentile) |
coastal_subsidence |
string | No | wtsub |
nosub or wtsub |
dry_run |
boolean | No | false |
Return mock data (sync, no quota) |
/api/v1/flood-depth/jobs/{job_id}/Get job status. Status values: PENDING, PROCESSING, COMPLETED, FAILED.
/api/v1/flood-depth/results/{job_id}/Get full results: riverine and coastal depth tables across all scenario-horizon combinations.
{
"job_id": "fd_abc123def456",
"status": "COMPLETED",
"input": {"lat": 38.63, "lon": -90.18, "label": "St. Louis property"},
"riverine": {
"baseline": {
"RP10": {"depth_m": 8.147},
"RP100": {"depth_m": 10.148}
},
"scenarios": {
"ssp245_2050": {
"RP10": {"projected_depth_m": 8.482, "ratio": 1.041, "flag": "normal"},
"RP100": {"projected_depth_m": 10.563, "ratio": 1.041, "flag": "normal"}
},
"ssp585_2080": {
"RP100": {"projected_depth_m": 11.402, "ratio": 1.123, "flag": "normal"}
}
}
},
"coastal": {
"baseline": {"RP100": {"depth_m": 0.0}},
"scenarios": {
"ssp585_2080": {"RP100": {"depth_m": null, "flag": "no_flood"}}
}
},
"metadata": {
"method_version": "flood_depth_v1.0",
"processing_time_sec": 2.1,
"coastal_projection": 50,
"coastal_subsidence": "wtsub"
}
}
/api/v1/flood-depth/batch/analyze/Batch up to 5,000 locations. Body: locations array (each with {lat, lon, label?}) plus optional coastal_projection, coastal_subsidence, dry_run applied to all.
/api/v1/flood-depth/batch/{batch_id}/Batch status (standard pattern).
/api/v1/flood-depth/batch/{batch_id}/results/Paginated batch results.
Response Field Reference
| Field path | Type | Meaning |
|---|---|---|
riverine.baseline.{RP}.depth_m |
number | Riverine flood depth at baseline (meters) |
riverine.scenarios.{scenario_horizon}.{RP}.projected_depth_m |
number or null | Projected depth (baseline × ratio) |
riverine.scenarios.{scenario_horizon}.{RP}.ratio |
number or null | Future / historical depth ratio |
riverine.scenarios.{scenario_horizon}.{RP}.flag |
string | One of 7 quality flags |
riverine.scenarios.{scenario_horizon}.{RP}.model_count |
integer | Number of underlying climate models with data (0–5) |
riverine.scenarios.{scenario_horizon}.{RP}.model_spread |
number or null | Max-min depth spread across models (uncertainty proxy) |
coastal.baseline.{RP}.depth_m |
number | Coastal flood depth at baseline |
coastal.scenarios.{scenario_horizon}.{RP}.depth_m |
number or null | Projected coastal depth |
coastal.scenarios.{scenario_horizon}.{RP}.change_pct |
number or null | Percentage change from baseline |
metadata.coastal_projection |
integer | SLR percentile used (5, 50, or 95) |
metadata.coastal_subsidence |
string | Subsidence setting (nosub or wtsub) |
Code Examples
curl -X POST https://api.continuuiti.com/api/v1/flood-depth/analyze/ \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"lat": 38.63, "lon": -90.18, "label": "St. Louis", "coastal_projection": 95}'
import requests
response = requests.post(
"https://api.continuuiti.com/api/v1/flood-depth/analyze/",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"lat": 38.63, "lon": -90.18, "label": "St. Louis", "coastal_projection": 95},
)
job_id = response.json()["job_id"]
const response = await fetch("https://api.continuuiti.com/api/v1/flood-depth/analyze/", {
method: "POST",
headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json" },
body: JSON.stringify({ lat: 38.63, lon: -90.18, label: "St. Louis", coastal_projection: 95 }),
});
const { job_id } = await response.json();
Hazard History API
The Continuuiti Hazard History API queries historical flood and landslide events at any location. Flood queries return clustered episodes with monthly seasonality and precipitation thresholds; landslide queries return individual events with year trend and fatality aggregation. Single-location queries are synchronous (HTTP 201) and return in 1 to 3 seconds; batch endpoints are asynchronous and accept up to 1,000 locations each.
Flood Stats Fields
| Field | Meaning |
|---|---|
episodes |
Number of clustered independent flood episodes (7-day gap rule) |
reports |
Total raw events within radius (pre-clustering) |
recurrence_months |
Average months between episodes (observation span / episodes) |
mean_duration_days |
Mean episode duration in days |
peak_month |
Month of year (1–12) with the most episodes |
largest_event_km2 |
Areal extent of the largest single event |
largest_event_date |
Date of the largest event |
Landslide Stats Fields
| Field | Meaning |
|---|---|
total_events |
Number of landslide events within radius |
avg_per_year |
Span-based average (events / total span years) |
years_with_events |
Distinct years with at least one event |
most_active_year |
Year with the most events |
trend |
increasing, decreasing, or stable (first-half vs second-half comparison) |
total_fatalities |
Sum of fatalities across reported events |
Endpoints
/api/v1/hazard-history/floods/query/Query flood history at a single location. Returns clustered episodes, monthly seasonality, precipitation thresholds matched to episode dates, and individual events.
| Param | Type | Default | Range | Description |
|---|---|---|---|---|
lat |
float | required | -90 to 90 | Latitude |
lon |
float | required | -180 to 180 | Longitude |
radius_km |
float | 10 | fixed | Search radius in kilometers. Fixed for now; not yet an adjustable request parameter |
limit |
integer | 5000 | fixed | Maximum events per location. Fixed at 5,000 for now; not yet an adjustable request parameter (stats computed on the full set) |
dry_run |
boolean | false | — | Return deterministic mock data |
{
"query": {"lat": 29.76, "lon": -95.37, "radius_km": 10.0},
"stats": {
"episodes": 172, "reports": 678, "recurrence_months": 1.5,
"mean_duration_days": 2.8, "peak_month": 5,
"largest_event_km2": 1863.6, "largest_event_date": "2005-09-24"
},
"month_counts": {"1":12,"2":12,"3":13,"4":15,"5":23,"6":16,"7":13,"8":18,"9":21,"10":14,"11":7,"12":8},
"flood_thresholds": {
"matched_episodes": 172,
"peak_daily_mm": {"p25": 11.3, "p50": 25.1, "p75": 44.5},
"max_3day_mm": {"p25": 19.2, "p50": 38.7, "p75": 74.2},
"precip_7day_mm":{"p25": 14.9, "p50": 31.2, "p75": 57.0}
},
"events": [
{"lat": 29.7597, "lon": -95.368, "area_km2": 0.02,
"start_date": "2017-08-27", "end_date": "2017-08-30",
"duration_days": 3, "distance_km": 0.2, "year": 2017}
]
}
/api/v1/hazard-history/floods/batch/Batch flood history query for up to 1,000 locations. Returns 202 Accepted with a batch_id; retrieve results from the export endpoints once processing completes (typically a few minutes). Each location costs one credit, and the stored report bundles both flood and landslide history.
| Field | Type | Default | Description |
|---|---|---|---|
locations |
array | required | 1–1,000 locations |
locations[].lat |
float | required | Latitude |
locations[].lon |
float | required | Longitude |
locations[].label |
string | "" |
Optional label |
locations[].radius_km |
float | — | Per-location override |
radius_km |
float | 10 | Default radius for all locations |
limit |
integer | 50 | Max events per location |
dry_run |
boolean | false | Return mock data |
/api/v1/hazard-history/landslides/query/Query landslide history at a single location. Returns events, year trend, and fatality aggregation. Search radius is fixed at 25 km for now (not yet an adjustable request parameter).
{
"query": {"lat": 19.08, "lon": 72.88, "radius_km": 25.0},
"stats": {
"total_events": 13, "avg_per_year": 1.4, "years_with_events": 6,
"most_active_year": 2010, "most_active_year_count": 6,
"trend": "decreasing", "total_fatalities": 24
},
"year_counts": {"2007": 1, "2008": 0, "2009": 1, "2010": 6},
"events": [
{"lat": 19.069, "lon": 72.8703, "date": "2010-06-25",
"trigger": "downpour", "category": "landslide", "size": "medium",
"fatalities": 0, "distance_km": 1.6, "year": 2010}
]
}
/api/v1/hazard-history/landslides/batch/Batch landslide history query for up to 1,000 locations. Same body shape as the flood batch with default radius_km = 25, and the same asynchronous behavior: a 202 Accepted with a batch_id, then results from the export endpoints. One credit per location, and the stored report bundles both hazards.
Response Field Reference
| Field path | Type | Meaning |
|---|---|---|
stats.episodes (flood) |
integer | Clustered independent episodes within radius |
stats.recurrence_months (flood) |
number | Observation-span / episode-count |
flood_thresholds.{metric}.{p25|p50|p75} |
number | Precipitation percentile thresholds matched to episode dates |
month_counts.{1-12} |
integer | Episode counts by start month (seasonality fingerprint) |
stats.trend (landslide) |
string | increasing, decreasing, or stable |
year_counts.{year} |
integer | Event count per year (includes years with zero events) |
events[].distance_km |
number | Distance from query coordinate |
Code Examples
curl "https://api.continuuiti.com/api/v1/hazard-history/floods/query/?lat=29.76&lon=-95.37&radius_km=10" \
-H "X-API-Key: YOUR_API_KEY"
import requests
response = requests.get(
"https://api.continuuiti.com/api/v1/hazard-history/floods/query/",
headers={"X-API-Key": "YOUR_API_KEY"},
params={"lat": 29.76, "lon": -95.37, "radius_km": 10},
)
data = response.json()
print(f"Episodes: {data['stats']['episodes']}, Peak month: {data['stats']['peak_month']}")
const url = new URL("https://api.continuuiti.com/api/v1/hazard-history/floods/query/");
url.searchParams.set("lat", "29.76");
url.searchParams.set("lon", "-95.37");
url.searchParams.set("radius_km", "10");
const response = await fetch(url, { headers: { "X-API-Key": "YOUR_API_KEY" } });
const data = await response.json();
Carbon Project Screening API
Carbon Project Screening checks the land parcels in a carbon project for deforestation history and climate risk, so you can gauge project integrity before relying on it. Submit each parcel as a polygon and the API screens all of them over the lookback period you choose. Processing is asynchronous; results are retrieved through the export API.
Endpoints
/api/v1/carbon-project-screening/screen/Submit a carbon project for screening. Returns 202 Accepted with the screening record and the IDs of the two analyses it starts (deforestation and climate). Costs one credit per parcel.
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Project name |
project_start_year |
integer | Yes | Year the project began |
lookback_years |
integer | No | How many years of history to check |
is_dry_run |
boolean | No | Sandbox mode; no credits consumed |
parcels |
array | Yes | Each parcel is {polygon, label}, where polygon is a GeoJSON Polygon |
Response (202 Accepted)
{
"site_id": "site_a1b2c3d4",
"status": "PENDING",
"carbon_batch_id": "carbon_e5f6g7h8",
"climate_batch_id": "climate_i9j0k1l2",
"climate_submitted": true,
"redirect_url": "/carbon-screening/site_a1b2c3d4/"
}
Field notes: site_id is the screening record; carbon_batch_id and climate_batch_id are the two analyses it starts; climate_submitted tells you whether the climate run began; redirect_url links to the screening in your dashboard.
Retrieve results through the export API: GET /api/v1/exports/carbon-screening/sites/, .../sites/{id}/, and .../parcels/{id}/.
402 with {"errors": {"credits": "..."}, "needed": N, "balance": N, "request_access_url": "/carbon-screening/credits/request/"}, a different shape from the other modules’ 402 body.Code Examples
curl -X POST https://api.continuuiti.com/api/v1/carbon-project-screening/screen/ \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Example REDD+ Project",
"project_start_year": 2026,
"lookback_years": 10,
"is_dry_run": false,
"parcels": [
{"polygon": {"type": "Polygon", "coordinates": [[[112.9,-3.1],[113.0,-3.1],[113.0,-3.0],[112.9,-3.0],[112.9,-3.1]]]}, "label": "Block A"}
]
}'
Frequently Asked Questions
What is the Continuuiti API base URL?
The Continuuiti API base URL is https://api.continuuiti.com/api/v1/. All endpoints across the seven APIs (Geocoder, LULC, Climate Risk, Damage Estimation, Flood Depth, Hazard History, Carbon Screening) are reached by appending the endpoint path to this base URL. For example, climate risk assessment for a single location uses POST https://api.continuuiti.com/api/v1/climate-risk/analyze/.
How do I get a Continuuiti API key?
Sign up for an account at tools.continuuiti.com/accounts/api-docs/ and generate an API key from your dashboard. Every request requires the key in the X-API-Key header. Keys are scoped per account; rate limits apply per key. There is no separate developer plan: every signup gets API access immediately.
Is there an API sandbox or free testing mode?
Yes. Every endpoint that supports dry_run accepts a dry_run query parameter (GET) or body field (POST). Sandbox responses return deterministic mock data — same input always returns the same output — so you can integrate, write tests, and run CI without consuming API quota. Sandbox responses include a [DRY RUN] prefix in metadata so you cannot accidentally treat them as real. See the Sandbox section for details.
What is the difference between sync and async Continuuiti APIs?
Synchronous APIs (Geocoder, Damage Estimation, Hazard History) return results in a single request-response cycle in seconds or sub-seconds. Asynchronous APIs (LULC, Climate Risk, Flood Depth, Carbon Project Screening) require a job-poll-fetch pattern: POST submits the job and returns a 202 with a job_id, GET polls status, GET fetches results when status is COMPLETED. All async APIs share the same flow, status values, and batch pattern. See the Batch & Async section.
What are the Continuuiti API rate limits?
Rate limits apply per API key. Default limits are 60 requests per minute and 500 requests per hour per account; exceeding them returns 429 with a Retry-After header. Batch endpoints accept up to 5,000 locations per submission. For sustained high-volume workloads or custom limits, book a demo to discuss your requirements.
How long do batch jobs take?
Batch processing time depends on the API and batch size. Geocoding completes 10,000 addresses in approximately ten minutes. Climate Risk batches run 2 to 4 minutes per location with controlled concurrency (max 20 parallel jobs). LULC batches run 4 to 6 minutes per location. Flood Depth runs about 2 seconds per location. Damage Estimation is sub-millisecond per building.
What happens if a Continuuiti batch partially fails?
When some locations in a batch fail and others succeed, the batch reaches PARTIAL status (not FAILED). The progress object reports counts for completed, failed, and retrying jobs separately. Failed locations can be retried at no additional credit cost. Use the results endpoint with ?status=FAILED to filter just the failed items for retry.
Are Continuuiti APIs aligned with TCFD or IFRS S2 reporting?
Continuuiti APIs provide screening-level data aligned with TCFD physical risk disclosure categories and IFRS S2 implementation guidance. The Climate Risk API outputs map directly to TCFD’s physical risk taxonomy (acute and chronic hazards, multiple scenarios, multiple time horizons). For full regulatory disclosure preparation, see /methodology/climate-risk/ and consult your compliance team.
