Search flights via Aviasales, tours via Travelata + Level.Travel, and excursions via Sputnik8 with real prices and booking links. Use when the user asks about travel, flights, airfare, hotels, tours, excursions, vacations, or trip planning.
Search for flights, tours, and excursions with real prices and booking links.
Run the HTTP client to call APIs:
python scripts/api_call.py --method GET --url "<URL>" --params '<JSON>'
python scripts/api_call.py --method POST --url "<URL>" --body '<JSON>'
--params, --body, and --headers must be valid JSON objects. Do not pass query strings like a=1&b=2.
Cached flight prices. Read references/aviasales-data-api.md for all endpoints and parameters.
Quick example — cheapest flights:
python scripts/api_call.py --method GET \
--url "https://api.botclaw.ru/v1/prices/cheap" \
--params '{"origin":"MOW","destination":"AYT","depart_date":"2026-06","currency":"rub"}'
After getting prices, create a booking link on aviasales.ru. See references/aviasales-links.md for URL format, then get a short link:
python scripts/api_call.py --method GET \
--url "https://api.botclaw.ru/short-link" \
--params '{"url":"https://www.aviasales.ru/search?origin_iata=MOW&destination_iata=AYT&depart_date=2026-06-01&return_date=2026-06-15&adults=1&children=0&infants=0&trip_class=0"}'
Package tours (flight + hotel). Real-time search with multiple tours per date, kids' ages, and search by specific hotel. Read references/travelata-api.md for the full flow and parameters. Use references/travelata-directories.md only if you need to look up IDs. Use references/tour-selection-playbook.md when you need to rank, group, and present a shortlist cleanly.
Two-step flow: start an async search, wait, then fetch tours. Use the same criteria in both calls.
Step 1 — start search:
python scripts/api_call.py --method POST \
--url "https://api.botclaw.ru/travelata-partners/tours/asyncSearch" \
--body '{"departureCity":2,"country":92,"checkInDateRange":{"from":"2026-06-08","to":"2026-06-22"},"nightRange":{"from":"7","to":"10"},"touristGroup":{"adults":2,"kids":1,"infants":0,"kidsAges":[8]},"hotelCategories":[4,7],"meals":[1,8]}'
Step 2 — fetch tours (same criteria; pass arrays as key[]):
python scripts/api_call.py --method GET \
--url "https://api.botclaw.ru/travelata-partners/tours" \
--params '{"departureCity":"2","country":"92","checkInDateRange[from]":"2026-06-08","checkInDateRange[to]":"2026-06-22","nightRange[from]":"7","nightRange[to]":"10","touristGroup[adults]":"2","touristGroup[kids]":"1","touristGroup[infants]":"0","touristGroup[kidsAges][]":["8"],"hotelCategories[]":["4","7"],"meals[]":["1","8"],"sections[]":["hotels","meals"],"limit":"30"}'
The response has tours[] with id, price, checkInDate, hotel (id), meal (id), hotelCategory (id). Resolve names via the hotels and meals sections in the same response.
Build the booking URL from tour.hotel and tour.id, then convert through /short-link:
python scripts/api_call.py --method GET \
--url "https://api.botclaw.ru/short-link" \
--params '{"url":"https://travelata.ru/hotel/<tour.hotel>/tourPage?identity=<tour.id>"}'
Important: always pass kidsAges when kids > 0. Always search a date range, never a single day. If filters return 0 results, the first thing to try is re-fetching GET /tours with the same parameters after another 3-second wait — most empty results on far destinations are caused by operators not having responded yet, NOT by overly strict filters. Only after the second fetch is still empty should you drop filters or widen dates.
For 2-adult tours of 7-15 nights within roughly the next 30 days, also call Level.Travel in parallel with Travelata. Level.Travel is a cached snapshot, not live search, so it usually returns immediately while Travelata keeps running.
Skip Level.Travel for families with kids, solo travelers, 3+ adults, trips shorter than 7 nights, longer than 15 nights, far-future dates, and requests that require a strict meal plan.
See references/leveltravel-api.md for the full decision table, parameters, response shape, and departure-key list.
Quick example:
python scripts/api_call.py --method GET \
--url "https://api.botclaw.ru/leveltravel/tours" \
--params '{"departure_key":"moscow","country_iso2":"TR","date_from":"2026-05-01","date_to":"2026-05-15","nights_min":"7","nights_max":"10","stars_min":"4","limit":"20"}'
The response includes feed_synced_at and feed_age_hours. Mention to the user
that Level.Travel data comes from that snapshot, and warn more strongly when
feed_age_hours > 12.
Convert every hotel_url through /short-link before showing it, exactly like
Travelata and Aviasales URLs.
Read references/sputnik8-api.md for endpoints.
Quick example — excursions in Kemer:
python scripts/api_call.py --method GET \
--url "https://api.botclaw.ru/sputnik8/v1/products" \
--params '{"city_id":"420","limit":"10","lang":"ru","currency":"rub","order":"rating","order_type":"desc"}'
GET https://api.botclaw.ru/short-link?url=<URL> — get a short booking link for any travel URLhttp://pics.avs.io/{width}/{height}/{IATA}.png (e.g. http://pics.avs.io/200/200/SU.png)GET http://yasen.aviasales.ru/adaptors/currency.jsoncountry=92) because users often mean the Antalya coast, not only the exact municipality.api.botclaw.ru/short-link/short-link before showing to users — this applies to Aviasales, Travelata, AND Sputnik8 links. Never show raw URLs from API responses directly.touristGroup.kids (not adults). Always pass touristGroup.kidsAges[] with the age of each child — without it the API silently defaults to age 11 and may return wrong room layouts. If the user gives count but not ages, pick a sensible default (e.g. 8) and tell the user you assumed it.GET /tours with the same parameters after waiting another 3 seconds — operators may not have answered yet, especially for Vietnam, Cuba, Dominican Republic, Bali, Maldives. Only AFTER that second fetch is still empty should you drop filters, then widen night range, then widen date range. Do not blast through retries — each is a separate API call.hotels[] when the user names a hotel.resorts[] (array format). Treat the returned tours as valid area matches even when the API returns a more specific child resort/subzone inside that area. Show the actual subzone in the response, but do not silently broaden to a different resort cluster or region unless the user agrees.feed_age_hours. When the query does not fit Level.Travel constraints, silently use Travelata only.Best options in Kemer and If you expand beyond Kemer. Do not mix them into one flat list.