If you are building a trip planner, a student project, or a prototype of a travel booking site, you do not need to pay for data on day one. Several travel APIs offer a free tier, a free sandbox, or open data that returns clean JSON.
The catch: "free" means different things. Some APIs are free forever but limited to places, maps or weather. Others give you a free test environment for flights and hotels, but real bookable inventory needs a paid or approved production account.
This guide lists the most useful free travel APIs, shows real request and response JSON for each, and is honest about the limits so you can pick the right one before writing code.
| API | Covers | API key? | What "free" means |
|---|---|---|---|
| Amadeus Self-Service | Flights, airports, hotels, destinations | Yes (OAuth2) | Free test environment with limited sample data; production access has its own quota and pricing |
| Travelpayouts | Cached flight and hotel price data | Yes (token) | Free for affiliate partners; data is cached, not live booking |
| Duffel | Flight search and booking | Yes | Free test mode; live usage is commercial |
| OpenTripMap | Attractions, points of interest | Yes (free key) | Free key with rate limits |
| Nominatim (OSM) | Geocoding, place search | No | Free under a strict fair-use policy (about 1 request per second) |
| Open-Meteo | Weather forecast | No | Free for non-commercial use; commercial plans exist |
| REST Countries | Country, currency, language data | No | Free, open data |
Step 1: get an access token
cURLcurl "https://test.api.amadeus.com/v1/security/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=YOUR_API_KEY&client_secret=YOUR_API_SECRET"
Response (JSON)
{
"type": "amadeusOAuth2Token",
"access_token": "YOUR_ACCESS_TOKEN",
"expires_in": 1799,
"state": "approved",
"token_type": "Bearer"
}
Step 2: search flight offers (Mumbai to Dubai)
cURLcurl "https://test.api.amadeus.com/v2/shopping/flight-offers?originLocationCode=BOM&destinationLocationCode=DXB&departureDate=2026-12-10&adults=1&max=2" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Response (trimmed, illustrative)
{
"meta": { "count": 2 },
"data": [
{
"type": "flight-offer",
"id": "1",
"itineraries": [
{
"duration": "PT3H15M",
"segments": [
{
"departure": { "iataCode": "BOM", "at": "2026-12-10T08:05:00" },
"arrival": { "iataCode": "DXB", "at": "2026-12-10T09:50:00" },
"carrierCode": "EK",
"number": "501"
}
]
}
],
"price": { "currency": "EUR", "grandTotal": "212.40" }
}
]
}
Python example
Pythonimport requests
BASE = "https://test.api.amadeus.com"
token = requests.post(
f"{BASE}/v1/security/oauth2/token",
data={
"grant_type": "client_credentials",
"client_id": "YOUR_API_KEY",
"client_secret": "YOUR_API_SECRET",
},
).json()["access_token"]
resp = requests.get(
f"{BASE}/v2/shopping/flight-offers",
headers={"Authorization": f"Bearer {token}"},
params={
"originLocationCode": "BOM",
"destinationLocationCode": "DXB",
"departureDate": "2026-12-10",
"adults": 1,
"max": 2,
},
)
for offer in resp.json().get("data", []):
price = offer["price"]
print(price["currency"], price["grandTotal"])
curl "https://api.travelpayouts.com/aviasales/v3/prices_for_dates?origin=BOM&destination=DXB&departure_at=2026-12¤cy=inr&limit=2&token=YOUR_TOKEN"
Response (trimmed, illustrative)
{
"success": true,
"currency": "inr",
"data": [
{
"origin": "BOM",
"destination": "DXB",
"price": 18450,
"airline": "EK",
"departure_at": "2026-12-10T08:05:00+05:30",
"transfers": 0,
"duration": 200,
"link": "/search/..."
}
]
}
curl -X POST "https://api.duffel.com/air/offer_requests?return_offers=true" \
-H "Authorization: Bearer YOUR_TEST_TOKEN" \
-H "Duffel-Version: v2" \
-H "Content-Type: application/json" \
-d '{
"data": {
"slices": [
{ "origin": "BOM", "destination": "DXB", "departure_date": "2026-12-10" }
],
"passengers": [ { "type": "adult" } ],
"cabin_class": "economy"
}
}'
This is where most "free travel API" lists mislead. Bookable hotel inventory is rarely free. Suppliers such as Hotelbeds typically give developers a free test or evaluation environment so you can build and certify your integration, but going live requires an approved commercial account.
If you only need hotel discovery (not booking), you can build it from free building blocks:
OpenTripMap returns points of interest around a coordinate. It is useful for "things to do" sections and itinerary builders. You need a free API key.
cURLcurl "https://api.opentripmap.com/0.1/en/places/radius?radius=2000&lon=55.2744&lat=25.1972&kinds=interesting_places&limit=2&apikey=YOUR_KEY"
Response (trimmed, illustrative)
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": { "type": "Point", "coordinates": [55.2744, 25.1972] },
"properties": {
"xid": "PLACE_ID",
"name": "Burj Khalifa",
"dist": 120.5,
"rate": 7,
"kinds": "interesting_places,architecture"
}
}
]
}
Nominatim converts place names to latitude and longitude with no key. Follow the usage policy:
no more than about one request per second, send a descriptive User-Agent, and cache results.
For heavy traffic, host your own instance or use a commercial geocoder.
curl "https://nominatim.openstreetmap.org/search?q=Dubai&format=json&limit=1" \
-H "User-Agent: my-travel-app/1.0 ([email protected])"
Response (trimmed)
[
{
"lat": "25.2048",
"lon": "55.2708",
"display_name": "Dubai, United Arab Emirates",
"type": "administrative"
}
]
Weather is a great free add-on for destination pages. Open-Meteo needs no key and is free for non-commercial use; check their terms if your site is commercial.
JavaScriptconst url = "https://api.open-meteo.com/v1/forecast" +
"?latitude=25.2048&longitude=55.2708" +
"¤t=temperature_2m,weather_code";
const res = await fetch(url);
const data = await res.json();
console.log(data.current.temperature_2m);
Response (trimmed, illustrative)
{
"latitude": 25.2,
"longitude": 55.27,
"timezone": "GMT",
"current_units": { "temperature_2m": "°C" },
"current": {
"time": "2026-12-10T12:00",
"temperature_2m": 28.4,
"weather_code": 1
}
}
Useful for destination pages: currency, capital, languages. No key required.
cURLcurl "https://restcountries.com/v3.1/name/united%20arab%20emirates?fields=name,capital,currencies,languages"
Response (trimmed, illustrative)
[
{
"name": { "common": "United Arab Emirates" },
"capital": ["Abu Dhabi"],
"currencies": { "AED": { "name": "United Arab Emirates dirham" } },
"languages": { "ara": "Arabic" }
}
]
A practical free stack for a destination page looks like this:
async function destinationSnapshot(city) {
// 1. Geocode
const geo = await fetch(
`https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(city)}&format=json&limit=1`
).then(r => r.json());
const { lat, lon } = geo[0];
// 2. Weather
const weather = await fetch(
`https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}¤t=temperature_2m`
).then(r => r.json());
return {
city,
lat: Number(lat),
lon: Number(lon),
temperatureC: weather.current.temperature_2m
};
}
destinationSnapshot("Dubai").then(console.log);
Output
{
"city": "Dubai",
"lat": 25.2048,
"lon": 55.2708,
"temperatureC": 28.4
}
If you plan to take real bookings, here is where free options usually stop:
Building the rest yourself (markup engine, wallet, B2B agent panel, vouchers, admin panel, multi-supplier search) is what makes custom travel portals slow and expensive. If you want to skip that and launch with flights, hotels and buses already integrated, see our white label travel portal and the cost breakdown. If you are weighing your options, read why most travel portals fail first.
Skip the API integration work. Launch a travel portal with supplier APIs, payments and a B2B agent panel already set up.
Request Free Demo View Pricing