Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/NASAARSET/PM2.5_AQ_Online_2026/llms.txt

Use this file to discover all available pages before exploring further.

The SINCA module provides a set of helper functions and one main entry point for retrieving hourly PM2.5 data from SINCA — Chile’s Sistema de Información Nacional de Calidad del Aire, operated by the Ministerio del Medio Ambiente. Unlike the OpenAQ integration, SINCA does not offer a public REST API, so data is obtained by scraping the SINCA web portal HTML and CGI download endpoints. The module is structured as a pipeline of increasingly granular helpers — from listing all stations in a region, through extracting sensor metadata and download links, down to parsing the raw semicolon-delimited CSV files — topped by the all-in-one F_get_SINCA_from_web function, which mirrors the interface of F_get_OpenAQ_from_API and returns data in the same (lat, lon, time) MultiIndex format. Use F_get_SINCA_from_web as a drop-in replacement for (or complement to) F_get_OpenAQ_from_API when working with Chilean monitoring sites, especially in regions not yet fully covered by OpenAQ.

get_sinca_region_stations

List all monitoring stations belonging to a SINCA administrative region.
def get_sinca_region_stations(region_id: str) -> pd.DataFrame

Parameters

region_id
str
required
Roman-numeral region code used by the SINCA portal. Examples:
CodeRegion
'I'Tarapacá
'II'Antofagasta
'VIII'Biobío
'RM'Región Metropolitana de Santiago
'XIV'Los Ríos
A full list of codes is available on the SINCA portal.

Return Value

result
pandas.DataFrame
A DataFrame with one row per station and the following columns:
ColumnDescription
region_idThe region code passed as input
station_idNumeric SINCA station identifier
station_nameHuman-readable station name
station_urlFull URL of the station page on sinca.mma.gob.cl

Example

df_stations = get_sinca_region_stations('VIII')
print(df_stations[['station_name', 'station_url']].head())
#            station_name                                        station_url
# 0   Concepción Centro   https://sinca.mma.gob.cl/index.php/...
# 1      Los Ángeles Sur   https://sinca.mma.gob.cl/index.php/...

get_sinca_station_metadata

Extract the geographic coordinates of a single SINCA station.
def get_sinca_station_metadata(station_url: str) -> dict

Parameters

station_url
str
required
Full URL of the SINCA station page (as returned in the station_url column of get_sinca_region_stations). The function parses the embedded Google Maps JavaScript on the page to locate the latitude and longitude values.

Return Value

result
dict
A dictionary with keys:
KeyTypeDescription
'lat'floatStation latitude in decimal degrees (negative = south)
'lon'floatStation longitude in decimal degrees (negative = west)

Example

meta = get_sinca_station_metadata(
    'https://sinca.mma.gob.cl/index.php/estacion/index/id/78'
)
print(meta)
# {'lat': -36.827, 'lon': -73.049}

Find all data/graph download links on a SINCA station page.
def get_sinca_station_graph_links(station_url: str) -> pd.DataFrame

Parameters

station_url
str
required
Full URL of the SINCA station page. The function parses the anchor elements on the page to extract links pointing to pollutant-specific graph/data subpages.

Return Value

result
pandas.DataFrame
A DataFrame with one row per pollutant/sensor combination:
ColumnDescription
link_textDisplay text of the link (e.g., 'PM2.5', 'PM10', 'SO2')
graph_urlFull URL of the data/graph subpage for that pollutant

get_sinca_macro_options

Extract the macro CGI parameter needed to construct SINCA download requests.
def get_sinca_macro_options(graph_url: str) -> pd.DataFrame

Parameters

graph_url
str
required
URL of a pollutant data/graph subpage (from the graph_url column of get_sinca_station_graph_links). The function parses the left navigation frame of the page to retrieve the macro values used by the SINCA download CGI endpoint.

Return Value

result
pandas.DataFrame
A DataFrame with one row per available time-aggregation option:
ColumnDescription
option_textHuman-readable option label (e.g., 'Horario' for hourly)
macro_valueNumeric macro parameter value to pass to the download CGI

_parse_sinca_hourly

Parse a raw SINCA hourly CSV text blob into a tidy DataFrame.
def _parse_sinca_hourly(raw_text: str) -> pd.DataFrame
This is a private helper function (indicated by the leading underscore). It is called internally by _download_one_station_pm25 and is not intended for direct use. It is documented here for transparency and debugging purposes.

Parameters

raw_text
str
required
The raw text content of a SINCA hourly CSV download. The expected column layout is fixed by column position, not header name:
FECHA;HORA;<validated>;<preliminary>;<not_validated>;
The parser prefers validated data over preliminary data over non-validated data, in that order of priority.

Return Value

result
pandas.DataFrame
A DataFrame with columns:
ColumnDescription
datetime_localLocal Chile time as a datetime object
PM2.5PM2.5 concentration in µg/m³ (best available validation tier)
statusData quality label: 'validated', 'preliminary', 'not_validated', or 'no_data'

_download_one_station_pm25

Download hourly PM2.5 data for a single SINCA station over a date range.
def _download_one_station_pm25(
    station_row: pd.Series,
    date_from: str,
    date_to: str
) -> pd.DataFrame | None
This is a private helper function called internally by F_get_SINCA_from_web. It is documented here for reference when debugging individual-station download failures.

Parameters

station_row
pandas.Series
required
A single row from the DataFrame returned by get_sinca_region_stations, providing the station_url and other metadata needed to construct the download request.
date_from
str
required
Start date for the download, as a string in 'YYYY-MM-DD' format.
date_to
str
required
End date for the download (inclusive), as a string in 'YYYY-MM-DD' format.

Return Value

result
pandas.DataFrame | None
A tidy DataFrame with columns datetime_local, PM2.5, and status for the requested station and date range, or None if PM2.5 data is unavailable for that station.

F_get_SINCA_from_web

The main entry point: download hourly PM2.5 for all SINCA stations within a geographic bounding box and time window.
def F_get_SINCA_from_web(
    n_lon_min: float,
    n_lat_min: float,
    n_lon_max: float,
    n_lat_max: float,
    t_start: numpy.datetime64,
    t_end: numpy.datetime64,
) -> pd.DataFrame | None

Parameters

n_lon_min
float
required
Minimum (western) longitude of the bounding box, in decimal degrees.
n_lat_min
float
required
Minimum (southern) latitude of the bounding box, in decimal degrees.
n_lon_max
float
required
Maximum (eastern) longitude of the bounding box, in decimal degrees.
n_lat_max
float
required
Maximum (northern) latitude of the bounding box, in decimal degrees.
t_start
numpy.datetime64
required
Start of the time window of interest. Must be a numpy.datetime64 object.
t_end
numpy.datetime64
required
End of the time window of interest. Must be a numpy.datetime64 object.

Return Value

result
pandas.DataFrame | None
A pandas.DataFrame with a three-level MultiIndex of (lat, lon, time) and a 'PM2.5' column containing hourly concentrations in µg/m³. This is the same format as the DataFrame returned by F_get_OpenAQ_from_API, making the two functions interchangeable as inputs to F_compute_metrics.Returns None if no SINCA stations with PM2.5 data are found in the specified region and time window.

Usage Example

import numpy as np

# Biobío bounding box
n_lon_min, n_lon_max = -74.0, -71.0
n_lat_min, n_lat_max = -39.0, -36.0

t_start = np.datetime64('2023-01-01')
t_end   = np.datetime64('2023-12-31')

f_data_monitors_SINCA = F_get_SINCA_from_web(
    n_lon_min = n_lon_min,
    n_lat_min = n_lat_min,
    n_lon_max = n_lon_max,
    n_lat_max = n_lat_max,
    t_start   = t_start,
    t_end     = t_end,
)

if f_data_monitors_SINCA is not None:
    print(f_data_monitors_SINCA.head())
    #                                    PM2.5
    # lat      lon      time
    # -36.827 -73.049  2023-01-01 00:00   14.3
    #                  2023-01-01 01:00   12.8
    #                  ...

Combining SINCA and OpenAQ data

Because both functions return identical DataFrame formats, their outputs can be concatenated for a more complete ground-observation dataset:
import pandas as pd

f_data_monitors_combined = pd.concat([f_data_monitors_OpenAQ, f_data_monitors_SINCA])
f_data_monitors_combined = f_data_monitors_combined[
    ~f_data_monitors_combined.index.duplicated(keep='first')
]

d_metrics = F_compute_metrics(
    f_data     = f_data_combined_gridded,
    s_value    = 'Average',
    s_estimate = 'SatPM',
)

SINCA data is retrieved by web scraping the public portal at sinca.mma.gob.cl. The portal layout may change without notice, which can break the parsing logic. If you encounter errors or empty DataFrames, check the SINCA portal directly and report issues to the training repository.
SINCA monitors report data in local Chilean time (CLT/CLST, UTC-4/UTC-3 depending on daylight saving). The functions convert timestamps to UTC before building the final MultiIndex to ensure consistency with OpenAQ data, which is always reported in UTC.
Scraping a large bounding box (e.g., all of Chile) or a multi-year time window will issue many sequential HTTP requests and may take several minutes. Consider narrowing the bounding box or breaking the time window into shorter segments if you experience timeouts.

  • F_get_OpenAQ_from_API — the equivalent function for OpenAQ ground observations; same output format.
  • F_compute_metrics — pass the returned DataFrame as ground-truth input for satellite validation.
  • F_plot_map — plot SINCA monitor locations and concentrations as point data via a_data_plot_point.
  • SINCA Data Source — background on the SINCA network, data quality tiers, and regional coverage.

Build docs developers (and LLMs) love