Skip to main content

ucip

Python client for the UCIP API: Mumbai ward-level heat vulnerability data and cited nature-based cooling recommendations, as pandas and geopandas frames.

No authentication, no key, no account. The API is public and read-only.

pip install ucip

Three lines

import ucip

wards = ucip.Ucip().wards_geo()
wards.plot(column="hvi", legend=True)

A GeoDataFrame of all 24 wards in EPSG:4326, indexed by ward code and sorted by rank, with the per-factor contributions flattened into contrib_* columns.

Frames

from ucip import Ucip

api = Ucip()

api.wards_frame()             # 24 wards, indexed by ward code, ranked
api.cells_frame()             # the 541-cell analysis grid, indexed by cell id
api.recommendations_frame()   # every intervention, with its citation
api.wards_geo()               # wards with geometry
api.cells_geo()               # the grid with geometry

wards_geo() and cells_geo() are one cached request for the whole dataset, not a paging loop. This project runs on a free-tier database; paging the per-record endpoints to build a frame is slower for you and is the surest way to meet the rate limiter.

Raw responses

When you want exactly what the API sent, as typed dicts:

api.meta()                                # coverage, counts, data vintage
api.wards(limit=5)
api.ward("F/N")                           # the slash is encoded for you
api.lookup(19.076, 72.877)                # which ward contains this point
api.recommendations(ward="C")
api.cells(bbox="72.80,19.00,72.95,19.15")
api.export("wards")                       # GeoJSON FeatureCollection
api.export_csv("cells")                   # CSV text, with lon/lat columns

Reading the data honestly

Three fields exist because the numbers deserve caveats, and a client that hides them is doing you a disservice.

single_factor_dominated, on each ward, marks a score driven by one indicator rather than a real combination of seven. Check it before treating a rank as a composite:

wards = api.wards_frame()
wards[wards["single_factor_dominated"]]     # empty on the current dataset

It is empty today. Do not assume that for a future refresh or another city.

source, on every raw response, is "database" or "snapshot". snapshot means the database was unreachable and committed static files were served instead, at most one refresh behind. Field names and the key set are identical either way, so you never need to branch on it; it is there for when data vintage matters.

composite_window, from meta(), is the Landsat window the measurements come from. It is older than generated_at, and it is the one that says how old the underlying observations actually are.

meta = api.meta()
meta["generated_at"]        # when the pipeline last ran
meta["composite_window"]    # what the imagery actually covers

Every recommendation carries the paper it rests on, and recommendations_frame() keeps that column. A planting recommendation separated from its citation is the artefact this project exists not to produce.

Methodology and limitations: https://uciplatform.vercel.app/methodology.

Errors

from ucip import UcipHTTPError, UcipNetworkError, UcipError

try:
    api.ward("ZZ")
except UcipHTTPError as err:
    err.status           # 404
    err.detail           # the API's own message
    err.hint             # its suggested next step, when there is one
    err.is_rate_limited  # True only on 429
    err.retry_after      # seconds, from the retry-after header
except UcipNetworkError:
    ...                  # never reached the API

Everything raised inherits from UcipError.

The client does not retry. The failure you are most likely to hit is a 429 from defeating the edge cache in a loop, and retrying makes that worse. retry_after is there for backing off deliberately.

Options

Ucip(
    "http://localhost:3000/api/v1",   # defaults to the public deployment
    timeout=30.0,
    user_agent="my-analysis/1.0",     # be identifiable
    session=my_requests_session,
)

Python 3.10 or newer. Depends on requests, pandas and geopandas, all imported lazily so import ucip does not pay for GDAL bindings.

Citing this

The dataset has a DOI. If it appears in something you publish, cite it:

Dhawan, A. UCIP: Urban Climate Intelligence Platform. https://doi.org/10.5281/zenodo.22923919

That concept DOI always resolves to the newest version; CITATION.cff in the repository has the per-version DOIs.

Where the types come from

src/ucip/models.py is generated from clients/openapi.json, the same spec artifact the TypeScript client is generated from, which is emitted from the module the API serves its spec from. CI regenerates and diffs, so an API change that is not reflected in these models fails the build.

Do not edit models.py. Change the spec and run python scripts/generate.py.

Development

pip install -e ".[dev]"
python scripts/generate.py            # rebuild models.py from the spec
python -m pytest tests/               # stubbed transport, no network
UCIP_LIVE=1 python -m pytest tests/   # also run against the deployment

Licence

Apache-2.0. The data has its own per-source terms: see https://uciplatform.vercel.app/legal.

Release files for ucip 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ucip 0.1.0
File Size Uploaded
ucip-0.1.0.tar.gz 22.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ucip 0.1.0
File Interpreter ABI Platform
ucip-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 38.0 kB

Release files / ucip-0.1.0.tar.gz

Download URL ucip-0.1.0.tar.gz
Size 22.0 kB
Tags Source
SHA-256 checksum
How to use checksums
47703a1a5707ec9592774acd1b8b32fa69b4aced0dd02b54e629c8c4b7babbf1
BLAKE2b-256 checksum
How to use checksums
e2b2b414181dab4af69f8cc25ff16b26038499be3a8eebdbdee547cf611ed33d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.2

Release files / ucip-0.1.0-py3-none-any.whl

Download URL ucip-0.1.0-py3-none-any.whl
Size 16.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fae652c3d4093b57a0ebe8306342e002f32f2b7467a5b270bd5d4f38e66b22cc
BLAKE2b-256 checksum
How to use checksums
6bf79be0fa79745dea27df6dad925c54f0559522294141beb03879374c42fc5f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.2

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page