# Flask

Serve the ProductLense script from your Flask app and forward events with the visitor's real IP address on the Scale plan.

Two routes in your Flask app serve the script from your domain and, on the Scale plan, forward events. Read the [proxy overview](/docs/proxy/overview) first for what each one fixes.

## The routes

```python title="pl_proxy.py"
import os
import time

import requests
from flask import Blueprint, Response, request

productlense = Blueprint("productlense", __name__)
_script = {"body": b"", "at": 0.0}


@productlense.get("/js/a.js")
def script():
    if time.time() - _script["at"] > 3600:
        r = requests.get("https://api.productlense.app/a.js", timeout=5)
        if r.ok:
            _script.update(body=r.content, at=time.time())
    if not _script["body"]:
        return Response(status=502)
    return Response(_script["body"], mimetype="application/javascript",
                    headers={"Cache-Control": "public, max-age=86400"})


@productlense.post("/m/api/e")
def events():
    if request.content_length and request.content_length > 8192:
        return Response(status=413)
    body = request.get_json(force=True, silent=True)
    if not isinstance(body, dict):
        return Response(status=400)
    extra = {"ip": request.remote_addr or "", "ua": request.headers.get("User-Agent", "")}
    if isinstance(body.get("b"), list):
        body["b"] = [{**e, **extra} for e in body["b"] if isinstance(e, dict)]
    else:
        body.update(extra)
    r = requests.post("https://api.productlense.app/api/v1/events", json=body, timeout=5,
                      headers={"Authorization": f"Bearer {os.environ['PL_API_KEY']}"})
    return Response(status=r.status_code)
```

```python title="app.py"
from flask import Flask
from werkzeug.middleware.proxy_fix import ProxyFix

from pl_proxy import productlense

app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1)  # behind one proxy: Nginx, a load balancer
app.register_blueprint(productlense)
```

`get_json(force=True)` reads the body even though the browser sends it as `text/plain`. `ProxyFix` makes `request.remote_addr` the visitor's address when the app runs behind a reverse proxy; without a proxy, leave it out.

## Use it

Serve only the script on any plan:

```html
<script defer data-site="SITE_ID" data-api="https://api.productlense.app" src="/js/a.js"></script>
```

On the Scale plan, set `PL_API_KEY` to a key with the `events` scope and change the tag to `data-api="https://example.com/m"`.

## Check it works

1. Open `https://example.com/js/a.js`: it returns JavaScript.
2. Load a page with the developer tools open. The Network tab shows a `POST` to `api/e` with status `204`.
3. Open the Live page in the ProductLense dashboard. Your visit appears within a few seconds.
