From 7e6baa968bfba5f128021fb9ba3d77e2727c8c0c Mon Sep 17 00:00:00 2001 From: Vinta Chen Date: Wed, 21 Oct 2026 08:16:20 +0800 Subject: [PATCH 1/7] feat(website): send a GA4 event on sponsorship email clicks Mailto clicks on the sponsorship page leave no page view, so the sponsor funnel ended at the sponsorship page and never showed who went on to email. Each click now sends a sponsor_contact event with a short tier value (headline, featured, indie, or general) rather than the link URL, since GA4 caps event parameter values at 100 characters and the Indie Dev mailto href is 101. Co-Authored-By: Claude --- website/static/main.js | 7 +++++++ website/templates/sponsorship.html | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/website/static/main.js b/website/static/main.js index 72dd4e0f..8ca095ba 100644 --- a/website/static/main.js +++ b/website/static/main.js @@ -399,6 +399,13 @@ document.addEventListener("click", function (e) { } }); +// Mailto clicks leave no page view, so the sponsor funnel needs its own event +document.addEventListener("click", function (e) { + const link = e.target.closest('a[href^="mailto:sponsorship@"]'); + if (!link || typeof gtag !== "function") return; + gtag("event", "sponsor_contact", { tier: link.dataset.tier }); +}); + if (location.hash) { const linkedRow = document.getElementById(decodeURIComponent(location.hash.slice(1))); if (linkedRow && linkedRow.matches("tr.row")) openEntryRow(linkedRow); diff --git a/website/templates/sponsorship.html b/website/templates/sponsorship.html index 34de3517..2c7de2fc 100644 --- a/website/templates/sponsorship.html +++ b/website/templates/sponsorship.html @@ -45,6 +45,7 @@
Email sponsorship@awesome-python.com @@ -128,6 +129,7 @@ Email about Headline tier @@ -151,6 +153,7 @@ Email about Featured tier @@ -180,6 +183,7 @@ Email about Indie Dev tier @@ -225,6 +229,7 @@ Email sponsorship@awesome-python.com with the four items below. @@ -261,6 +266,7 @@
Email sponsorship@awesome-python.com From 07c3e3eb4c4ebdb4000d9780892b623add94e8b1 Mon Sep 17 00:00:00 2001 From: Vinta Chen Date: Wed, 21 Oct 2026 08:17:08 +0800 Subject: [PATCH 2/7] feat: add annual sponsorship pricing with 3 months free Co-Authored-By: Claude --- SPONSORSHIP.md | 10 +++++----- website/templates/sponsorship.html | 13 +++++++------ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/SPONSORSHIP.md b/SPONSORSHIP.md index 1e27bee5..6e4e9846 100644 --- a/SPONSORSHIP.md +++ b/SPONSORSHIP.md @@ -21,17 +21,17 @@ Your sponsorship puts your product in front of developers at the exact moment th ## Sponsorship Tiers -### Headline Sponsor - $500/month +### Headline Sponsor - $500/month or $4,500/year - Large logo and one-line description (max 120 characters) pinned at the very top of the README, above all project entries - Logo link in the sponsor section of [awesome-python.com](https://awesome-python.com/) -### Featured Sponsor - $200/month +### Featured Sponsor - $200/month or $1,800/year - Text entry (`[Name](URL) - Description.`, max 120 characters) pinned at the top of the README, directly below Headline sponsors - Text link in the sponsor section of [awesome-python.com](https://awesome-python.com/) -### Indie Dev Sponsor - $99/month or $249/quarter +### Indie Dev Sponsor - $99/month, $249/quarter, or $891/year - Lower-cost tier for indie developers, solo founders, and one-person companies only - Text entry (`[Name](URL) - Description.`, max 120 characters) in the README sponsor section, directly below Featured sponsors @@ -43,9 +43,9 @@ Indie Dev Sponsor is available only to founder-led (one-person company) products Email [sponsorship@awesome-python.com](mailto:sponsorship@awesome-python.com?subject=Awesome%20Python%20Sponsorship) with: -- **Tier:** Headline Sponsor ($500/mo), Featured Sponsor ($200/mo), or Indie Dev Sponsor ($99/mo or $249/quarter) +- **Tier:** Headline Sponsor ($500/mo or $4,500/year), Featured Sponsor ($200/mo or $1,800/year), or Indie Dev Sponsor ($99/mo, $249/quarter, or $891/year) - **Content:** Product name, URL, logo, and description (Headline tier) or `[Name](URL) - Description.` entry (Featured or Indie Dev tier) -- **Duration:** 1, 3, 6 months, or longer +- **Duration:** 1, 3, or 6 months, or 1 year (3 months free) - **Payment method:** card via [Open Collective](https://opencollective.com/awesome-python), US bank transfer (ACH/wire), or PayPal One upfront payment per term. Setup takes less than 24 hours. diff --git a/website/templates/sponsorship.html b/website/templates/sponsorship.html index 2c7de2fc..d7904baf 100644 --- a/website/templates/sponsorship.html +++ b/website/templates/sponsorship.html @@ -115,7 +115,7 @@

Headline Sponsor

$500 - / month + / month or $4,500 / year

Logo pinned at the top of the README. Logo on the website. @@ -138,7 +138,7 @@

Featured Sponsor

$200 - / month + / month or $1,800 / year

Text link pinned at the top of the README. Text link on the website. @@ -162,7 +162,7 @@

Indie Dev Sponsor

$99 - / month or $249 / quarter + / month, $249 / quarter, or $891 / year

Lower-cost text link for eligible indie developers and solo @@ -238,8 +238,9 @@

Tier
- Headline Sponsor ($500/mo), Featured Sponsor ($200/mo), or Indie Dev - Sponsor ($99/mo or $249/quarter). + Headline Sponsor ($500/mo or $4,500/year), Featured Sponsor + ($200/mo or $1,800/year), or Indie Dev Sponsor ($99/mo, $249/quarter, + or $891/year).
@@ -252,7 +253,7 @@
Duration
-
1, 3, 6 months, or longer.
+
1, 3, or 6 months, or 1 year (3 months free).
Payment method
From 94f70f84254e2f792ce90e0a4fd20850fe4606d8 Mon Sep 17 00:00:00 2001 From: Vinta Chen Date: Wed, 21 Oct 2026 08:17:09 +0800 Subject: [PATCH 3/7] feat: show lowest sponsor price and link README CTA to the website The README sponsor CTA linked to SPONSORSHIP.md, so sponsor-page visits from the README never reached GA4. Co-Authored-By: Claude --- README.md | 2 +- website/templates/sponsorship.html | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index db0ac398..abf49fef 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ An opinionated guide to the best Python frameworks, libraries, and tools. - [iPulse AI](https://ipulseai.com) - Open Agentic Investment Research Platform. Inspect deep multi-agent market forecasts and stock picks. -> The **#10 most-starred repo on GitHub**. Put your product in front of Python developers. [Become a sponsor](SPONSORSHIP.md). +> **Sponsor slots open**, from $99/month. The **#10 most-starred repo on GitHub**. Put your product in front of Python developers. [Become a sponsor](https://awesome-python.com/sponsorship/). ## Categories diff --git a/website/templates/sponsorship.html b/website/templates/sponsorship.html index d7904baf..82c36295 100644 --- a/website/templates/sponsorship.html +++ b/website/templates/sponsorship.html @@ -32,7 +32,8 @@

The #10 most-starred repository on GitHub, and the list Python developers check when choosing what to use. Your sponsorship puts your - product in front of them at the moment of decision. + product in front of them at the moment of decision. Headline $500, + Featured $200, Indie Dev from $99 a month.

{% if hero_stats %} From 69d35b24621aa2117d9cec90a58a66085b39f27f Mon Sep 17 00:00:00 2001 From: Vinta Chen Date: Wed, 21 Oct 2026 08:54:23 +0800 Subject: [PATCH 4/7] feat: tag sponsor links with UTM parameters Sponsor links carried no UTM tags, so sponsors saw awesome-python clicks at best as generic referral traffic and could not tell README clicks from website clicks. README sponsor entries now carry utm_source=awesome-python&utm_medium=sponsorship&utm_campaign=sponsor&utm_content=readme, and the website build rewrites utm_content to website for the sponsor band. New sponsor entries in README.md need the same UTM suffix. Co-Authored-By: Claude --- README.md | 2 +- SPONSORSHIP.md | 2 ++ website/build.py | 13 ++++++++++ website/tests/test_build.py | 49 +++++++++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index abf49fef..83107e9f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ An opinionated guide to the best Python frameworks, libraries, and tools. ## **Sponsors** -- [iPulse AI](https://ipulseai.com) - Open Agentic Investment Research Platform. Inspect deep multi-agent market forecasts and stock picks. +- [iPulse AI](https://ipulseai.com?utm_source=awesome-python&utm_medium=sponsorship&utm_campaign=sponsor&utm_content=readme) - Open Agentic Investment Research Platform. Inspect deep multi-agent market forecasts and stock picks. > **Sponsor slots open**, from $99/month. The **#10 most-starred repo on GitHub**. Put your product in front of Python developers. [Become a sponsor](https://awesome-python.com/sponsorship/). diff --git a/SPONSORSHIP.md b/SPONSORSHIP.md index 6e4e9846..8e20d6cb 100644 --- a/SPONSORSHIP.md +++ b/SPONSORSHIP.md @@ -50,6 +50,8 @@ Email [sponsorship@awesome-python.com](mailto:sponsorship@awesome-python.com?sub One upfront payment per term. Setup takes less than 24 hours. +Sponsor links carry UTM tags, so clicks show up in your own analytics as `awesome-python / sponsorship`. + ## Previously Sponsored By - [Warp](https://www.warp.dev/) - The terminal for modern developers. diff --git a/website/build.py b/website/build.py index fde14149..5d90459b 100755 --- a/website/build.py +++ b/website/build.py @@ -11,6 +11,7 @@ from collections.abc import Sequence from datetime import UTC, datetime from pathlib import Path from typing import TypedDict +from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit from jinja2 import Environment, FileSystemLoader from markdown_it import MarkdownIt @@ -130,6 +131,16 @@ def extract_github_repo(url: str) -> str | None: return m.group(1) if m else None +def sponsor_website_url(url: str) -> str: + """Set utm_content=website so sponsors can tell website clicks from README clicks.""" + parts = urlsplit(url) + pairs = parse_qsl(parts.query, keep_blank_values=True) + if not any(k == "utm_content" for k, _ in pairs): + return url + query = [(k, "website" if k == "utm_content" else v) for k, v in pairs] + return urlunsplit(parts._replace(query=urlencode(query))) + + def load_stars(path: Path) -> dict[str, dict]: """Load star data from JSON. Returns empty dict if file doesn't exist or is corrupt.""" if path.exists(): @@ -749,6 +760,8 @@ def build(repo_root: Path) -> None: parsed_groups = parse_readme(readme_text) sponsors = parse_sponsors(readme_text) + for sponsor in sponsors: + sponsor["url"] = sponsor_website_url(sponsor["url"]) categories = [cat for g in parsed_groups for cat in g["categories"]] cat_slugs = [cat["slug"] for cat in categories] diff --git a/website/tests/test_build.py b/website/tests/test_build.py index 7bba67be..1ccefc6b 100644 --- a/website/tests/test_build.py +++ b/website/tests/test_build.py @@ -26,6 +26,7 @@ from build import ( load_pypi_badges, load_stars, sort_entries, + sponsor_website_url, subcategory_path, ) from readme_parser import parse_readme, slugify @@ -458,6 +459,37 @@ class TestBuild: assert llms_txt != readme assert "# Contributing" not in llms_txt + def test_homepage_sponsor_link_tags_website_clicks(self, tmp_path): + readme = textwrap.dedent("""\ + # Awesome Python + + Intro. + + ## **Sponsors** + + - [Sponsor](https://sponsor.example.com/?ref=x&utm_source=awesome-python&utm_content=readme) - Sponsored tool. + + ## Projects + + **Tools** + + ## Widgets + + - [w1](https://example.com) - A widget. + + # Contributing + + Help! + """) + (tmp_path / "README.md").write_text(readme, encoding="utf-8") + self._copy_real_templates(tmp_path) + + build(tmp_path) + + index_html = (tmp_path / "website" / "output" / "index.html").read_text(encoding="utf-8") + assert 'href="https://sponsor.example.com/?ref=x&utm_source=awesome-python&utm_content=website"' in index_html + assert "utm_content=readme" not in index_html + def test_build_cleans_stale_output(self, tmp_path): readme = textwrap.dedent("""\ # T @@ -1278,6 +1310,23 @@ class TestExtractGithubRepo: assert extract_github_repo("https://github.com/org") is None +# --------------------------------------------------------------------------- +# sponsor_website_url +# --------------------------------------------------------------------------- + + +class TestSponsorWebsiteUrl: + def test_replaces_readme_utm_content(self): + url = "https://example.com?utm_source=awesome-python&utm_medium=sponsorship&utm_campaign=sponsor&utm_content=readme" + assert sponsor_website_url(url) == "https://example.com?utm_source=awesome-python&utm_medium=sponsorship&utm_campaign=sponsor&utm_content=website" + + def test_keeps_path_and_other_params(self): + assert sponsor_website_url("https://example.com/landing?ref=x&utm_content=readme") == "https://example.com/landing?ref=x&utm_content=website" + + def test_leaves_untagged_url_unchanged(self): + assert sponsor_website_url("https://example.com") == "https://example.com" + + # --------------------------------------------------------------------------- # load_stars # --------------------------------------------------------------------------- From 636584efd2e79a30b2cc464c4ffbe269540fd9d8 Mon Sep 17 00:00:00 2001 From: Vinta Chen Date: Wed, 21 Oct 2026 08:54:49 +0800 Subject: [PATCH 5/7] feat: include a small logo in the Featured sponsor tier Featured cost $200/month, about twice Indie Dev's $99, yet offered the same text-only README entry and website link, leaving little visible reason to pick it over Indie Dev. Featured now includes a small logo next to its README entry and a logo on the website, and the Get Started content list asks Featured sponsors for a logo. Co-Authored-By: Claude --- SPONSORSHIP.md | 6 +++--- website/templates/sponsorship.html | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/SPONSORSHIP.md b/SPONSORSHIP.md index 8e20d6cb..f788606e 100644 --- a/SPONSORSHIP.md +++ b/SPONSORSHIP.md @@ -28,8 +28,8 @@ Your sponsorship puts your product in front of developers at the exact moment th ### Featured Sponsor - $200/month or $1,800/year -- Text entry (`[Name](URL) - Description.`, max 120 characters) pinned at the top of the README, directly below Headline sponsors -- Text link in the sponsor section of [awesome-python.com](https://awesome-python.com/) +- Small logo and text entry (`[Name](URL) - Description.`, max 120 characters) pinned at the top of the README, directly below Headline sponsors +- Logo and text link in the sponsor section of [awesome-python.com](https://awesome-python.com/) ### Indie Dev Sponsor - $99/month, $249/quarter, or $891/year @@ -44,7 +44,7 @@ Indie Dev Sponsor is available only to founder-led (one-person company) products Email [sponsorship@awesome-python.com](mailto:sponsorship@awesome-python.com?subject=Awesome%20Python%20Sponsorship) with: - **Tier:** Headline Sponsor ($500/mo or $4,500/year), Featured Sponsor ($200/mo or $1,800/year), or Indie Dev Sponsor ($99/mo, $249/quarter, or $891/year) -- **Content:** Product name, URL, logo, and description (Headline tier) or `[Name](URL) - Description.` entry (Featured or Indie Dev tier) +- **Content:** Product name, URL, logo, and description (Headline tier), logo and `[Name](URL) - Description.` entry (Featured tier), or `[Name](URL) - Description.` entry (Indie Dev tier) - **Duration:** 1, 3, or 6 months, or 1 year (3 months free) - **Payment method:** card via [Open Collective](https://opencollective.com/awesome-python), US bank transfer (ACH/wire), or PayPal diff --git a/website/templates/sponsorship.html b/website/templates/sponsorship.html index 82c36295..46d546d8 100644 --- a/website/templates/sponsorship.html +++ b/website/templates/sponsorship.html @@ -142,15 +142,18 @@ / month or $1,800 / year

- Text link pinned at the top of the README. Text link on the website. + Small logo and text link pinned at the top of the README. Logo and + text link on the website.

  • - Text entry ([Name](URL) - Description., max 120 - characters) pinned at the top of the README, directly below - Headline sponsors. + Small logo and text entry + ([Name](URL) - Description., max 120 characters) + pinned at the top of the README, directly below Headline sponsors. +
  • +
  • + Logo and text link in the sponsor section of awesome-python.com.
  • -
  • Text link in the sponsor section of awesome-python.com.
Content
- Product name, URL, logo, and description (Headline tier), or - [Name](URL) - Description. entry (Featured or Indie Dev - tier). + Product name, URL, logo, and description (Headline tier), logo and + [Name](URL) - Description. entry (Featured tier), or + [Name](URL) - Description. entry (Indie Dev tier).
From 45ad644f1b29b5672f4dcdd7637d86c24bd50752 Mon Sep 17 00:00:00 2001 From: Vinta Chen Date: Wed, 21 Oct 2026 08:55:12 +0800 Subject: [PATCH 6/7] refactor(website): move the sponsor band into a template partial The sponsor band markup lived inline in index.html, so another page could show it only by copying the markup. The rendered homepage is byte-identical. Co-Authored-By: Claude --- website/templates/_sponsor_band.html | 25 +++++++++++++++++++++++++ website/templates/index.html | 26 +------------------------- 2 files changed, 26 insertions(+), 25 deletions(-) create mode 100644 website/templates/_sponsor_band.html diff --git a/website/templates/_sponsor_band.html b/website/templates/_sponsor_band.html new file mode 100644 index 00000000..a47dabab --- /dev/null +++ b/website/templates/_sponsor_band.html @@ -0,0 +1,25 @@ +{% if sponsors %} + +{% endif %} diff --git a/website/templates/index.html b/website/templates/index.html index 4cb99d4c..4b024c3c 100644 --- a/website/templates/index.html +++ b/website/templates/index.html @@ -70,31 +70,7 @@ {% endblock %} {% block content %} -{% if sponsors %} - -{% endif %} +{% include "_sponsor_band.html" %}
From 786ebb4c782c6d94b632c37b60da4e1985e02a4d Mon Sep 17 00:00:00 2001 From: Vinta Chen Date: Wed, 21 Oct 2026 08:57:55 +0800 Subject: [PATCH 7/7] feat(website): show the sponsor band on category pages The sponsor band rendered only on the homepage, so visitors who entered on a category page and never opened the homepage saw no sponsors. Category, subcategory, group, and Stdlib pages now show the same band between the hero and the table. Co-Authored-By: Claude --- DESIGN.md | 2 +- website/build.py | 1 + website/templates/category.html | 2 ++ website/tests/test_build.py | 31 +++++++++++++++++++++++++++++++ 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/DESIGN.md b/DESIGN.md index 69b99542..8cad2e73 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -162,7 +162,7 @@ The component vocabulary is small and table-led. Source of truth: `website/stati - **Hero**. Magazine-cover headline, dark earthy ground, kicker and proof microcopy, primary CTA button using `--hero-btn-start` / `--hero-btn-end`. Subtle grid plus slow sheen. Respects `prefers-reduced-motion`. - **Jump links**. One per use case in a category hero, plus one for the guide. Underlined like the intro's links, with a trailing ↓. Never tag-styled: tags filter the table or open another page, jump links only scroll. - **Guide band**. Everything in a category intro after its "How to choose" list. It sits below the table on the hero's dark gradient, with its H2 above the text. -- **Sponsor band**. Sits below the hero on the home page, on `--bg-paper-strong`. Editorial layout, not a logo wall. Sponsor links share the global accent treatment. +- **Sponsor band**. Sits below the hero on the home page and category pages, on `--bg-paper-strong`. Editorial layout, not a logo wall. Sponsor links share the global accent treatment. - **CTA**. Warm `--cta-bg`, full-bleed within shell. The button itself uses accent tokens. - **Footer**. Dark warm charcoal, part of the same system. Footer links share the global hover and focus treatment. - **Search**. Pill input with `--search-inset` interior and `--search-focus-ring` focus ring. Focus shadow uses `--search-focus-shadow`. diff --git a/website/build.py b/website/build.py index 5d90459b..24b35b72 100755 --- a/website/build.py +++ b/website/build.py @@ -894,6 +894,7 @@ def build(repo_root: Path) -> None: parent_category=parent_category, group_categories=group_categories, category_json_ld=category_json_ld, + sponsors=sponsors, ), encoding="utf-8", ) diff --git a/website/templates/category.html b/website/templates/category.html index 4563dc4f..6b867269 100644 --- a/website/templates/category.html +++ b/website/templates/category.html @@ -208,6 +208,8 @@ {% endmacro %} +{% include "_sponsor_band.html" %} +
{% if entry_groups %} diff --git a/website/tests/test_build.py b/website/tests/test_build.py index 1ccefc6b..eed15fdc 100644 --- a/website/tests/test_build.py +++ b/website/tests/test_build.py @@ -490,6 +490,37 @@ class TestBuild: assert 'href="https://sponsor.example.com/?ref=x&utm_source=awesome-python&utm_content=website"' in index_html assert "utm_content=readme" not in index_html + def test_category_page_shows_sponsor_band(self, tmp_path): + readme = textwrap.dedent("""\ + # Awesome Python + + Intro. + + ## **Sponsors** + + - [Sponsor](https://sponsor.example.com) - Sponsored tool. + + ## Projects + + **Tools** + + ## Widgets + + - [w1](https://example.com) - A widget. + + # Contributing + + Help! + """) + (tmp_path / "README.md").write_text(readme, encoding="utf-8") + self._copy_real_templates(tmp_path) + + build(tmp_path) + + category_html = (tmp_path / "website" / "output" / "categories" / "widgets" / "index.html").read_text(encoding="utf-8") + assert 'href="https://sponsor.example.com"' in category_html + assert '' in category_html + def test_build_cleans_stale_output(self, tmp_path): readme = textwrap.dedent("""\ # T