Skip to main content

Promotions & Sponsors

Recurring monetisation beyond one-time premium listings — promotion placements, sponsor slots, bundling and pricing strategy.

Why this matters

One-time premium listings get you to first revenue, but they cap out fast — every project owner pays once and then stops giving you money. Promotions and sponsors are the two recurring-revenue layers that turn a directory into a sustainable business: project owners renew month after month for visibility, and unrelated brands pay to reach your audience without owning a listing.

Sustainable directory businesses split revenue roughly 30/40/30 across one-time premium, recurring promotions and recurring sponsorships. This page documents both recurring layers — when each works, how to price them, and the Stripe wiring that makes them run unattended.

The two systems

SystemWho buysBilling
PromotionsProject owners wanting extra visibilityMonthly subscription
SponsorsCompanies/brands wanting a permanent slotMonthly subscription

Both live in the admin panel. Pricing comes from advertising.config.ts.

Promotions

Three placement types, each independent:

PlacementWhere it appearsDefault price
BannerHorizontal ad below the site header, every page$69/mo
Catalog CardPromoted card mixed into the project grid$39/mo
Detail CardSidebar card on project detail pages$19/mo

Buyers at /promote can pick one, two, or all three. Picking all three triggers a 30% discount (via a Stripe coupon — see Advertising Config).

Promotion admin

Admins can:

  • Approve new purchases (if manual review is required)
  • Edit start / end dates
  • Revoke a placement
  • See which projects are currently running ads

Sponsors

Sponsors get logo placement in a sponsor section (sidebar, footer, or dedicated partners page — depending on your layout). Recurring monthly subscription.

maxSponsors: 8 in advertising.config.ts caps how many run simultaneously. Excess buyers go on a waitlist.

Stripe setup

Each placement and the sponsor plan needs a recurring monthly Price in Stripe:

.env.local
STRIPE_PRICE_ID_PROMO_BANNER=price_...
STRIPE_PRICE_ID_PROMO_CATALOG=price_...
STRIPE_PRICE_ID_PROMO_DETAIL=price_...
STRIPE_PRICE_ID_PARTNER_SUBSCRIPTION=price_...
STRIPE_COUPON_ID_PROMO_ALL_THREE=    # optional 30% off bundle

See Payments for full Stripe flow.

Feature flags

Turn the whole system off with:

config/features.config.ts
promotions: false,   // /promote page → 404, admin section gone
partners: false,     // sponsor slots → hidden

When to use which

  • Premium listings — the starter revenue stream. User pays once, gets upgrade for one listing.
  • Promotions — recurring revenue for projects that already paid premium and want more visibility.
  • Sponsors — unrelated-to-directory brands who want to reach your audience (e.g. an IDE sponsoring a "developer tools" directory).

A real-world walkthrough: pricing for a 5,000-pageviews/month directory

Suppose your directory does 5,000 monthly pageviews, evenly split across 200 listings. A "banner" promotion appears on every page → 5,000 impressions/month. At a CPM of $5–10 (typical for niche audiences), that's $25–50 of pure ad value per month — so $69/month is fair for a project owner who cares about visibility (they're getting their listing seen plus implicit category authority).

Catalog placements ($39/mo) appear ~40% of the time the user is browsing the catalog. Detail-page placements ($19/mo) appear only on co-located project pages. Stack them and the project owner gets blanket coverage; offer the "all three" bundle at 30% off ($89/mo) to make the upgrade obvious.

Sponsors are different: they pay for brand association, not pageview math. A boutique productivity-tool brand sponsoring a "tools for designers" directory cares about being seen in the right room, not raw impressions. Price sponsorships at $200–500/month for a small directory — you'll have 3–8 slots maximum, all sold with personal outreach. Don't open a public "buy a sponsorship" page until you have 50,000+ monthly pageviews.

Common pitfalls

  • Setting promotion prices based on cost instead of value. Stripe takes 2.9% + $0.30. A $19/month promotion nets you ~$18, but the ad is worth $40+ to the buyer. Don't underprice — promotion buyers care about ROI, not your margins.
  • Forgetting to invalidate the homepage cache when promotions change. If your homepage ISR cache doesn't include "active promotions" as a tag, a paid banner can take an hour to appear. Use revalidateTag('promotions') in the Stripe webhook handler.
  • Selling unlimited sponsor slots. Sponsors lose value when there are 20 of them on a page. Hard-cap with maxSponsors and run a waitlist; scarcity drives renewals.
  • Ignoring failed renewals. Stripe's customer.subscription.updated event fires when a card declines. The webhook handler should mark the placement inactive and email the buyer — otherwise their banner keeps running for free.
  • Mixing promotion and premium pricing in one Stripe Price. Each placement type needs its own Stripe Price. Reusing a Price across types makes upgrades and refunds messy. Set up explicit STRIPE_PRICE_ID_* env vars per type.

FAQ

Should I offer a yearly discount on promotions?

Yes — a "save 2 months by paying yearly" deal converts cash flow and reduces churn. Add a yearly Stripe Price alongside the monthly one and surface both at /promote. Most operators see ~20% of buyers choose yearly when offered.

Can I let project owners self-serve cancel their promotion?

Yes — link to the Stripe Customer Portal from your dashboard. Stripe handles cancellation, the webhook handler revokes the placement on customer.subscription.deleted. No custom code beyond a portal link.

How many active promotions can a directory run before it looks sponsored?

Empirically, more than 1 banner + 5 catalog placements + 10 detail-page placements starts to feel ad-heavy at small scale. The catalog ratio matters most: keep promoted-to-organic at 1:5 or better. Above 1:3, users notice and bounce.