One message, a full itinerary. I had a weekend and an OpenAI API key. This is what I built, what surprised me, and what didn't work.
TravelMe started as a question I asked myself on a Sunday: how hard would it be to build an app where you just describe a trip in plain language and get a complete, ready-to-go itinerary? I had the weekend, I had API access to OpenAI, and I wanted to find out.
This is what I built, what didn't work, and what genuinely surprised me about how capable LLMs have become for this kind of task.
Most travel apps make you enter your destination, then your dates, then your interests, then your budget. That is five separate screens of structured input before you get anything useful. TravelMe's hypothesis is that one message is enough:
“I want to spend 5 days in Japan in October, I like food and architecture but hate tourist traps, budget around €2,000 including flights.”
From that single message, the app should produce: a day-by-day itinerary, accommodation suggestions, estimated costs, and a packing list. All tailored to exactly what was described.
I kept it as simple as possible for the weekend build. A React Native app with a single chat-like input screen, a Node.js backend to handle the OpenAI call, and a results screen that renders the structured output.
The key architectural decision was asking the model to return structured JSON rather than free-form text. This makes the output easy to render properly in the app, easier to validate, and easier to post-process.
// The system prompt: simplified version
const systemPrompt = `
You are a travel planning expert. The user will describe a trip they want to take.
Return a complete travel plan as structured JSON with the following shape:
{
destination: string,
duration_days: number,
estimated_budget: { min: number, max: number, currency: string },
days: Array<{
day: number,
theme: string,
morning: { activity: string, location: string, tip: string },
afternoon: { activity: string, location: string, tip: string },
evening: { activity: string, location: string, tip: string },
estimated_cost: number
}>,
accommodation: Array<{ name: string, area: string, why: string, price_per_night: number }>,
packing_essentials: string[],
local_tips: string[]
}
Be specific. Avoid generic tourist suggestions unless they genuinely match the request.
If the user said they hate tourist traps, don't suggest the Eiffel Tower.
`;One of the most impressive things about modern LLMs is how well they follow a JSON schema when it's described clearly. I didn't need to do any parsing gymnastics. The model returned valid JSON on the first try in about 90% of test cases.
The quality of the travel advice was genuinely good. When I described a trip to Kyoto and said I love traditional craftsmanship but not crowds, the model suggested Nishiki Market at 7am (before it gets busy), a specific lacquerware district I'd never heard of, and a ryokan in Arashiyama rather than the more obvious options in central Kyoto.
This is not generic travel blog output. The model was applying the stated preferences (avoiding crowds, loving craftsmanship) to produce recommendations that actually matched the brief. For a weekend proof of concept, that felt remarkable.
Prices. The model's cost estimates were wildly inconsistent and often outdated. A meal that costs €8 in reality would come back as €15. A hotel that's €120/night would be estimated at €80. This is an inherent limitation of LLMs for anything time-sensitive: their knowledge has a cutoff date and they have no access to live pricing.
For the production version of TravelMe, live pricing will come from APIs (Booking.com, Skyscanner, Google Places), not from the model. The model handles the planning logic; the APIs handle the current data.
The weekend proof of concept became TravelMe. The next steps are real API integrations for live pricing, user accounts so itineraries can be saved, and a sharing feature for planning trips with others. I'll write about each of these as they get built.
LLMs are remarkably good at planning tasks when you give them clear structure constraints and specific context. The secret is not in prompting tricks, it's in being precise about the output format you need and specific about the user's preferences.
Written by
Kristian Gjergji
Developer · Kosovo / Italy