Home API Documentation

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.

Developer Access
Get a Continuuiti API Key
Every Continuuiti Tools account includes a personal API key. Create an account, copy your key from the Platform API page, and pass it in the X-API-Key header.

Get API Key

Getting Started

Get your API key

Every Continuuiti Tools account includes a personal API key. To get yours:

  1. Create an account at tools.continuuiti.com with your business email.
  2. Open Platform API in the sidebar. Your key (sk_live_...) is generated automatically, so copy it from there.
  3. Pass it on every request in the X-API-Key header.

You can regenerate the key at any time from the same page. The old key stops working immediately.

Credits. Triggering analyses consumes the same per-module credits as the web dashboard (1 credit per location, building, or parcel). If your balance is insufficient the API returns 402 with your current balance, and an access request is logged for our team automatically. Reading your results is always free.
Your jobs, your dashboard. Every analysis you submit through the API is recorded against your account and appears in your web dashboard alongside jobs submitted through the UI. Jobs complete automatically on our side, so you can poll the status endpoints but you never have to.

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
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
curl https://api.continuuiti.com/api/v1/exports/geocoding/reports/ \
  -H "X-API-Key: YOUR_API_KEY"
Python
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()
Node
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"}
Best practice: Store your API key in an environment variable, never in source code. Rotate keys periodically from the dashboard. Treat keys as sensitive credentials — anyone with the key can make billable calls against your account.

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

  1. POST to /{api}/batch/analyze/ with your locations array. Returns 202 Accepted with a batch_id immediately — processing has not started yet.
  2. GET /{api}/batch/{batch_id}/ repeatedly to poll progress. Status moves through PROCESSING and ends at COMPLETED, PARTIAL, or FAILED.
  3. GET /{api}/batch/{batch_id}/results/?page=1&page_size=100 to 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.

Python (full polling loop)
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.

GET/api/v1/exports/{module}/reports/

List your reports for a module (paginated and filterable).

GET/api/v1/exports/{module}/reports/{id}/

Fetch one full report with its results.

GET/api/v1/exports/{module}/batches/

List your batches for a module.

GET/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 (geocoder dry-run)
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
  }'
Python (climate risk dry-run)
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", {}))
Node (hazard history dry-run)
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,
    ...
  }
}
When to use sandbox: writing tests against the API contract; building frontend code without consuming quota; demoing the platform to stakeholders without quota concerns; CI pipelines that exercise the integration path without budget impact. Sandbox does not validate your inputs against the real data — production calls are still required for real assessments.

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"
}
503 handling: Async APIs already retry transient upstream failures automatically (see Batch & Async — retry behavior). For sync APIs, retry once after 2 seconds; if still 503, retry once more after 8 seconds. If the third attempt fails, treat it as a real outage.

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.

Practical guidance: Batch endpoints (max 5,000 locations per submission) drain at controlled concurrency on the server side — you do not need to throttle your own batch submissions. For sync endpoints (Geocoder single, Damage Estimation, Hazard History), keep client-side concurrency at 5–10 parallel requests per key as a safe baseline.

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

POST/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
GET/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
GET/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
POST/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
GET/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
}
GET/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
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"]
  }'
Python
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"]
Node
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.”

Python (geocode → climate risk → top hazards)
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

POST/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.

GET/api/v1/exports/geocoding/reports/{id}/

Retrieve a specific previously geocoded address by ID through the export API. Returns 404 if not found.

POST/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
GET/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.

GET/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
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"}'
Python
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"])
Node
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

POST/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)
GET/api/v1/lulc/jobs/{job_id}/

Get job status. Status values: PENDING, RUNNING, COMPLETED, FAILED. Progress object reports years_completed / years_total.

GET/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}]
  }
}
POST/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.

GET/api/v1/lulc/batch/{batch_id}/

Get batch status and progress. Same shape as the cross-API batch status pattern.

GET/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
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}'
Python
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"]
Node
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

POST/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
  }
}
POST/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
First Floor Height (FFH): The 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
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
  }'
Python
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}")
Node
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

POST/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)
GET/api/v1/flood-depth/jobs/{job_id}/

Get job status. Status values: PENDING, PROCESSING, COMPLETED, FAILED.

GET/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"
  }
}
POST/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.

GET/api/v1/flood-depth/batch/{batch_id}/

Batch status (standard pattern).

GET/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
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}'
Python
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"]
Node
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.

One call covers both hazards. Each query costs one credit, and the stored report includes both flood and landslide history regardless of which endpoint you call, so a single call per location is enough.

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

GET/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}
  ]
}
POST/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
GET/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}
  ]
}
POST/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
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"
Python
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']}")
Node
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

POST/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}/.

Credit error shape differs here: if you run out of credits, Carbon Project Screening returns 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
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.

Continuuiti Platform
Try the Platform Yourself
Run climate risk, flood depth, geocoding, and damage assessments across up to 5,000 locations per batch. Start with a demo.

Book a Demo