Scrape Payhip & marketplaces β Analyze trends β Generate Arena.ai prompts to create better versions.
92% of Payhip/Gumroad sellers make <$100/mo because they guess products. PayhipScraper reverses this:
- Scrapes the public Payhip marketplace via its own search endpoint
- Analyzes demand, competition, pricing and quality β Opportunity Score 0-100
- Generates production Arena.ai prompts (Cloner, Differentiator, Bundle, Niche Report) that create superior versions in minutes
Payhip does not publish sales counts, so nothing here estimates them. Demand is derived from review volume, quality from star rating weighted by review count, and competition from the marketplace-wide result count for the keyword plus category saturation within the sample. Scoring is deterministic: scan the same keyword twice and you get the same scores.
Payhip's marketplace search matches any term in the phrase, so a search for "notion budget template" returns everything containing "budget". The scraper works around this: it probes each term, narrows on the most selective one, then filters locally for products matching the whole phrase. Each scan reports whether its results were exact or partial matches.
Reduce research from 10 hours to 7 minutes.
- Dashboard: search bar, stats cards, product grid with score circles, right rail prompts
- Drawer: detailed breakdown, tags, prompt actions
- Dark/light theme, responsive
git clone ...
cd payhipscraper
npm install
cp .env.example .env
# Edit .env: set JWT_SECRET >=32 chars
npm start
# Open http://localhost:3000Default login: create new account in UI (any email). No external email verification required for MVP.
- Backend: Node.js 20 + Express 4, SQLite (better-sqlite3, WAL), JWT httpOnly cookies, Zod validation, Helmet, RateLimit, Pino logs
- Scraper: axios + cheerio against
marketplace_aux/get_search_results_ajax(the endpoint the marketplace grid itself calls), UA rotation, 1.5s delay, DNS-level SSRF guard, cache 6h in-memory + SQLite - Analyzer: Opportunity Score = 0.40demand + 0.25competition + 0.20priceFit + 0.15quality β every term from an observed signal, no randomness
- Prompter: 4 template types, gap analysis, SEO suggestions, Arena deep-link
https://arena.ai/agent?prompt=... - Frontend: Vanilla ES Modules, no build step, CSS variables design system, Chart.js, hash router, PubSub store
Folder Tree: see ../docs/ARCHITECTURE.md
POST /auth/signup,/login,/me,/refresh,/logoutPOST /scans{keyword, platforms} β 202 scanIdGET /scanslist,GET /scans/:id,GET /scans/:id/status,GET /scans/:id/products,GET /scans/:id/promptsGET /products/trending,GET /products/:idGET /promptslibrary,POST /prompts,PUT,DELETE,GET /prompts/:id/arena-urlGET /user/profile,PUT,PUT /user/password,GET /user/stats,/api-keys,/export?format=csv|jsonGET /health,GET /platforms
See ../docs/API_SPEC.md for full spec.
- β Onboarding & JWT auth (bcrypt 12, access 15m, refresh 7d)
- β Primary dashboard: search, stats, filters, grid/table, charts, polling job progress
- β Scraping engine: Payhip live via its search API, relevance filtering, cached; Gumroad/Etsy are sample data only and always labelled as such
- β Trend analyzer: deterministic scores, keyword extraction, price/category stats
- β Prompt generator for Arena.ai with copy & open link, library, starring
- β Settings: profile, security, API keys, export, GDPR delete
- β Security: helmet, CORS, rateLimit, SSRF check, prepared statements, sanitize, auth middleware
See .env.example. Two things to know:
JWT_SECRETandJWT_REFRESH_SECRETare required (β₯32 chars, must differ). In production the app exits rather than start without them.ENABLE_MOCK_FALLBACKdefaults to false. When enabled, a failed scrape returns sample products instead of an error β useful for a demo, misleading anywhere else.
See DEPLOY.md. Short version: this is a stateful monolith (SQLite on disk + an in-process job queue), so it needs a long-lived container with a volume β Fly.io, Render, or any Docker host. It cannot run on serverless without swapping the database and the queue.
docker compose up -d --build # local / self-hosted
fly deploy # Fly.io, config in fly.tomlnpm testParser tests run against a captured copy of Payhip's real search markup
(test/fixtures/), so a layout change upstream fails loudly instead of
degrading silently to sample data.
- Sign up β Onboarding
- Search "notion budget template" β Scan β wait ~20-40s
- See products sorted by opportunity score
- Right rail prompts β Copy β Open in Arena.ai
- Check Trending tab, Prompt Library, Settings Export
- Only public metadata scraped, max 500 chars snippet, link back to original, attribution
- Respects robots.txt spirit: delay, UA rotate, cache
- Not affiliated with Payhip. Sellers can request delist (future endpoint).
- Disclaimer in footer
- v1.1: Stripe, Team workspaces, BullMQ+Redis queue, Puppeteer fallback, Chrome Extension, Webhooks
- v1.2: Affiliate auto-inject, Scheduled reports, API rate higher, Etsy/Gumroad real scraping upgrade
All enterprise docs in /docs:
PRD.mdβ personas, journeys, functional specsARCHITECTURE.mdβ tech stack, folder tree, data flowSCHEMA.mdβ SQL schema, modelsAPI_SPEC.mdβ endpoint specs, UI hierarchySECURITY.mdβ threat model, mitigations, qualityIMPLEMENTATION_PLAN.mdβ 3 phases
MIT β Build something profitable.
Built according to 2026 production standards: monolith first, modular, secure, fallback-safe, one-command deploy.