AIclicks: Product Engineer, AI Visibility Analytics

Owned features end to end across a Next.js dashboard, a FastAPI backend on Postgres and two Python workers: prompt tagging and bulk workflows, a multi-stage LLM pipeline for prompt discovery, the mention and citation pipeline, a recommendations service, and the first version of a public Data Explorer.

Product engineer (contract) · Apr 2026 to Nov 2026 · Remote · AIclicksGEO analytics SaaS

Engagement overview

AIclicks tracks how AI assistants mention and cite brands. I joined as a contract product engineer and took customer problems from ticket to merged PR across the whole stack: schema and migrations, FastAPI routes, Postgres rollups and indexes, background workers, and the React dashboard. Work went out as small PRs behind feature flags, merged to a dev branch and released to main.

Measured from git log across the five repositories I committed to, Apr to Oct 2026: about 1,900 commits, about 500 PR merges, roughly 225,000 lines added and 45,000 removed, excluding lockfiles and generated files. Commit counts include merge commits.

Product surfaces

Prompt tags and bulk workflows

B2B SaaS
Dashboard features for paying customers; no public demo.

Problem

Customers tracked hundreds of prompts per brand with no way to group, filter or act on them in bulk, and analytics views lost their state on refresh.

My role

Schema, API, and dashboard UI for every feature in this chapter.

Built the tag system for prompts and topics: schema, API, tag chips in the prompts table, a dashboard tag filter, and bulk tag assignment at topic level. Added select-all and bulk actions on the prompts table with server-side selection semantics, so a customer can tag, pause or delete a whole filtered set at once.

Made the analytics views stateful: filters, tabs and sort live in the URL, prompt analytics sorting and a dynamic timeline follow one shared date range, and a project picker lets users who belong to several brands switch without losing context. A domain-view rollup aggregates brand pages across tracked domains.

Bulk topic delete went from one request per topic to one request for the whole set. On a 5,000-prompt fixture with a real Redis: 2,399 ms to 316 ms, 78 cache scans to 3, 26 webhooks to 1, 78 commits to 1.

  • Bulk delete of 5,000 prompts: 2,399 ms to 316 ms and 78 commits to 1 (fixture, real Redis).
  • Filters persist in the URL, so a view can be shared as a link.
  • Prompt analytics sort and timeline follow one shared range state.
Skills

Next.jsReactTypeScriptRadix UITailwindrechartsURL stateFastAPI (Python)PostgresRow-level securityJest

Prompt discovery: a multi-stage LLM pipeline with topic clustering

LLM pipeline
Backend feature behind the prompt-discovery route; no public page.

Problem

Customers needed new prompts to track, but single-shot generation repeated themes the brand already covered and drifted away from what the brand wanted to rank for.

My role

Designed and built the pipeline, its tests and the route; the team made later edits.

Built the generate-cluster-prompts pipeline in the FastAPI backend as eight stages. It embeds the brand's existing prompts, reduces them with PCA and clusters them with KMeans, picking k by a silhouette sweep from 5 to 40. Pools under 50 prompts skip clustering, and a clustering failure falls back to one bucket. A small model then labels each cluster as a topic.

A planning call to a larger model reads the labelled topics and returns a brief: saturated themes to avoid, forbidden phrases, and an instruction for the writer. A generation call writes 8 to 12 candidate prompts under geo-targeting rules. Candidates pass a cosine-similarity filter against the existing pool with two bands (0.70 and 0.92); the gray zone goes to an LLM judge, survivors are re-ranked by alignment minus avoidance similarity, and the top 10 are returned, with optional scoring.

In the recommendations service I extended the content-ideas path with the same idea at a different layer: a breadth-first search over co-cited URLs forms clusters, and each of the top 10 clusters gets one model call with its top 25 URLs to name the topic.

  • Topic clustering replaced single-shot generation, so new prompts avoid saturated themes.
  • Two cosine bands plus an LLM judge keep near-duplicates out without rejecting useful variants.
  • Pipeline and cluster-stats behaviour covered by 10 tests I wrote; the content-ideas topic step has 17.
Skills

Python 3.12OpenAI modelsEmbeddingsscikit-learn (PCA, KMeans, silhouette)Prompt designFastAPI (Python)pytest

Data Explorer, first version and groundwork

Public data product
Shipped behind a feature flag as a first version; the rollup infrastructure needed a redesign before it could run across the full dataset.

Problem

AIclicks wanted a public explorer over its citation data: which sources AI assistants cite, per brand and per day. The raw tables were too large to query live.

My role

Rollup design, the nightly worker, the API routes and the explorer views.

Designed the rollup tables and wrote the nightly explorer-preprocess worker as sole author: day planning, roll-day jobs, a claim guard with takeover, retry and heal paths, roll versioning and a late scan for stragglers. A gate serves a rollup only once it is complete for the day. The worker runs on a VM under Docker Compose.

On the API side I added the explorer metric cards, a citations API, a lean cited-sources endpoint, an influence route and an untracked-mentions route, backed by a GIN index on citations and a function-based query path. Compressing the cited-sources endpoint with brotli took the payload from 18.7 MB to 2.0 MB, a 9.34x cut measured in the real app stack.

This shipped as a first version. The rollup infrastructure needed a redesign before it could run across the full dataset, and that work was not finished when the engagement ended.

  • Cited-sources payload 18.7 MB to 2.0 MB (9.34x) with brotli q4; 57 ms CPU against 238 ms for gzip.
  • Reach queries after the citations GIN index: 106.8 s to 1.9 s on the heaviest site class, 153.9 s to 30.9 s over 14 queries with identical results (prod, one run each). Index 1.92 GiB on a 55 GiB table.
  • Brand tab query 4.1 to 5.9 s down to under 0.1 s with a daily-brands rollup (prod mirror).
  • Nightly worker: 90 of 90 days rolled, 0 errors, 163 to 190 s per day (prod, Oct 2026).
Skills

Postgres rollupsGIN indexesSQL functionsPython workersDocker ComposeFastAPI (Python)brotliNext.jsFeature flags

Mention pipeline and recommendations

Data pipeline
Internal pipeline and operator tooling; no public page.

Problem

Mention detection missed subdomains and social hosts, credits were charged inconsistently, onboarding snapshots raced the first analysis, and the recommendations service had no operator view.

My role

Worker and backend fixes, and sole author of the Get Mentioned path in the recommendations service.

In the pipeline worker I added citation host matching and a test suite for the mention matcher before touching classification, fixed the stage-5 social host classifier, credited a brand's subdomains to the brand, and reworked mention credit accounting. On the backend I fixed the onboarding snapshot race so a new brand's first snapshot waits for its first analysis.

In the recommendations service I built the Get Mentioned path: citations enrichment with an on-demand variant, a ScrapingBee rate limiter, title-group jobs, scheduled mention fetches, Engage Online v2, the first phase of outreach feedback, and a reco-ops dashboard so operators can see job state without reading logs.

  • Matcher review removed 1,814 wrong detections and kept all 5,674 correct ones (16,502 competitor rows against 38,804 page titles).
  • Subdomain credit fix: 13.4% of tracked brands had uncredited subdomain mentions before it (prod read, checksum verified).
  • Stage-5 brand-name fix recovered 15.8% more mentions for one customer, 174 snapshots rewritten and verified three ways.
  • Operators see job state in a dashboard instead of logs.
Skills

Python 3.12asyncioPostgresScrapingBeeLLM pipelinesFastAPI (Python)pytestOperator dashboards