Part 1 built a page that turns a URL into a plain-English fix list. People who run an audit want to keep it, send it to someone, or paste it into a ticket. This part adds downloads.

You could render the PDF yourself with a headless browser. That means running Chromium, handling fonts, and keeping it patched. The export endpoint does it for you.

The export endpoint

GET /audit/export runs the same audit as /audit and returns a file instead of JSON:

Parameter Values
url The page to audit
format pdf, md or csv
brand_name Your name on the report (Pro and Ultra)
brand_color A hex color for headings (Pro and Ultra)
logo_url A PNG or JPEG logo (Pro and Ultra)

It counts as one audit, the same as the JSON call. The file contains what your key's JSON response contains, including the plain-English sentence and the owner for every fix.

Add a download route

from flask import Response

EXPORT = "https://seoscoreapi.com/audit/export"
TYPES = {"pdf": "application/pdf", "md": "text/markdown", "csv": "text/csv"}
BRAND = {"brand_name": "Acme Web Studio", "brand_color": "#0f766e",
         "logo_url": "https://acme.example/logo.png"}


@app.get("/download")
def download():
    url = request.args.get("url", "").strip()
    fmt = request.args.get("format", "pdf")
    if not url or fmt not in TYPES:
        return "Bad request", 400
    params = {"url": url, "format": fmt}
    if fmt == "pdf":
        params.update(BRAND)          # white-label needs Pro or Ultra; remove on other plans
    r = requests.get(EXPORT, params=params, headers={"X-API-Key": KEY}, timeout=120)
    if r.status_code != 200:
        return r.json().get("detail", "Export failed"), r.status_code
    return Response(r.content, mimetype=TYPES[fmt],
                    headers={"Content-Disposition": f'attachment; filename="audit.{fmt}"'})

Then add links under the report in the home() view:

    links = " · ".join(
        f'<a href="/download?url={escape(url)}&format={f}">{f.upper()}</a>' for f in TYPES)

Which format for what

  • PDF is for sending to a person. With the three brand parameters it carries your name, color and logo instead of ours.
  • Markdown pastes cleanly into a GitHub issue, a Notion page or an email.
  • CSV has one row per fix, with severity and owner as columns. It imports into a spreadsheet or a ticket tracker, which is how an audit becomes a work plan.

Two details worth knowing

White-label is plan-gated. On plans below Pro, sending the brand parameters returns a 403. The code above only sends them for PDFs, so the Markdown and CSV downloads work on any plan.

Don't run the audit twice. As written, viewing the report and then downloading it spends two audits. If you cached the JSON in part 1, build the Markdown and CSV from the cached result yourself and only call the export endpoint for the PDF.

Next

The tool now produces something people will pay for. Part 3 adds a free preview and a one-time payment to unlock the full report.