All projects

analytics · full-stack · llm · marketing

2026

BLOXHUB Analytics Dashboard

Nordic Hub for Sustainable Urbanization, Copenhagen — Marketing Analytics Intern

BLOXHUB Analytics Dashboard

BLOXHUB is a non-profit in Copenhagen, Denmark described as the Nordic hub for sustainable urbanization. BLOXHUB operates as a membership organization of architects, engineers, municipalities, corporations, and startups primarily in Denmark, but has members across Europe. It works as a super-connector for those in the sustainability and urban industry, often taking projects with cities to improve their urban loneliness, well-being, housing crisis, climate adaptation plans, or generally implement circular economy ideals. I spent the summer of 2026 with them as a marketing analytics intern. I worked primarily with the communications team, who publish on LinkedIn constantly but didn't have a reliable way of knowing what was working.

They used LinkedIn's native page admin dashboard for insights, but that only offers basic social media insights and metrics such as impressions, CTR, engagement rate, and demographics. There are no answers to helping you understand what specifically worked or why specific posts performed better than others.

The questions the team actually wanted answers to weren't how many impressions did we get? Rather: Does opening a post with a question-based hook perform better than an announcement-style post? Do event recaps do better than event invitations? Are we posting about the canonical company themes we say we care about, or are we skewing more towards specific ones? LinkedIn doesn't answer any of those questions. If the communications team wanted to answer those questions, they would have had to manually tag each post and then figure it out. So the dashboard I built classifies them with an LLM model and answers those questions.

Essentially, the dashboard ingests and classifies posts from LinkedIn and tags them. In the LinkedIn data export, all the posts during a specified time period are included, where the rows are each individual post, and the columns are information on the posts, including the text from that post. From here, the dashboard essentially ingests and classifies these posts across six dimensions: its hook style, CTA and links, tone, structure and length, content type, and canonical company theme. The findings that are the answers to those questions are created when the classified post text is aggregated, like "Do posts with data-driven hooks have higher engagement than question-based hooks?" or "Does our audience have higher engagement with short-form content or long-form content?" or "What specifically are we posting the most about?"

The dashboard's import screen after an upload: a drag-and-drop area, the four LinkedIn exports each tagged by type, a parse summary counting competitor pages, daily metrics, demographic breakdowns and posts classified, and reference cards naming the sheets inside each export

I. The import screen after four LinkedIn exports, with sixty-three posts classified

Part I. How the numbers are computed

When designing the dashboard, I was really wary of how LLMs hallucinate, so I leveraged deterministic code where one rule sits above everything else in the codebase. Deterministic code computes all facts, numbers, selections, and rankings. The model only classifies posts and narrates values it was handed. For context, deterministic code essentially always produces the same output and follows the same steps every single time it runs, as long as it is given the same input. Overall, the deterministic code and the LLM work together to create data-backed recommendations and insights that prevent hallucination while leveraging the power of the LLM's classification abilities.

Here are some examples where the deterministic code (or guardrails) can be enforced. The recommendation cards show which statistic should be used and the system, via the deterministic code, fills in the figure, so an explanation of a chart and the chart itself do not disagree with each other. Posts that the model nominates as specific examples of having a trait when creating recommendations are validated against previously stored classifications and are backfilled deterministically, so a recommendation about "question hooks" cannot illustrate itself as a post classified with something else, essentially throwing off the recommendation. A hook labeled "Data-Driven" is re-classified unless a digit or a percentage actually appears in the post's opening, taking into account case-insensitivity (ex. "Eight" versus "eight") and free variation (ex. "8" versus "eight"). A post will not be classified as call-to-action if it cannot provide evidence of the direct quote in the post text. Whether a post has a link attached isn't determined by an LLM model; instead, it is derived from parser columns. And a separate guard blocks any suggestion from the LLM about images, videos, or carousels, because the system and LinkedIn export only ever sees post text and don't know what media a post originally had. That's not included in the export.

A high-priority recommendation card: the written advice, a why-this-works note, a supporting-data block carrying the pre-event, invitation-hook and baseline engagement rates, and the three example posts the finding rests on

II. One recommendation card, with its supporting figures and the posts behind it

The page and content performance section: a date range filter, four summary tiles for impressions, engagement rate, new followers and page views, and a daily content volume chart with impressions selected

III. Page and content performance: four summary tiles and daily impressions

One part of building this dashboard I was surprised by was how much of LinkedIn's data looked initially like a bug. For example, the daily values are attributed to the day the activity occurred, so a spike can come from a post published months earlier. A day can also report negative engagement when there are deleted comments or undone reposts. Some location strings have the country repeated, which is why a row can read as "London Area, United Kingdom, United Kingdom".

The same chart with three metrics overlaid and normalizing switched on, rescaling each to a 0 to 100 index, sitting above the notes on activity-date attribution and negative daily values

IV. The same chart with three metrics overlaid and rescaled to a 0 to 100 index

Daily new follower growth split organic versus paid, daily page visitors, and a followers-by-location breakdown with a donut chart led by Copenhagen

V. New follower growth, daily page visitors, and followers by location

Part II. The six classification layers

There are six layers per post: content category, theme, tone, hook style, structure and length, and calls-to-action and links. These layers include predefined lists, meaning a post cannot be classified as both "data-driven" and "data driven." Every label the model returns is forced onto the fixed enum (type of data using predefined values) by its nearest match before it is stored; the idea is that classifications stick to the closest legitimate value instead of creating hundreds of new categories that are all similar to each other.

The content distribution matrix: the five canonical themes crossed with six content categories, each cell carrying a post count and a share of that theme, shaded by volume

VI. Post counts for each canonical theme crossed with each content category

Not everything maps into a company theme, which is the reasoning for this "Other" section. For recurring programs that are posted about, such as UNLEASH, Masterclass, or the AEC Hackathon, they are matched on keywords in the post text and moved into a separate heatmap matrix. These are programs, not topics, which is why they are separated from the first canonical heatmap.

The Other matrix: posts that map to no canonical theme, grouped into community, other posts, job opportunities, newsletter, masterclass, UNLEASH and AEC Hackathon rows

VII. The Other matrix: posts and recurring programs that fit no canonical theme

Layer 1, content category: a bar chart ranking post types by average engagement rate, with a note naming the most-used category and the collapsed classification reference beneath

VIII. Content category, ranked by average engagement rate

Layer 3, tone: a bar chart ranking visionary, conversational, promotional and academic by average engagement rate, with a written read on why conversational led

IX. Tone, ranked by average engagement rate

Layer 4, hook style: a bar chart ranking invitation, data point, insight, question and story openings by average engagement rate, with a written read on why invitation led

X. Hook style, ranked by average engagement rate

The winning formula panel: content category and hook style pairings ranked against the organic baseline, each naming the number of posts it rests on, above three named combinations

XI. Content category and hook style pairings, ranked against the organic baseline

Month over month: a line naming the partial months excluded from the comparison, four comparison tiles against the previous month, and a multi-month trend chart

XII. Month over month comparison tiles and the multi-month trend chart

Two heatmaps of post counts per calendar month: the five canonical themes above, and the Other groupings below

XIII. Posts per calendar month: canonical themes above, Other groupings below

Part III. How accurate the classifier was

I tested the accuracy of my dashboard's classification by utilizing a validation dataset. By the end of my internship, tone classification was roughly 80 percent accurate and hook style about 92 percent, with "Question" as the most over-assigned label of the set. Content category and links were nearly perfect, which makes sense because one is structural (content category) and the other is derived from parser columns rather than an LLM's judgment (links).

One issue I initially ran into was classifying too many posts at once. In the current iteration, classification runs in batches of eight posts, reduced from initially 15. At 15, the per-post output grew long enough that the response was occasionally truncated (cut) in the middle of the analysis, which silently returned fewer classifications than there were posts. At eight, all posts were classified properly while taking into account the resources it takes for classification (time + API cost).

Part IV. The rest of the internship

AI literacy. As part of my internship, I educated the team on how to best leverage AI as a tool. I taught the team how different LLM models work, how many of these AI tools have tools within them, such as deep research capabilities or web search capabilities, and generally explained some tips and tricks when prompting AI. For example, to be detailed with your prompt and to specify as much as you can, whether it's:

  • what you've uploaded as context
  • what your goal is
  • what you would like the output to be
  • the style that you would like
Presenting the AI literacy session to the BLOXHUB team, with the prompt-example slide on screen comparing a one-line request against a fully specified one

XIV. The AI literacy session, on the slide comparing two versions of one prompt

Competitor benchmarking. I looked at how other innovation hubs operate, such as Station F in Paris, MaRS in Toronto, Tech Barcelona, Here East and Plus X in the UK, Marineholmen in Bergen, and the New York campuses at Brooklyn Navy Yard and Industry City. I created four recommendations: a predictable recurring events calendar, a Nordic Urban Innovators recognition campaign, an annual public impact report with quantified outcomes, and curated member discounts on legal, IP and software services, which raises the value of BLOXHUB's membership without impacting the price or the building itself.

A thought leaders list. I created a list of individuals and organizations worth tracking and engaging, split into three lists because outreach tactics differ with various audiences: individuals for general audience growth, communities/organization pages for the same, and individuals for specific lead generation.

Looking out over the canal at Christians Brygge in central Copenhagen from the BLOXHUB terrace

XV. The view over the canal from the BLOXHUB terrace

A note on what is shown here. The screenshots are the live internal dashboard, running on sample data of a real LinkedIn Export. The source repository is private to BLOXHUB and the deployed app renders empty without an upload, so neither is linked. Nothing here includes individual team members' performance, although there is a portion of the dashboard dedicated to this. Photographs by JoLyn King for CIEE.