AirAware India with AI and Google Cloud
Air quality has become something many of us check almost like the weather. We open an app, search for a city, and see a number:
AQI: 168 — Moderate
I started thinking about what happens after that.
What does that number actually tell an ordinary person? Which pollutant is doing most of the work? How different is the air between home and work? And can that information be explained without expecting everyone to read a table of PM2.5, PM10, NO₂ and the rest?
That question became AirAware India.
The working app is live: airaware-dt66p2y4ea-uc.a.run.app
From an AQI number to something you can ask
India already has a serious monitoring ecosystem. The Central Pollution Control Board (CPCB) defines the national AQI (NAQI) from pollutants such as PM2.5, PM10, NO₂, SO₂, CO and O₃.
The gap I wanted to close is not “there is no data.”
It is the gap between having measurements and being able to interrogate them in plain language.
AirAware is an air-quality explainer, not another dashboard. You sign in, save places, see official readings, then ask Gemini about those readings — not about whatever the model remembers about Delhi or Hyderabad.
Typical questions:
"Why is the air quality poor today?"
"Which pollutant is contributing the most?"
"Compare home and office."
"What changed since yesterday?"
"Explain this without the technical terminology."
AI explains the data. It does not invent it.
Large language models are good at questions and explanations. They should not be the source of environmental numbers.
If you ask AirAware about current air quality, Gemini does not guess an AQI.
The path is:
User question
↓
FastAPI (Cloud Run)
↓
Official Air Quality API (current + ~24h history)
↓
Validate / normalise
↓
JSON of measurements
↓
Gemini (Vertex AI)
↓
Explanation grounded only on that JSON
Two jobs stay separate.
The backend decides what was measured.
Gemini decides how to say it.
If Google has no reading, or history is missing, the app says so. It does not ask the model to fill the hole.
What is actually running
This started as a concept for the Gen AI Academy APAC Cohort 3 Ideathon (Cloud Run, Firebase, Gemini). The prototype is now a single Cloud Run service: the React app and the FastAPI backend share one origin.
Cloud Run
One container serves the UI and /api. No Gemini key, Firestore credential, or Air Quality key is shipped to the browser. The Air Quality API key lives in Secret Manager. Gemini uses Vertex AI and the Cloud Run service account.
Firebase Authentication
Google Sign-In. Each account has its own saved places and chats.
Firestore
Profiles, locations, conversations and messages sit under that user’s UID. The browser never talks to Firestore. FastAPI uses the verified Firebase ID token.
Gemini on Vertex AI
Gemini only sees JSON the backend already fetched: current AQI for saved places, hourly history, index name and scale. Org policy blocked API keys, so this is Vertex + Application Default Credentials, not a Gemini API key in .env.
Official measurements
Current conditions and history come from the Google Air Quality API, with India CPCB / NAQI preferred when Google returns it, otherwise Universal AQI (UAQI). Place search uses OpenStreetMap / Nominatim so the product does not depend on Maps JavaScript billing.
Two AQI scales (this matters)
A number on screen is not enough. The scales run in opposite directions.
Universal AQI (UAQI) is roughly 0–100. Higher is better.
81 with “Excellent” and PM2.5 around 3 µg/m³ is clean air.
India NAQI (CPCB) is 0–500. Lower is better.
88 with “Satisfactory” and PM2.5 around 45 µg/m³ is a different story.
If you only show 81 next to 88, people will think 88 is better. The AirAware reading card now leads with the category, then value / max, then the index name and whether higher or lower is better. Chat is told the same rule: never compare raw numbers across different indexes; compare the same index, or compare pollutants.
Beyond a coloured circle
Why is my air bad?
The card and the chat both see official pollutant concentrations. Gemini can point at the dominant pollutant if it is in the JSON.
Compare my locations
Save Home, Office, or anywhere you search. Chat loads official AQI for each saved place (not only the one in the dropdown), so “compare home and office” is a real comparison.
What changed since yesterday?
The backend pulls about the last 24 hours of official hourly history and a simple now-vs-~24h delta. Gemini explains that series. It does not invent a weekly trend we do not have.
Imperfect data, on purpose
Sensor-backed APIs are messy: missing hours, UAQI in one city and NAQI in another, history that does not always exist.
The pipeline is:
Google Air Quality API
↓
Normalise (index, pollutants, units)
↓
Cache ~1 hour per location
↓
Gemini only sees the JSON
↓
User
Unavailable history or a failed place shows up as an error field, not as a confident story.
Responsible AI, as product constraints
Air quality is adjacent to health. AirAware explains environmental readings. It does not diagnose illness or replace a clinician.
- AQI and concentrations come from the API, not from model weights.
- Each reading keeps location, index, and observation time when the API provides them.
- Missing data is stated, not smoothed over.
- User data is scoped by Firebase UID.
- Secrets stay on the server.
Those rules are the product, not a later ethics appendix.
Try it
- Open the live app and sign in with Google.
- Save two places.
- Read the AQI card — category first, then the scale.
- In chat, ask why it is poor, compare home and office, or what changed since yesterday.
The question I am still exploring is the same one that started this:
Can generative AI make public environmental data easier to use, rather than just generating more text about it?
AirAware India is my attempt to keep the numbers honest and still make them speak.
#AccelerateAIwithCloudRun

Top comments (0)