Skip to content
Back to home
In production2026

Qué Pinta Salta

Five sources, one place: Salta's cultural calendar fed automatically

The problem

Salta's event listings live fragmented across four ticketing sites that don't talk to each other and, above all, across Instagram flyers — where the information exists as an image, not as data.

For the user: there's no single place to see what's on tonight. For the organizer: their event shows up scattered, or doesn't show up at all.

Loading everything by hand doesn't scale. That was the real problem.

The decision that defines the system

The model extracts.model lane
The code decides.deterministic lane

Gemini 2.5 Flash reads the flyer and the caption and returns structured JSON. But it publishes nothing. The gate is a deterministic TypeScript filter that validates four fields by shape —title, date, time, venue— and decides whether the event goes live or falls to human review.

This is deliberate. An LLM that is confidently wrong is more dangerous than one that fails outright. Putting the decision in code makes the criterion auditable, versionable and explainable — and when extraction fails, the flyer isn't lost: it enters the review queue with the original caption as context.

How it works

Structured sourcesNorteTicket · VamosgobEntradaUno · AlPogoInstagramflyer + captionNormalizationTypeScript codeGemini 2.5 Flashextracts JSONGatedeterministic filtervalidates 4 fieldsPublishedJaccard dedupReviewhuman queueStructured sourcesNorteTicket · VamosgobEntradaUno · AlPogoNormalizationTypeScript codeInstagramflyer + captionGemini 2.5 Flashextracts JSONGatedeterministic filtervalidates 4 fieldsPublishedJaccard dedupReviewhuman queue
The accent color marks the only lane where the model intervenes.

Open any screenshot to view it full size

Qué Pinta Salta home page: the featured event is a Salta Symphony Orchestra concert with date, venue and price, labelled "imported automatically from entradauno" — the pipeline filled the whole record with no manual step.
Category grid with a per-section event count and a "Show social flyers" switch — the Instagram lane seen from the user's side: events extracted from flyers sit alongside those from the ticketing sites.
Mi Radar Salteño: email alert setup, with delivery frequency and destination inbox.
Mi Radar Salteño: picking categories of interest and followed Instagram organizers, which decide what goes into each digest.

Five active sources. Four return structured data and are normalized with code. Only Instagram goes through the LLM, because it's the only one where the data lives as an image.

Events that appear in several sources aren't duplicated: a Jaccard similarity algorithm scoped to same venue and same day merges them, keeping every purchase link and the lowest price.

J(A,B)=|A ∩ B||A ∪ B|

Set similarity between tokenized titles, scoped to same venue and same day.

StackNext.js 16 · TypeScript · Supabase (RLS) · Gemini 2.5 Flash · Apify · Tailwind 4 · Framer Motion · Resend · Vercel