Inspiration

South African football clubs have some of the most loyal fan bases on the continent, and a recurring problem. When ownership or management changes, results on the pitch can be fine while fans still feel the club no longer speaks their language. Marketing teams know their fans' ages and areas, but not what those fans love outside football, so campaigns fall back on the same artists, the same activations and the same tone. The question I kept hearing from clubs and sponsors was simple: is there a quantitative way to read a fan base and turn it into a campaign? Qloo's taste data answers the first half. FanDNA is the answer to the second, and it is built to be honest about where the data ends and the guesswork begins.

Built for rate limits

Qloo results are cached in SQLite for seven days, and briefs and agent answers are cached by their inputs. On a Groq rate limit, FanDNA tries a smaller model once and never retries in a loop. A demo seed lets a fresh deployment serve the reviewed demo without any API call. There are 49 offline tests plus a live check script.

What it does

FanDNA turns Qloo's taste data for a club's supporters into campaign concepts a marketing team can act on, and researches follow-up questions in Qloo itself.

  1. Shows what Qloo knows about the fans. For men aged 18 to 35 in Johannesburg, Qloo's top brands include Topman, BMW and Tag Heuer. Add Kaizer Chiefs to that audience and they become Hunter's Cider, Power Play, TelkomSA, Cell C and Bathu, with places like Jojo Rooftop Lounge and Orlando Amstel Arena and taste tags like Local Heritage and Cultural Pride. FanDNA shows both side by side and marks what appears only for the club's fans.
  2. Writes three campaign concepts and recommends one. Each has an activation plan, channels, a budget in rand, KPIs, and a checklist of what to confirm before spending.
  3. Keeps the evidence and the interpretation apart. Under each signal, a line written by code states what Qloo returned ("#9 among Kaizer Chiefs fans' brands; not in the whole audience's top 10"). The language model only writes what it suggests.
  4. Shows what Qloo adds. The same request goes to the same model without Qloo's data, and its claims are checked against Qloo's results.
  5. Ask FanDNA, a research agent. Type or speak a follow-up, such as "How do Chiefs fans differ from Pirates fans?" or "Who are Bathu's fans?" The agent decides which Qloo tools to call, shows each lookup, and checks every name in its answer against what Qloo returned.
  6. Works inside other agents. An MCP server exposes FanDNA's Qloo tools and the full campaign brief to Claude Desktop, Cursor or any MCP client.

How we built it

FanDNA is a Python app on Streamlit, using the Qloo Insights API, Groq for the language model and speech-to-text, and the MCP Python SDK.

Qloo is the evidence layer. The brief makes nine fixed, cached calls: /search to resolve the club, then /v2/insights four times for the audience alone and four times with the club added (signal.interests.entities), across artists, brands, places and taste tags. Items that appear only with the club are the club-specific signals. The agent adds entity_fans, which reads Qloo's demographics insight (filter.type=urn:demographics), and compare_clubs.

The agent is bounded. Ask FanDNA uses Groq tool calling with at most three Qloo tool calls per question. Tool outputs carry names and ranks only, so a question costs roughly 1,300 to 6,000 tokens. Every Qloo item in an answer must be bolded, and each bold name is checked in code against the tool results.

Checks happen in code, not in the prompt:

  • A cited signal that isn't in Qloo's results is shown as unproven.
  • A model reading that claims behaviour Qloo can't show ("the audience frequents this venue") is replaced and marked as rewritten.
  • Budget amounts are computed in code, with 10% held back as contingency.
  • Numbers the model writes in KPI baselines are flagged as unverified.
  • For every cited brand or place, a partner or availability check is added if the model missed it. Concepts involving alcohol get an age-gating check.
  • Other sports are set aside before generation, and a club is used only when Qloo's match really is that club.

Challenges we ran into

  • Rate limits: The first version ran a general agent loop that resent its tools and history on every step. Each request reached about 6,000 tokens against an 8,000-tokens-per-minute limit, so it failed constantly. We rebuilt around fixed, cached Qloo calls for the brief and a tightly bounded agent for follow-ups. A brief now costs about 3,500 tokens, and our test question to the agent cost 1,299.
  • Keeping the model honest: Prompt rules alone didn't hold. The model wrote that fans "frequent" a venue, invented baselines such as "average attendance of 20,000", and added its own contingency line on top of ours. Each of these is now caught and corrected in code, and the rewrites are shown rather than hidden.
  • Qloo's coverage of southern Africa: Testing locations outside Johannesburg was revealing:
    • "Durban" resolved to Durban-Corbières, a village in southern France.
    • Cape Town returned venues in Phuket and Amsterdam, and Pretoria returned nothing.
    • "Mbabane Swallows", an Eswatini club, matched "Swallow's Nest", a place.
    • Mamelodi Sundowns resolved to a place entity, which Qloo silently ignored as a signal.
    • FanDNA now accepts a club only when every word of the query is in the match, prefers brand entities, offers only the verified city, and says plainly when it falls back to the audience alone.

Accomplishments that we're proud of

  • Every claim is traceable. A judge can see which Qloo result supports each concept, which parts are interpretation, and what still needs validating.
  • A non-obvious lead. David Tlale, a South African designer, appears only for Kaizer Chiefs fans (#9 among their brands). A generic model wouldn't suggest him.
  • An agent that stays inside its limits: one well-chosen tool call, 5.5 seconds, every cited name verified.
  • A product, not a transcript. The design uses the South African flag's colours as meaning: green for Qloo-backed evidence, gold for what's specific to the club, red for anything unproven, blue for the generic-AI answer.
  • Usable by other agents through MCP, with the same checks applied.

What we learned

  • Fan bases differ less from each other than from everyone else. Kaizer Chiefs and Orlando Pirates fans share 8 of their top 10 brands, but both differ sharply from young men in the city overall. The useful contrast is club fan versus general audience.
  • Data beats assumptions. Qloo's demographics show Bathu's audience over-indexing on ages 35 to 44 (+0.51) and men (+0.31), and under-indexing on people 24 and younger (−0.17). That's not what most people would guess for a streetwear sneaker brand.
  • Affinity isn't behaviour. Qloo shows what an audience ranks highly, not where people go or what they buy. Keeping that line visible is what makes the output trustworthy.
  • Rules belong in code. Every rule the model broke more than once is now enforced after generation

What's next for FanDNA

  • More cities, added one by one as Qloo's location data checks out for them.
  • More sports: the Springboks and rugby audiences first, then cricket.
  • Clubs' own numbers: let a club enter its baselines and supplier quotes, so KPIs and budgets move from illustrative to real.
  • A first paying customer: a paid campaign brief for a PSL club or sponsor, delivered through QuantaIntelligence, our data consultancy in Eswatini.

Built With

  • gpt-oss
  • groq
  • model-context-protocol
  • python
  • qloo
  • streamlit
  • whisper
Share this project:

Updates

Submission history