Bad support isn’t a complaint theme. It’s an amplifier: “customer care” was the main complaint only 40% of the times it came up.
13,000 reviews over 30 days. 4,126 with real content. 1,633 inside a theme. 182 labelled by hand.
In three lines
- I pulled 13,000 public Play Store reviews of Swiggy from 30 days and clustered them into 15 complaint themes, ranked by how many reviews each contains.
- “Customer care” came up almost as often as late delivery, but it was the main complaint only 40% of the time. It rides on top of other failures.
- Scored against 182 reviews I labelled by hand, the clustering agreed with me 21% of the time. It tells you where to look, not what to conclude.
Context
This one grew out of my voice mode research. While interviewing people, I found I was more interested in the frustrations they described than in voice itself. It made me think about a wider problem: product teams get thousands of reviews, complaints and support tickets. How do they actually make sense of them?
Reading everything isn't practical, and counting complaints doesn't always show the problem underneath. I wanted to see how far AI could help a PM get from "what are people complaining about" to "what are they actually struggling with".
So I asked a simple question, what do 13,000 reviews actually say, and picked Swiggy's main app on the Play Store and collected every review from 16 August to 15 September 2026. That came to 13,000 reviews, about 430 a day.
Problem
Nobody can read 13,000 reviews, and a summary you can't check is not much better than not reading them. Two things made this corpus harder than it looks.
Most reviews say nothing. 68% were four words or shorter: "good", "worst", a thumbs-up. Only 4,126 had real content.
- Collected13,000
- With real content4,126
- Inside a named theme1,633
The ratings are U-shaped. 84% of reviews are either 1-star or 5-star, and of the 7,162 five-star reviews almost none are longer than four words. Happy users say nothing, so the usable complaints are essentially the 1-star and 2-star pile.
- 5 stars7,162
- 4 stars1,136
- 3 stars570
- 2 stars314
- 1 star3,818
What I did
- 1Scrape13,000 reviews, 30 days
- 2Clean4,126 with real content
- 3ClusterEmbeddings, then HDBSCAN
- 4Name by hand15 complaint themes
- 5ScoreAgainst 182 hand labels
- 6DashboardEach theme opens its reviews
- Scraped 30 days of reviews into a local database and never modified the raw rows, so every quote resolves to an unedited source.
- Cleaned them: dropped reviews under five words, removed duplicates, normalised stretched words.
- Turned each review into a multilingual sentence embedding, reduced it with UMAP and clustered with HDBSCAN.
- Read the clusters and named them by hand. Praise clusters and an unsorted mix of regional languages were left out. That left 15 complaint themes.
- Sampled 200 reviews and labelled 182 of them myself, to have an answer key to score the clustering against.
- Built a searchable dashboard where each theme opens to the verbatim reviews behind it, ordered by how many other users marked them helpful.

Key decisions and tradeoffs
Thirty days instead of six months. My spec said six months and 5,000 reviews. A 200-review test pull showed about 430 reviews a day, which makes those two numbers disagree by a factor of twenty. I cut the window to 30 days and gave up seeing seasonal change in exchange for a corpus I could iterate on.
Minimum theme size 40 instead of 15. I set 15 before I had data. On 4,126 reviews it produced 63 fragments, with one theme scattered across eleven of them. Raising it to 40 gave readable themes. The cost: a real complaint about inflated delivery distances, 27 reviews, sits below the line and never surfaces.
Rank by plain count. I considered weighting themes by severity and by stated intent to leave. I shipped frequency only, because a ranking I can explain in one sentence beats one I can't defend.
Counts, not adjectives. Every claim in the tool is a number with the reviews behind it. Play Store has no public link for a single review, so a citation here means the verbatim text, rating and date.
What I found
- Delivery runs far past the ETA, and cancelling costs money486 reviews
- Support gives scripted non-answers, nobody takes accountability242
- Fees stack up: platform, packaging, surge, delivery181
- Refunds denied, partial or delayed after a failed order144
- Users explicitly switching to Zomato78
Support failure is an amplifier, not a theme. In my hand-labelled sample, "customer care" was mentioned almost as often as delivery delay. But it was the primary complaint only 40% of the time it appeared. The order goes wrong first; the support experience is what turns an annoyance into a one-star review. That is also why it resisted clustering: it is spread across most other complaints. Counting complaints alone would have pointed at support as the problem. Reading them showed it was more often a symptom of something that had already gone wrong.
- Delay or late367 reviews
- Cancellation275
- Fees or charges275
- Support or chatbot273
- Refund225
Volume and agreement run in opposite directions. The biggest theme's top review has 21 "helpful" votes. The most-upvoted review inside any theme has 150, and it sits in the smallest one: hidden fees and paid benefits not being honoured. The most-upvoted review in the whole corpus, 2,590 votes, about insects in the food and support brushing it off, was never clustered at all. People write about late delivery; they endorse complaints about being overcharged. Ranking by count buries the second kind.
- Top review in the largest theme, late delivery21 helpful votes
- Top review in any theme, hidden fees150
- Top review in the whole corpus, never clustered2,590
The agreement score is 21%. When I said two reviews described the same complaint, the clustering agreed 21% of the time. Reading the output confirms it: roughly a quarter of the reviews in any theme belong somewhere else. This narrows 13,000 reviews to a dozen places worth looking. It does not classify reliably enough to act on without reading the evidence.
Two limits on what any of this means. Reviewers are a self-selected, mostly angry minority, so this measures public complaints, not user experience. And 486 reviews about late delivery says nothing about how many orders were late.
What I'd do differently
- Set the product rule and the model parameter together. I said "show anything above 15 reviews" and separately set the clustering minimum to 40. They disagreed, and everything between 15 and 40 became invisible.
- Let a review hold more than one complaint. About 18% of reviews describe two or more problems, but each is filed under one theme. A keyword count across themes showed chatbot complaints at more than double their theme size.
- Show endorsement beside volume. Helpful votes tell a different story from counts, and the tool ranks by counts alone.
- Handle language before clustering. Romanised Hinglish landed correctly inside themes, but delivery complaints in Hindi and in English formed separate clusters.
Version 2 turned the one-off snapshot into a daily job. It fetches new reviews each evening, places each one in the nearest existing theme or holds it in an unassigned bucket, re-ranks the themes, and adds a local app for flagging wrong placements. In its first two weeks, 26 one-star and two-star reviews were placed in praise clusters, 1.8% of everything assigned. I documented that as a known limitation instead of adding more thresholds.
These frames are from my one-minute walkthrough of version 2, recorded on 20 October 2026, about five weeks into daily runs. By then it had collected 27,410 reviews and the themes had been merged down to 11.




How it was built
Python, google-play-scraper, SQLite, sentence-transformers (a multilingual MiniLM model), UMAP, HDBSCAN and pandas. Version 2 adds Flask and a scheduled daily run.
What was mine: the question, the PRD, the scope, the hand-labelled set, the theme names and merges, and every threshold and tradeoff above.
What AI did: I used Claude as a pair-programmer throughout, for scaffolding the scripts and diagnosing clustering failures, and Claude Code for the version 2 code. It laid out options; I made the calls.
Links
- VoC Copilot V1 on GitHub: the snapshot pipeline, dashboard and hand-labelled set
- VoC Copilot V2 on GitHub: the daily pipeline and local app