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.