aqi-in-api 0.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ @GuyKh
@@ -0,0 +1,15 @@
1
+ # These are supported funding model platforms
2
+
3
+ github: guykh # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4
+ patreon: # Replace with a single Patreon username
5
+ open_collective: # Replace with a single Open Collective username
6
+ ko_fi: # Replace with a single Ko-fi username
7
+ tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8
+ community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9
+ liberapay: # Replace with a single Liberapay username
10
+ issuehunt: # Replace with a single IssueHunt username
11
+ lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12
+ polar: # Replace with a single Polar username
13
+ buy_me_a_coffee: guykh # Replace with a single Buy Me a Coffee username
14
+ thanks_dev: # Replace with a single thanks.dev username
15
+ custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
@@ -0,0 +1,11 @@
1
+ ---
2
+ name: Question
3
+ about: Questions about this project
4
+ labels: question
5
+ ---
6
+
7
+ ### Checklist
8
+ - [ ] There are no similar issues or pull requests about this question.
9
+
10
+ ### Describe your question
11
+ <!-- A clear and concise description of what the question is. -->
@@ -0,0 +1,19 @@
1
+ ---
2
+ name: Bug report
3
+ about: Report a bug to help improve this project
4
+ labels: bug
5
+ ---
6
+ ### Describe the bug
7
+ <!-- A clear and concise description of what the bug is. -->
8
+
9
+ ### To reproduce
10
+ <!-- Provide a *minimal* example with steps to reproduce the bug locally.-->
11
+
12
+ ### Expected behavior
13
+ <!-- A clear and concise description of what you expected to happen. -->
14
+
15
+ ### Actual behavior
16
+ <!-- A clear and concise description of what actually happens. -->
17
+
18
+
19
+ ### Additional context
@@ -0,0 +1,33 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for this project.
4
+ labels: feature, enhancement
5
+ ---
6
+
7
+ ### Checklist
8
+
9
+ <!-- Please make sure you check all these items before submitting your feature request. -->
10
+
11
+ - [ ] There are no similar issues or pull requests for this yet.
12
+
13
+ ### Is your feature related to a problem? Please describe.
14
+
15
+ <!-- A clear and concise description of what you are trying to achieve.
16
+
17
+ Eg "I want to be able to [...] but I can't because [...]". -->
18
+
19
+ ## Describe the solution you would like.
20
+
21
+ <!-- A clear and concise description of what you would want to happen.
22
+
23
+ For API changes, try to provide a code snippet of what you would like the API to look like.
24
+ -->
25
+
26
+ ## Describe alternatives you considered
27
+
28
+ <!-- Please describe any alternative solutions or features you've considered to solve
29
+ your problem and why they wouldn't solve it. -->
30
+
31
+ ## Additional context
32
+
33
+ <!-- Provide any additional context, screenshots, tracebacks, etc. about the feature here. -->
@@ -0,0 +1,5 @@
1
+ ## What changes do you are proposing?
2
+
3
+ ## How did you test these changes?
4
+
5
+ **Closing issues**
@@ -0,0 +1,11 @@
1
+ # To get started with Dependabot version updates, you'll need to specify which
2
+ # package ecosystems to update and where the package manifests are located.
3
+ # Please see the documentation for all configuration options:
4
+ # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5
+
6
+ version: 2
7
+ updates:
8
+ - package-ecosystem: "pip"
9
+ directory: "/"
10
+ schedule:
11
+ interval: "weekly"
@@ -0,0 +1,97 @@
1
+ name: release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "[0-9]+.[0-9]+.[0-9]+"
7
+ - "[0-9]+.[0-9]+.[0-9]+a[0-9]+"
8
+ - "[0-9]+.[0-9]+.[0-9]+b[0-9]+"
9
+ - "[0-9]+.[0-9]+.[0-9]+rc[0-9]+"
10
+
11
+ env:
12
+ PACKAGE_NAME: "py-aqi-in-api"
13
+ OWNER: "GuyKh"
14
+
15
+ jobs:
16
+ details:
17
+ runs-on: ubuntu-latest
18
+ outputs:
19
+ new_version: ${{ steps.release.outputs.new_version }}
20
+ suffix: ${{ steps.release.outputs.suffix }}
21
+ tag_name: ${{ steps.release.outputs.tag_name }}
22
+ steps:
23
+ - uses: actions/checkout@v7
24
+
25
+ - name: Extract tag and Details
26
+ id: release
27
+ run: |
28
+ if [ "${{ github.ref_type }}" = "tag" ]; then
29
+ TAG_NAME=${GITHUB_REF#refs/tags/}
30
+ NEW_VERSION=$(echo $TAG_NAME | awk -F'-' '{print $1}')
31
+ SUFFIX=$(echo $TAG_NAME | grep -oP '[a-z]+[0-9]+' || echo "")
32
+ echo "new_version=$NEW_VERSION" >> "$GITHUB_OUTPUT"
33
+ echo "suffix=$SUFFIX" >> "$GITHUB_OUTPUT"
34
+ echo "tag_name=$TAG_NAME" >> "$GITHUB_OUTPUT"
35
+ echo "Version is $NEW_VERSION"
36
+ echo "Suffix is $SUFFIX"
37
+ echo "Tag name is $TAG_NAME"
38
+ else
39
+ echo "No tag found"
40
+ exit 1
41
+ fi
42
+
43
+ check_pypi:
44
+ needs: details
45
+ runs-on: ubuntu-latest
46
+ steps:
47
+ - name: Fetch information from PyPI
48
+ run: |
49
+ response=$(curl -s https://pypi.org/pypi/${{ env.PACKAGE_NAME }}/json || echo "{}")
50
+ latest_previous_version=$(echo $response | jq --raw-output "select(.releases != null) | .releases | keys_unsorted | last")
51
+ if [ -z "$latest_previous_version" ]; then
52
+ echo "Package not found on PyPI."
53
+ latest_previous_version="0.0.0"
54
+ fi
55
+ echo "Latest version on PyPI: $latest_previous_version"
56
+ echo "latest_previous_version=$latest_previous_version" >> $GITHUB_ENV
57
+
58
+ - name: Compare versions and exit if not newer
59
+ run: |
60
+ NEW_VERSION=${{ needs.details.outputs.new_version }}
61
+ LATEST_VERSION=$latest_previous_version
62
+ if [ "$(printf '%s\n' "$LATEST_VERSION" "$NEW_VERSION" | sort -rV | head -n 1)" != "$NEW_VERSION" ] || [ "$NEW_VERSION" == "$LATEST_VERSION" ]; then
63
+ echo "The new version $NEW_VERSION is not greater than the latest version $LATEST_VERSION on PyPI."
64
+ exit 1
65
+ else
66
+ echo "The new version $NEW_VERSION is greater than the latest version $LATEST_VERSION on PyPI."
67
+ fi
68
+
69
+ setup_and_build:
70
+ needs: [details, check_pypi]
71
+ runs-on: ubuntu-latest
72
+ steps:
73
+ - uses: actions/checkout@v7
74
+
75
+ - name: Set up Python
76
+ uses: actions/setup-python@v6
77
+ with:
78
+ python-version: "3.11"
79
+
80
+ - name: Install uv
81
+ uses: astral-sh/setup-uv@v8.2.0
82
+ with:
83
+ enable-cache: true
84
+
85
+ - name: Set project version
86
+ run: |
87
+ uv version ${{ needs.details.outputs.new_version }}
88
+
89
+ - name: Install dependencies
90
+ run: uv sync
91
+
92
+ - name: Build source publish
93
+ env:
94
+ PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
95
+ run: |
96
+ uv build
97
+ uv publish --token $PYPI_TOKEN
@@ -0,0 +1,66 @@
1
+ name: pull_request
2
+ on: [pull_request]
3
+ jobs:
4
+ lint:
5
+ name: "Lint"
6
+ runs-on: ubuntu-latest
7
+ steps:
8
+ - run: echo "Running lint for ${{ github.ref }} branch"
9
+
10
+ - name: Check out repository code
11
+ uses: actions/checkout@v7
12
+ with:
13
+ fetch-depth: 1
14
+
15
+ - name: Install uv
16
+ uses: astral-sh/setup-uv@v8.2.0
17
+ with:
18
+ enable-cache: true
19
+
20
+ - name: Install dependencies
21
+ run: uv sync --dev
22
+
23
+ - name: Running Lint
24
+ run: uv run --with ruff ruff check .
25
+
26
+ run_tests:
27
+ name: "Run Tests"
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - run: echo "Running tests for ${{ github.ref }} branch"
31
+
32
+ - name: Check out repository code
33
+ uses: actions/checkout@v7
34
+ with:
35
+ fetch-depth: 1
36
+
37
+ - name: Install uv
38
+ uses: astral-sh/setup-uv@v8.2.0
39
+ with:
40
+ enable-cache: true
41
+
42
+ - name: Install dependencies
43
+ run: uv sync --dev
44
+
45
+ - name: Running tests
46
+ run: uv run --with pytest --with pytest-cov pytest --cov-report=term --cov .
47
+
48
+ mypy:
49
+ name: "Mypy Validation"
50
+ runs-on: ubuntu-latest
51
+ steps:
52
+ - name: Check out repository code
53
+ uses: actions/checkout@v7
54
+ with:
55
+ fetch-depth: 1
56
+
57
+ - name: Install uv
58
+ uses: astral-sh/setup-uv@v8.2.0
59
+ with:
60
+ enable-cache: true
61
+
62
+ - name: Install dependencies
63
+ run: uv sync --dev
64
+
65
+ - name: Running Mypy
66
+ run: uv run --with mypy python3 -m mypy src/ tests/ --follow-untyped-imports --explicit-package-bases --check-untyped-defs
@@ -0,0 +1,30 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ *.egg
8
+ .eggs/
9
+
10
+ # Virtual environment
11
+ .venv/
12
+ venv/
13
+
14
+ # IDE
15
+ .vscode/
16
+ .idea/
17
+
18
+ # OS
19
+ .DS_Store
20
+
21
+ # Coverage
22
+ .coverage
23
+ htmlcov/
24
+ coverage/
25
+
26
+ # Environment
27
+ .env
28
+
29
+ # Local tooling
30
+ token-optimizer/
@@ -0,0 +1,119 @@
1
+ Metadata-Version: 2.4
2
+ Name: aqi-in-api
3
+ Version: 0.0.2
4
+ Summary: Python SDK for the AQI.in Air Quality API
5
+ Project-URL: Repository, https://github.com/GuyKh/py-aqi-in-api
6
+ Project-URL: Issues, https://github.com/GuyKh/py-aqi-in-api/issues
7
+ Author: GuyKh
8
+ License-Expression: MIT
9
+ Requires-Python: >=3.11
10
+ Requires-Dist: httpx>=0.28.0
11
+ Requires-Dist: pyjwt>=2.10.0
12
+ Description-Content-Type: text/markdown
13
+
14
+ # py-aqi-in-api
15
+
16
+ Python SDK for the [AQI.in](https://aqi.in) Air Quality API.
17
+
18
+ Fully typed, async, using modern Python (3.11+), dataclasses, and httpx.
19
+
20
+ > **Source**: This is a Python port of the [aqi-in-api](https://github.com/neo773/aqi-in-api) TypeScript SDK by [@neo773](https://github.com/neo773).
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ pip install py-aqi-in-api
26
+ ```
27
+
28
+ Requires Python 3.11+.
29
+
30
+ ## Usage
31
+
32
+ ```python
33
+ import asyncio
34
+
35
+ from aqi_in_api import AQIClient
36
+
37
+
38
+ async def main() -> None:
39
+ client = AQIClient()
40
+
41
+ ip_details = await client.get_ip_details()
42
+ print(f"Location: {ip_details.city}, {ip_details.country}")
43
+
44
+ nearest = await client.get_nearest_location(
45
+ lat=ip_details.lat, long=ip_details.lon,
46
+ )
47
+ station = nearest[0]
48
+ print(f"Nearest station: {station.station} ({station.location_slug})")
49
+
50
+ location = await client.get_location_by_slug(slug=station.location_slug)
51
+ print(f"Location AQI: {location[0].iaqi}")
52
+
53
+ history = await client.get_last_24_hour_history(
54
+ slug=station.location_slug, sensorname="pm25", slug_type="locationId",
55
+ )
56
+ print(f"24h PM2.5 avg: {history.avgValue}")
57
+
58
+ await client.close()
59
+
60
+
61
+ asyncio.run(main())
62
+ ```
63
+
64
+ ## API
65
+
66
+ ### `create_aqi_client(config?)`
67
+
68
+ | Option | Type | Required | Description |
69
+ |--------|------|----------|-------------|
70
+ | `token` | `str \| None` | No | JWT authentication token (auto-generated if omitted) |
71
+ | `base_url` | `str` | No | API base URL (default: `https://apiserver.aqi.in`) |
72
+ | `user_agent` | `str` | No | Custom user agent |
73
+
74
+ ### Methods
75
+
76
+ All methods take keyword-only arguments. No `*Params` objects needed.
77
+
78
+ | Method | Keyword Args | Returns | Description |
79
+ |--------|-------------|---------|-------------|
80
+ | `get_ip_details()` | — | `IPDetails` | Get location from your IP address |
81
+ | `get_nearest_location(**kwargs)` | `lat`, `long`, `type?` | `list[Station]` | Get nearest monitoring stations by coordinates |
82
+ | `get_location_by_slug(**kwargs)` | `slug`, `type?` | `list[LocationDetails]` | Get location details by slug |
83
+ | `search(**kwargs)` | `search_string` | `SearchResults` | Search locations by name |
84
+ | `get_last_12_hour_history(**kwargs)` | `slug`, `sensorname`, `slug_type` | `HistoryData` | Get 12-hour sensor history |
85
+ | `get_last_24_hour_history(**kwargs)` | `slug`, `sensorname`, `slug_type` | `HistoryDataWithWHO` | Get 24-hour history with WHO guidelines |
86
+ | `get_last_7_days_history(**kwargs)` | `slug`, `sensorname`, `slug_type` | `HistoryDataWithWHO` | Get 7-day sensor history |
87
+ | `get_last_30_days_history(**kwargs)` | `slug`, `sensorname`, `slug_type` | `HistoryDataWithWHO` | Get 30-day sensor history |
88
+ | `get_rankings(**kwargs)` | `sensorname`, `type`, `limit=10` | `list[RankingEntry]` | Get city or country pollution rankings |
89
+ | `close()` | — | `None` | Close the underlying HTTP client |
90
+
91
+ ### Types
92
+
93
+ ```python
94
+ from aqi_in_api.models import (
95
+ Station, City, State, Country,
96
+ LocationDetails, IPDetails, SearchResults, RankingEntry,
97
+ HistoryData, HistoryDataWithWHO,
98
+ IAQI, Weather, WeatherCondition, WeatherSimple, UVCondition,
99
+ SensorName, SearchType, SlugType, LocationType, RankType,
100
+ )
101
+ ```
102
+
103
+ ## Development
104
+
105
+ ```bash
106
+ git clone https://github.com/GuyKh/py-aqi-in-api
107
+ cd py-aqi-in-api
108
+ uv sync --dev
109
+ uv run pytest
110
+ uv run ruff check .
111
+ ```
112
+
113
+ ## License
114
+
115
+ MIT
116
+
117
+ ## Disclaimer
118
+
119
+ This is an **unofficial** API client and is not affiliated with, endorsed by, or associated with AQI.in or its parent organization. This package is provided for educational and informational purposes under fair use.
@@ -0,0 +1,106 @@
1
+ # py-aqi-in-api
2
+
3
+ Python SDK for the [AQI.in](https://aqi.in) Air Quality API.
4
+
5
+ Fully typed, async, using modern Python (3.11+), dataclasses, and httpx.
6
+
7
+ > **Source**: This is a Python port of the [aqi-in-api](https://github.com/neo773/aqi-in-api) TypeScript SDK by [@neo773](https://github.com/neo773).
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ pip install py-aqi-in-api
13
+ ```
14
+
15
+ Requires Python 3.11+.
16
+
17
+ ## Usage
18
+
19
+ ```python
20
+ import asyncio
21
+
22
+ from aqi_in_api import AQIClient
23
+
24
+
25
+ async def main() -> None:
26
+ client = AQIClient()
27
+
28
+ ip_details = await client.get_ip_details()
29
+ print(f"Location: {ip_details.city}, {ip_details.country}")
30
+
31
+ nearest = await client.get_nearest_location(
32
+ lat=ip_details.lat, long=ip_details.lon,
33
+ )
34
+ station = nearest[0]
35
+ print(f"Nearest station: {station.station} ({station.location_slug})")
36
+
37
+ location = await client.get_location_by_slug(slug=station.location_slug)
38
+ print(f"Location AQI: {location[0].iaqi}")
39
+
40
+ history = await client.get_last_24_hour_history(
41
+ slug=station.location_slug, sensorname="pm25", slug_type="locationId",
42
+ )
43
+ print(f"24h PM2.5 avg: {history.avgValue}")
44
+
45
+ await client.close()
46
+
47
+
48
+ asyncio.run(main())
49
+ ```
50
+
51
+ ## API
52
+
53
+ ### `create_aqi_client(config?)`
54
+
55
+ | Option | Type | Required | Description |
56
+ |--------|------|----------|-------------|
57
+ | `token` | `str \| None` | No | JWT authentication token (auto-generated if omitted) |
58
+ | `base_url` | `str` | No | API base URL (default: `https://apiserver.aqi.in`) |
59
+ | `user_agent` | `str` | No | Custom user agent |
60
+
61
+ ### Methods
62
+
63
+ All methods take keyword-only arguments. No `*Params` objects needed.
64
+
65
+ | Method | Keyword Args | Returns | Description |
66
+ |--------|-------------|---------|-------------|
67
+ | `get_ip_details()` | — | `IPDetails` | Get location from your IP address |
68
+ | `get_nearest_location(**kwargs)` | `lat`, `long`, `type?` | `list[Station]` | Get nearest monitoring stations by coordinates |
69
+ | `get_location_by_slug(**kwargs)` | `slug`, `type?` | `list[LocationDetails]` | Get location details by slug |
70
+ | `search(**kwargs)` | `search_string` | `SearchResults` | Search locations by name |
71
+ | `get_last_12_hour_history(**kwargs)` | `slug`, `sensorname`, `slug_type` | `HistoryData` | Get 12-hour sensor history |
72
+ | `get_last_24_hour_history(**kwargs)` | `slug`, `sensorname`, `slug_type` | `HistoryDataWithWHO` | Get 24-hour history with WHO guidelines |
73
+ | `get_last_7_days_history(**kwargs)` | `slug`, `sensorname`, `slug_type` | `HistoryDataWithWHO` | Get 7-day sensor history |
74
+ | `get_last_30_days_history(**kwargs)` | `slug`, `sensorname`, `slug_type` | `HistoryDataWithWHO` | Get 30-day sensor history |
75
+ | `get_rankings(**kwargs)` | `sensorname`, `type`, `limit=10` | `list[RankingEntry]` | Get city or country pollution rankings |
76
+ | `close()` | — | `None` | Close the underlying HTTP client |
77
+
78
+ ### Types
79
+
80
+ ```python
81
+ from aqi_in_api.models import (
82
+ Station, City, State, Country,
83
+ LocationDetails, IPDetails, SearchResults, RankingEntry,
84
+ HistoryData, HistoryDataWithWHO,
85
+ IAQI, Weather, WeatherCondition, WeatherSimple, UVCondition,
86
+ SensorName, SearchType, SlugType, LocationType, RankType,
87
+ )
88
+ ```
89
+
90
+ ## Development
91
+
92
+ ```bash
93
+ git clone https://github.com/GuyKh/py-aqi-in-api
94
+ cd py-aqi-in-api
95
+ uv sync --dev
96
+ uv run pytest
97
+ uv run ruff check .
98
+ ```
99
+
100
+ ## License
101
+
102
+ MIT
103
+
104
+ ## Disclaimer
105
+
106
+ This is an **unofficial** API client and is not affiliated with, endorsed by, or associated with AQI.in or its parent organization. This package is provided for educational and informational purposes under fair use.
@@ -0,0 +1,38 @@
1
+ import asyncio
2
+
3
+ from aqi_in_api import create_aqi_client
4
+
5
+
6
+ async def main() -> None:
7
+ client = create_aqi_client()
8
+
9
+ ip_details = await client.get_ip_details()
10
+ print(ip_details)
11
+
12
+ nearest_location = await client.get_nearest_location(
13
+ lat=ip_details.lat,
14
+ long=ip_details.lon,
15
+ )
16
+ print(nearest_location)
17
+
18
+ station = nearest_location[0].location_slug
19
+
20
+ location_details = await client.get_location_by_slug(slug=station)
21
+ print(location_details)
22
+
23
+ history = await client.get_last_24_hour_history(
24
+ slug=station,
25
+ sensorname="pm25",
26
+ slug_type="locationId",
27
+ )
28
+ print(history)
29
+
30
+ history_30_days = await client.get_last_30_days_history(
31
+ slug=station,
32
+ sensorname="pm25",
33
+ slug_type="locationId",
34
+ )
35
+ print(history_30_days)
36
+
37
+
38
+ asyncio.run(main())
@@ -0,0 +1,62 @@
1
+ [project]
2
+ name = "aqi-in-api"
3
+ version = "0.0.2"
4
+ description = "Python SDK for the AQI.in Air Quality API"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license = "MIT"
8
+ authors = [
9
+ { name = "GuyKh" },
10
+ ]
11
+
12
+ dependencies = [
13
+ "httpx>=0.28.0",
14
+ "pyjwt>=2.10.0",
15
+ ]
16
+
17
+ [project.urls]
18
+ Repository = "https://github.com/GuyKh/py-aqi-in-api"
19
+ Issues = "https://github.com/GuyKh/py-aqi-in-api/issues"
20
+
21
+ [build-system]
22
+ requires = ["hatchling"]
23
+ build-backend = "hatchling.build"
24
+
25
+ [tool.hatch.build.targets.wheel]
26
+ packages = ["src/aqi_in_api"]
27
+
28
+ [tool.ruff]
29
+ line-length = 100
30
+ target-version = "py311"
31
+
32
+ [tool.ruff.lint]
33
+ select = ["E", "F", "I", "N", "W"]
34
+
35
+ [tool.ruff.lint.per-file-ignores]
36
+ "src/aqi_in_api/models.py" = ["N815"]
37
+ "src/aqi_in_api/_exceptions.py" = ["N818"]
38
+
39
+ [tool.mypy]
40
+ strict = true
41
+ python_version = "3.11"
42
+ namespace_packages = true
43
+ explicit_package_bases = true
44
+ mypy_path = ["src/"]
45
+ follow_untyped_imports = true
46
+ check_untyped_defs = true
47
+
48
+ [[tool.mypy.overrides]]
49
+ module = "pytest_httpx"
50
+ ignore_missing_imports = true
51
+
52
+ [tool.pytest.ini_options]
53
+ asyncio_mode = "auto"
54
+ testpaths = ["tests"]
55
+
56
+ [dependency-groups]
57
+ dev = [
58
+ "pytest>=9.1.1",
59
+ "pytest-asyncio>=1.4.0",
60
+ "pytest-httpx>=0.36.2",
61
+ "ruff>=0.15.20",
62
+ ]
@@ -0,0 +1,9 @@
1
+ from aqi_in_api._client import AQIClient, ClientConfig, create_aqi_client
2
+ from aqi_in_api._exceptions import AQIException
3
+
4
+ __all__ = [
5
+ "AQIClient",
6
+ "AQIException",
7
+ "ClientConfig",
8
+ "create_aqi_client",
9
+ ]