← Projects

Case study · 2026

bilete.online: 7,954 places to see in Romania, on a map, with a ticket on your phone.

A marketplace for attractions, museums, parks and experiences from all over the country. You pick the day, pay online and get in with the QR code on your phone. Besides tickets, the site helps you choose: a map, routes and a trip planner.

Role
Founder. Product, design, code
Period
May - October 2026
Status
Live on bilete.online
Built on
ePAS, the platform behind Tixello
The problem
Tickets to museums and attractions are sold in many places, and information about what is worth seeing is scattered.
What I built
A marketplace on ePAS, with 7,954 attractions on a map, 37 routes, a trip planner and a panel for operators.
The result
Live on bilete.online. The catalogue is complete, but tickets are for now sold only for a few experiences added by operators.
bilete.online
bilete.online home page: experiences start before you get in
  • 7,954 Romanian attractions in the catalogue
  • 341 cities
  • 37 themed routes
  • 321 commits on the site, in five months

Counted in the repo and on the site, on 5 November 2026. The attractions form an informational catalogue. Tickets are sold only for experiences added by operators.

01 · The problem

You know you want to get out of town. You do not know where yet.

Tickets to museums and attractions in Romania are sold in many different places, often only at the ticket desk. Information about what is worth seeing is scattered too: town hall websites, blogs, old lists.

I wanted one place where a family starts from a simple question, “what do we do this weekend?”, and ends up with a plan, ticket included where one can be bought.

02 · What it does

From the map to a plan, then to a ticket.

Each attraction has its own page, with a description, photos and its place on the map. The interactive map shows all 7,954 points and filters them by type, region and county. Themed routes link attractions along a real road, with distances and travel times.

The planner starts from your city, the number of days and the people you travel with, then suggests a day-by-day itinerary. It is built on rules, not on a language model, and says openly that visit durations are estimated.

Map of attractions in Romania on bilete.online, with clusters of points for each area
The map: all attractions, loaded from a single static file, without one request for each point.
The Transylvanian castles route on the map, with the stops in order
A route: the road of Transylvanian castles, with stops, distances and travel time.
The Peleș Castle attraction page, with photo, description and map
An attraction page: what it is, where it is, what else is nearby.
The bilete.online trip planner: let’s go somewhere
The planner: where you leave from, how many days, who with. The plan stays in the address, so you can send it.

03 · The architecture

A fast site in front, a ticketing platform behind.

bilete.online has no database of its own. It is a separate PHP site that talks to ePAS, my ticketing platform (the same one under Tixello and AmBilet), through an API with its own key. In ePAS, each marketplace is a client with its own domain, currency and modules.

  1. 01

    ePAS (Laravel)

    • Attractions, experiences, venues
    • Orders, QR tickets, payments
    • Filament panel shared by all sites
  2. 02

    Marketplace API

    • Separate key for each site
    • Modules switched on only for their client
    • Limit of 120 requests per minute
  3. 03

    Two-level cache

    • API responses, served even after they expire
    • Whole pages, generated ahead
    • Parallel requests with curl_multi
  4. 04

    The visitor

    • Fast HTML pages, behind Cloudflare
    • Map and planner from static files
    • Payment sent back to ePAS

New modules, like activities with time slots, are switched on only for bilete.online. AmBilet, which runs on the same platform, does not see them.

04 · Components

What I built, piece by piece.

01

The visitor site

55 page types, all redesigned in September 2026.

  • City, category, attraction, experience, venue
  • Map by type and by region, 37 routes
  • Deterministic trip planner
  • Cart, payment, gift card, voucher, order recovery
  • Customer account: family, 2FA, data export
02

The operator panel

41 pages for the museums and companies that sell their experiences here.

  • Sales, attendees, analytics
  • Team, invoices, payouts to the operator
  • Point of sale (POS) for selling at the entrance
  • Booking widget for their own site
  • API documentation
03

The activities module in ePAS

14 new services in the platform, now also used by Viaqui.

  • Time slots and bookings with a payment deadline
  • Automatic release of unpaid seats
  • Commission with a minimum per ticket
  • Three-level recommendations, nearby places
  • Attraction import from CSV
04

Data and SEO

The catalogue and the search pages.

  • 7,275 attractions from one import, plus 681 monuments from Wikidata
  • City × intent pages for searches
  • SEO text and FAQs from the admin
  • WebSite, Organization, FAQPage structured data
  • Thumbnails generated on the server, at 3% of the photo size

05 · The data

Fifteen county capitals were missing from the catalogue.

The initial attractions file had no rows for cities like Brașov, Sibiu, Iași or Constanța. I filled them in from Wikidata, with a search over the city area: 680 monuments, compared with 253 from a search by administrative unit. The bulk extraction timed out, so I rewrote it to go city by city and add to what already existed.

The original photos were up to 18 MB and were shown in 300-pixel cards. Now the server generates a thumbnail once, at about 3% of the size, and keeps it on disk.

The Museums and exhibitions in Romania category on bilete.online
A category: museums and exhibitions, with filters, text for searches and a map.

06 · Metrics

What I counted.

All figures are measured in the repo or on the site, on 5 November 2026. The site does not yet have traffic or sales I can publish.

  • 7,954attractions in the catalogue: 7,275 from the initial import and 681 monuments from Wikidatameasured
  • 341cities, in 8 regionsfrom the site
  • 12experience categoriesfrom the site
  • ~3%size of a thumbnail compared with the original photomeasured
  • 0.3 shome page response time, a single measurementmeasured
  • 55 + 41pages for visitors and for operatorsin code

How much code

205
PHP files
52k
lines of PHP
89
CSS files
191
design mockups
14
new services in ePAS
~25
migrations in ePAS

07 · What I learned

What I take with me.

A shared platform shortens the launch

Payments, tickets, emails and the admin panel already existed in ePAS. I built only what was new: activities, the map and the planner.

New modules must not touch existing clients

Each new feature is switched on only for the client that uses it, and the platform refuses requests from the others.

Static data beats the API at high volume

The map with almost 8,000 points loads from a single JSON file, generated at build time. It does not make one request for each point.

Say what is estimated

The planner does not have the opening hours of each museum. It uses durations per attraction type and says so right on the page.

08 · Where it is now

The catalogue is large. Tickets on sale are still few.

All 7,954 attractions have a page, photos and a place on the map, but tickets can be bought only for experiences added by operators, and for now there are only a few of those. The attraction pages are not yet in the sitemap. The operator panel, payment and QR tickets work and are waiting for more partners.

09 · Next

I cloned the site for Europe in 11 days.

In October 2026, I started from bilete.online and built Viaqui: 113,093 attractions in 51 countries, with currencies and languages. The Viaqui case study.

Working on something similar?

I build web products from idea to launch, as a developer or as a technical co-founder. Write to me and we will see if it fits.