Self-hosted web application to find the cheapest Ryanair flight date combinations.
Scan all possible departure/return date pairs within a configurable range, sort by price, and find the best deal.
Browser :8080
|
Nginx (reverse proxy)
|
+-- /api/* --> Backend (Fastify :3000)
| |
| +-- Ryanair Adapter
| | |
| | +-- @2bad/ryanair v8.1.0
| | |
| | +-- Ryanair API
| |
| +-- Cache (in-memory, TTL configurable)
| +-- Scanner (concurrency-limited queue)
|
+-- /* --> Frontend (React SPA via Nginx)
| Layer | Technology |
|---|---|
| Backend | Node.js, TypeScript, Fastify 5 |
| Ryanair client | @2bad/ryanair v8.1.0 |
| Validation | Zod |
| Frontend | React 19, Vite 6, TailwindCSS 3 |
| Charts | Recharts |
| Proxy | Nginx 1.27 |
| Deployment | Docker, Docker Compose |
Per avviare rapidamente l'applicazione, copia e incolla questo comando:
curl -O https://raw.githubusercontent.com/fenrir22/ryanair-flight-scanner/master/docker-compose.quick.yml && docker compose -f docker-compose.quick.yml up -dOppure scarica manualmente il file docker-compose.quick.yml e avvia:
docker compose -f docker-compose.quick.yml up -dL'applicazione sarà disponibile su http://localhost:8080
- Docker >= 24
- Docker Compose >= 2.20
git clone https://github.com/fenrir22/ryanair-flight-scanner.git
cd ryanair-flight-scanner
cp .env.example .envEdit .env:
| Variable | Default | Description |
|---|---|---|
NODE_ENV |
production |
Environment |
PORT |
3000 |
Backend port |
MARKET |
it-it |
Ryanair market (e.g., it-it, en-gb, de-de, es-es) |
MAX_CONCURRENT_REQUESTS |
3 |
Max parallel Ryanair API calls |
REQUEST_TIMEOUT |
15000 |
Request timeout in ms |
MAX_RETRIES |
2 |
Max retries per request |
CACHE_TTL |
300 |
Cache TTL in seconds |
DEFAULT_CURRENCY |
EUR |
Default currency |
CORS_ORIGIN |
http://localhost:8080 |
Allowed CORS origin |
NTFY_URL |
https://ntfy.sh |
Ntfy server URL for notifications |
SCAN_INTERVAL |
86400000 |
Scheduler interval in ms (default: 24h) |
By default the app uses the Italian market (it-it). This means:
- API calls go to
https://www.ryanair.com/api/booking/v4/it-it/availability - Booking links redirect to
https://www.ryanair.com/it-it/booking?... - Prices are in EUR
To use a different market, change MARKET in .env. The currency will still follow the DEFAULT_CURRENCY setting or the per-search currency parameter.
The app supports sending notifications via Ntfy, a simple pub-sub notification service.
Setup:
- Choose a unique topic name (e.g.,
my-ryanair-deals-123) - Subscribe to the topic on your device:
- Android/iOS: Download the ntfy app from Play Store/App Store and subscribe to your topic
- Web: Visit https://ntfy.sh and subscribe to your topic
- Self-hosted: Set
NTFY_URLto your ntfy server URL
- Create a saved search in the app and set the same topic name
- The scheduler will automatically scan for deals and send notifications when prices match your criteria
Notification content:
- Flight route and dates
- Price
- Direct link to book on Ryanair
- Sent only when deals are found within your price range
docker compose up -d- Select one or more departure airports (e.g., CTA, PMO)
- Select one or more destination airports (e.g., STN, LTN)
- Set departure date range
- Set min/max stay duration
- Click "SEARCH FLIGHTS"
The scanner generates all valid date combinations and queries Ryanair for each, with concurrency control and rate limiting.
- Sorted by price (cheapest first)
- Filter by airport
- Sort by cheapest, duration, departure date, or price per day
- Click "View flight" to open Ryanair booking page
- Toggle price chart to visualize min/avg prices by departure date
Click "Stop scan" to cancel an in-progress search. Completed results are kept.
Create saved searches to receive daily notifications about flight deals:
- Click "+ Nuova Ricerca" in the Saved Searches section
- Fill in the search criteria:
- Name: A descriptive name for your search
- Airports: Origin and destination airports
- Date range: When you want to travel
- Stay duration: Min/max days for your trip
- Trip type: Round-trip or one-way
- Max price: Optional price threshold
- Ntfy topic: Your ntfy topic for notifications
- Save the search
- The scheduler runs every 24 hours and sends notifications when deals are found
You can:
- Enable/disable searches without deleting them
- Delete searches you no longer need
- Edit search criteria by creating a new search
Health check.
Search airports by name, city, or IATA code.
Get available destinations from an airport.
Start a new search.
{
"origins": ["CTA"],
"destinations": ["STN"],
"departureFrom": "2026-09-01",
"departureTo": "2026-09-30",
"minStay": 3,
"maxStay": 7,
"passengers": 1,
"currency": "EUR"
}Response: { "searchId": "search_xxx" }
Get search progress and results.
SSE stream for real-time progress updates.
Cancel an in-progress search.
Get all saved searches. Optional query parameter user_id to filter by user.
Create a new saved search.
{
"user_id": "user123",
"name": "Summer vacation",
"origins": ["CTA"],
"destinations": ["STN", "LTN"],
"departure_from": "2026-06-01",
"departure_to": "2026-08-31",
"min_stay": 7,
"max_stay": 14,
"passengers": 2,
"currency": "EUR",
"trip_type": "round-trip",
"max_price": 200,
"ntfy_topic": "my-ryanair-deals",
"enabled": true
}Update a saved search.
Delete a saved search.
- Ryanair may not operate the requested route
- Dates may be too far in the future (Ryanair typically opens bookings ~1 year ahead)
- Check Docker logs:
docker compose logs backend
The scanner automatically backs off with exponential retry. Reduce MAX_CONCURRENT_REQUESTS if needed.
The Ryanair client version pin has expired. The library auto-refreshes it. If the issue persists, set RYANAIR_CLIENT_VERSION env var to the current Ryanair web build version.
Large date ranges x multiple airports = many combinations. Example: 30 days x 5 stay options x 3 origins x 2 destinations = 900 requests. With 3 concurrent = ~300 sequential batches.
- Depends on
@2bad/ryanairwhich wraps unofficial Ryanair APIs - Ryanair may change or block their APIs at any time
- No booking capability -- links redirect to ryanair.com
- Prices are point-in-time and may change immediately after verification
- No CAPTCHA handling; heavy use may trigger Ryanair's bot detection
This project uses @2bad/ryanair v8.1.0 as the primary interface to Ryanair's API. All integration is isolated in backend/src/adapters/ryanair/. If Ryanair changes their API, only the adapter needs updating.
Methods used:
airports.getActive()-- airport list for autocompleteairports.getDestinations()-- available destinations from an airportfares.findDailyFaresInRange()-- daily fare overview (available for future use)flights.getDates()-- available flight dates (available for future use)
Note: flights.getAvailable() is not used directly because the library hardcodes the en-gb market in the booking API URL. Instead, the adapter makes direct HTTP calls to https://www.ryanair.com/api/booking/v4/{market}/availability with the configurable market (default: it-it). The client-version header auto-refresh logic from the library is replicated in the adapter.
This project is built on top of @2bad/ryanair, an unofficial TypeScript client for the Ryanair API. Special thanks to the 2BAD team for creating and maintaining this excellent library.
Original work: 2BAD/ryanair - MIT License
MIT