# Lou·Ka
### Tous les logements à louer du Québec. Un seul endroit.
**[www.lou-ka.com](https://www.lou-ka.com)**









*Agrégateur indépendant de logements locatifs — chaque annonce avec toutes ses photos,
ses détails standardisés, et un lien direct vers l'annonce originale du gestionnaire.
Toujours à jour, automatiquement.*
---
## Pourquoi Lou-Ka ?
Chercher un appartement au Québec, c'est ouvrir 70 sites web différents — chacun avec sa
propre navigation, ses propres filtres, son propre format. **Lou-Ka retourne le problème** :
un connecteur dédié par gestionnaire immobilier visite chaque site, normalise chaque annonce
vers un schéma unique, et détecte les changements en continu.
> Les sites d'agences n'offrent pas de webhooks. Lou-Ka reproduit l'équivalent :
> **synchronisation périodique + hash de contenu** → ajouts, mises à jour et retraits
> détectés automatiquement. Une annonce qui disparaît du site source disparaît de Lou-Ka.
## L'architecture en 30 secondes
```mermaid
flowchart LR
subgraph Sources["74 gestionnaires immobiliers"]
S1["Logisco · Cogir · CAPREIT
Immostar · DMA · Laberge
Akelius · Devimco · Mondev
… 68 connecteurs actifs"]
end
subgraph LouKa["Lou-Ka"]
C["Connecteurs
1 adaptateur / site"] --> N["Normalisation
schéma Listing unique"]
N --> D[("SQLite
hash + diff")]
D --> A["API FastAPI
/api/listings · /api/facets"]
A --> F["React 18 + Vite
PWA mobile · thème clair"]
end
W["⏱ Watcher horaire
(PM2)"] -.-> C
S1 --> C
F --> U["🔑 Locataire"]
```
| Couche | Rôle | Fichiers |
|---|---|---|
| **Connecteurs** | 1 module Python par gestionnaire : HTML rendu serveur, API JSON internes (Building Stack, RealVuu, Planpoint, Rentsync, source.immo, JetEngine…), ou Firecrawl pour les sites derrière Cloudflare | `louka/connectors/*.py` |
| **Schéma** | `Listing` standardisé : adresse, secteur, ville, type (3½…), prix, disponibilité, commodités, **toutes les images** | `louka/schema.py` |
| **Diff engine** | Upsert par hash de contenu — nouvelle / modifiée / disparue (désactivée) | `louka/db.py` |
| **API** | Filtres ville / secteur / taille / prix / gestionnaire / recherche, facettes, stats, déclencheur de sync | `louka/web.py` |
| **Frontend** | Design « éditorial sharp » : Space Grotesk, ombres décalées, accent lime, ticker temps réel, bottom sheet mobile, galeries photos, PWA installable | `frontend/` |
## Démarrage rapide
```bash
git clone https://github.com/spboucher-ai/lou-ka.git && cd lou-ka
# Backend
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
# Frontend
cd frontend && npm install && npm run build && cd ..
# (optionnel) sites JavaScript/anti-bot
echo "FIRECRAWL_API_KEY=fc-votre-cle" > .env
# Ingestion puis service
.venv/bin/python run.py sync # toutes les sources (ou: run.py sync logisco msi)
.venv/bin/python run.py serve 8080 # → http://localhost:8080
.venv/bin/python run.py watch 60 # resynchronisation en boucle (minutes)
```
## Ajouter un gestionnaire (≈ 30 lignes)
L'enregistrement est **auto-découvrant** : déposez un module dans `louka/connectors/`,
c'est tout — aucun fichier partagé à modifier.
```python
# louka/connectors/mon_agence.py
from ..schema import Listing, infer_city, normalize_unit_type, parse_price
from .base import BaseConnector
class MonAgenceConnector(BaseConnector):
source_id = "mon_agence"
def fetch(self) -> list[Listing]:
html = self.get("https://mon-agence.ca/logements").text # throttlé, poli
# ... parser les cartes, les fiches, les photos ...
return [Listing(
source=self.source_id, external_id="123",
url="https://mon-agence.ca/logement/123",
title="555, avenue Exemple", sector="Limoilou",
city=infer_city("Limoilou"), unit_type=normalize_unit_type("4 1/2"),
price=parse_price("1 250 $ / mois"), images=[...],
)]
```
Puis : `.venv/bin/python run.py sync mon_agence` — et l'annonce apparaît sur le site,
avec sa fiche, sa galerie et son lien source. Ajoutez l'entrée correspondante dans
`data/sources.json` pour la page **Sources**.
## API
| Endpoint | Description |
|---|---|
| `GET /api/listings?city=§or=&unit_type=&source=&price_min=&price_max=&q=` | Recherche filtrée, triée par prix |
| `GET /api/listings/{uid}` | Fiche complète (toutes les images, commodités, source) |
| `GET /api/facets` | Valeurs distinctes pour construire les filtres |
| `GET /api/sources` | Registre des 74 gestionnaires + compteurs + dernière sync |
| `GET /api/stats` | Totaux par région, loyer moyen, journal de synchronisation |
| `POST /api/sync` | Déclenche une synchronisation en arrière-plan |
## Couverture
**Ville de Québec & Lévis** — Logisco, Cogir, Groupe Laberge, Immostar, DMA/Locago,
Groupe Dallaire, Trudel, Immeubles Roussin, Immeubles Simard, MSI, Gestipro, Logisma,
Lafrance & Mathieu, SIB, SDG, SGIQ, GIM Côté, Logisbourg, Bribourg, Paul-E. Richard,
Headway, Contraste, Appartements Urbains, Picard, Brochu, GParadis, CAPREIT, Lokalia,
Immoappart, OK Louer, et une douzaine de complexes (Huma, Le Clif, Terra, La Klé,
Sentinelle, Rivero, Viridi, Quartier les Éléments…).
**Grand Montréal** — Akelius, InterRent, Boardwalk, Minto, MetCap, Realstar, Hazelview,
Groupe Copley, Cromwell, Lynk/Olymbec, Trylon, Plan A, Lofts MTL, Axia, Mondev, Devimco,
Collection Équinoxe (Batimo/EMD), Progim, Rentalys, UTILE, Werkliv, 1 Square Phillips,
Firma, Le Domaine, Beaudoin, Denux, Gestion Montréal, Nid d'Amour, SHDM…
Chaque source non-connectable est **documentée avec sa raison** dans `data/sources.json`
(ex. : aucun prix affiché, inventaire vide, site placeholder).
## Production
Déployé sous **PM2** (3 processus) derrière **ngrok** :
```
lou-ka-web .venv/bin/python run.py serve 8095 # API + frontend
lou-ka-sync .venv/bin/python run.py watch 60 # resync horaire
lou-ka-ngrok ngrok http --url=www.lou-ka.com 8095 # tunnel
```
Philosophie d'exploitation : **on ne pousse que le code — le serveur maintient ses
données lui-même.**
## Principes
1. **Politesse** — délai ≥ 0,5 s entre requêtes, garde-fous de crawl, User-Agent identifié.
2. **Fidélité** — aucun prix inventé : si la source n'affiche pas de prix, `price = null`.
3. **Traçabilité** — chaque fiche renvoie vers l'annonce originale du gestionnaire.
4. **Robustesse** — un connecteur qui casse n'affecte jamais les autres (auto-découverte
tolérante, try/except par annonce, journal `sync_log`).
---
## Auteur
**Simon-Pierre Boucher**
[](mailto:contact@spboucher.ai)
[](https://github.com/spboucher-ai)
*Conçu, construit et déployé en une journée — de la recherche de marché
(74 gestionnaires recensés et vérifiés) au produit en production.*
© 2026 Simon-Pierre Boucher — tous droits réservés.