# How I Built a Carbon Footprint Tracker with Django + NVIDIA NIM

> Source: <https://dev.to/divyansh0208/how-i-built-a-carbon-footprint-tracker-with-django-nvidia-nim-4jp3>
> Published: 2026-06-24 16:01:00+00:00

I wanted to build something that mattered. Climate change is one of those topics where awareness is the first barrier — most people don't know their actual footprint. So I built **Carbon.Ledger**: a full-stack web app that lets you log activities, see your CO₂ impact, and get AI-powered tips to cut back.

Here's how I built it, what went wrong, and the one engineering decision I'm most proud of.

`mistralai/mistral-large-3-675b-instruct-2512`

The app is built around one simple loop:

**Understand → Track → Reduce**

Users can read lesson articles, browse a glossary (CO₂e, Scope 1/2/3, Net Zero), and ask free-text climate questions via an LLM-powered Q&A — rate limited to 10 questions/day.

An activity logging form lets users log transport, energy, food, and goods. CO₂ is auto-computed on save using emission factors stored in the database — no LLM involved here. The dashboard shows a 30-day breakdown with a Chart.js donut chart and progress against a monthly goal.

Rule-based insights compare the user's totals against national average benchmarks. The top emission category drives filtered recommendations. And once per day, the LLM generates a personalized tip — cached 24h per user.

This is the part I'm most proud of.

LLMs make up numbers. That's a real problem when you're talking about emissions data — if the model says "beef produces 5 kg CO₂ per kg" when the real figure is 27, that's actively harmful misinformation.

My solution in `services/llm.py`

:

``` php
def _contains_hallucinated_numbers(response: str, context: str) -> bool:
    """
    Extract all numbers from the LLM response.
    Check every one of them exists in the context we provided.
    If not → hallucination detected.
    """
    import re
    response_numbers = set(re.findall(r'\d+\.?\d*', response))
    context_numbers = set(re.findall(r'\d+\.?\d*', context))
    return not response_numbers.issubset(context_numbers)
```

The flow:

`timeout=10.0`

and `max_retries=1`

are explicitly set to prevent hung Gunicorn worker threads on slow LLM responses.**Key principle: the LLM is never used for calculations. All math is Python/DB.**

The activity log form has a dependent dropdown — selecting a category dynamically loads the relevant emission factors. I used htmx for this instead of writing custom JavaScript:

```
<select name="category" 
        hx-get="/factors/"
        hx-target="#factor-select"
        hx-trigger="change">
```

One attribute. No JS file. The server returns a partial HTML snippet with the filtered factors. This is exactly what htmx is built for.

Render makes Django deployment straightforward. A few Django-specific things worth noting:

```
# settings.py
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SECURE_SSL_REDIRECT = not DEBUG
SESSION_COOKIE_SECURE = not DEBUG
CSRF_COOKIE_SECURE = not DEBUG
```

Render terminates TLS at the proxy level, so `SECURE_PROXY_SSL_HEADER`

is essential — without it Django won't recognize HTTPS requests and will redirect loop forever.

Also: randomize your admin URL via env var:

```
ADMIN_URL = env('ADMIN_URL', default='admin/')
```

Stops bots from hammering the default `/admin/`

endpoint.

**Async LLM calls.** Right now the LLM calls are synchronous — a slow NVIDIA NIM response blocks a Gunicorn worker. The fix is Celery + Redis for background tasks. I skipped it for the MVP but it's the first thing I'd add.

**Audited emission factors.** The 22 factors I seeded are approximations from public sources, not peer-reviewed figures. For a production app, you'd want to integrate a verified dataset like the UK Government GHG conversion factors.

**Email verification.** Currently anyone can sign up with any email. Not a problem for an MVP, but a real gap.

If you're building something with Django + LLMs, the hallucination guard pattern is worth stealing. And if you have thoughts on better emission factor datasets, I'd love to hear them in the comments.
