I tested both models for two weeks. The answer wasn't about price. It came down to what happens when you tell the model someone has a dietary restriction.
When I started building TravelMe, I knew the core product lived or died on the quality of the AI recommendations. The model had to understand nuanced travel preferences, produce structured JSON reliably, handle ambiguous requests gracefully, and do all of this at a cost that made a consumer app viable.
I tested GPT-4o and Gemini 1.5 Pro seriously for two weeks. This is what I found.
I evaluated both models across five things that matter specifically for TravelMe:
Both models handle structured JSON output well when you use the dedicated JSON mode (OpenAI) or response MIME type setting (Gemini). Without these settings, both occasionally add markdown code fences around the JSON or include explanatory text before or after it, both of which break JSON.parse().
With the settings enabled, OpenAI's JSON mode was slightly more reliable in my tests, about 97% valid JSON vs 94% for Gemini with the same schema. The 3% failures in Gemini were mostly truncated responses for long itineraries (10+ days), which suggests a max-tokens issue rather than a fundamental problem.
This is where the tests got interesting. I created a standard test prompt: a 7-day trip to Italy for someone who explicitly hates touristy restaurants, has a €150/day budget, is vegetarian, and wants to see lesser-known areas.
GPT-4o followed all four constraints in every test run. No tourist traps, budget within range, all meals vegetarian, suggestions in non-obvious locations.
Gemini 1.5 Pro followed three out of four consistently. The vegetarian constraint was honoured in about 80% of runs. In the remaining 20%, meat dishes would appear in restaurant suggestions, sometimes flagged with a note ('can be made vegetarian on request') and sometimes not. For a travel app where dietary restrictions can be a safety concern (religious dietary laws, allergies), 80% is not good enough.
Both models produce specific suggestions, but GPT-4o's suggestions felt more curated. Asking for Rome without the tourist trap filter would get the Colosseum and the Vatican from both, as expected. Applying the filter, GPT-4o suggested Quartiere Coppedè, the Protestant Cemetery in Testaccio, and a specific family-run osteria in Pigneto. Gemini suggested similar alternatives but stayed closer to the well-known neighbourhoods.
This is partly about training data and partly about how the model interprets 'avoid tourist traps'. For TravelMe, where differentiation lives in exactly this kind of local knowledge, the difference matters.
Gemini 1.5 Pro is significantly cheaper. At the time of testing, a full 7-day itinerary generation was costing approximately:
For a freemium app where free-tier users get a limited number of generations per month, GPT-4o is affordable. For a high-volume app with many free users, the cost difference would be significant.
I went with GPT-4o for TravelMe's initial launch. The instruction-following reliability was the deciding factor, specifically the dietary restriction handling. Travel planning is a domain where getting someone's preferences wrong has real consequences, not just a slightly off playlist recommendation.
The cost difference is real, and I'll revisit Gemini once Google addresses the instruction-following consistency. I'm not permanently committed to OpenAI. The model layer in TravelMe's backend is intentionally abstracted so I can swap providers without changing the app.
// Provider-agnostic interface in TravelMe's backend
interface TravelPlannerModel {
generateItinerary(prompt: string): Promise<TravelPlan>;
}
// Easy to swap: OpenAI today, Gemini tomorrow
class OpenAITravelPlanner implements TravelPlannerModel {
async generateItinerary(prompt: string): Promise<TravelPlan> {
// GPT-4o implementation
}
}
class GeminiTravelPlanner implements TravelPlannerModel {
async generateItinerary(prompt: string): Promise<TravelPlan> {
// Gemini implementation, ready when needed
}
}Abstract your AI provider from day one. The model landscape is changing fast, what's best today may not be best in six months. Keeping the interface clean means you can switch without rewriting your whole application.
Written by
Kristian Gjergji
Developer · Kosovo / Italy