├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── questioning.md └── workflows │ ├── deploy-docs.yml │ ├── node-package.yml │ └── python-package.yml ├── .gitignore ├── LICENSE ├── README.md ├── docs ├── assets │ ├── favicon.ico │ └── logo.png ├── index.md ├── nodejs.md ├── projects.md └── python.md ├── mkdocs.yml ├── nodejs ├── .eslintrc.json ├── .npmignore ├── FlightRadar24 │ ├── api.js │ ├── core.js │ ├── entities │ │ ├── airport.js │ │ ├── entity.js │ │ └── flight.js │ ├── errors.js │ ├── flightTrackerConfig.js │ ├── index.d.ts │ ├── index.js │ ├── request.js │ └── util.js ├── LICENSE ├── README.md ├── package-lock.json ├── package.json └── tests │ └── testApi.js └── python ├── .flake8 ├── FlightRadar24 ├── __init__.py ├── api.py ├── core.py ├── entities │ ├── __init__.py │ ├── airport.py │ ├── entity.py │ └── flight.py ├── errors.py └── request.py ├── LICENSE ├── README.md ├── pyproject.toml ├── requirements.txt └── tests ├── conftest.py ├── package.py ├── test_api.py └── util.py /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Set this input '....' 17 | 3. Run the '....' 18 | 4. Scroll down to '....' 19 | 5. See error 20 | 21 | **Expected behavior** 22 | A clear and concise description of what you expected to happen. 23 | 24 | **Screenshots** 25 | If applicable, add screenshots to help explain your problem. 26 | 27 | **System (please complete the following information):** 28 | - OS: [e.g. Windows] 29 | - Python Version [e.g. 1.10] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | - A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | - A clear and concise description of what you want to happen. 13 | - A clear and concise description of any alternative solutions or features you've considered. 14 | 15 | **Is not your feature request related to a problem? Please describe** 16 | A clear and concise description of how your feature can positively impact the project. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/questioning.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Questioning 3 | about: Ask a question about the project 4 | title: '' 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your problem described in the documentation? If so, please describe** 11 | A clear and concise description of what is confusing in the documentation. 12 | 13 | **Describe your question** 14 | A clear and concise description of what the bug is. 15 | 16 | **Is your question reproducible? Please describe** 17 | Steps to reproduce the behavior: 18 | 19 | 1. Go to '...' 20 | 2. Set this input '....' 21 | 3. Run the '....' 22 | 4. Scroll down to '....' 23 | 5. See behavior 24 | 25 | **Screenshots** 26 | If applicable, add screenshots to help explain your problem. 27 | 28 | **System** 29 | If applicable, please complete the following information: 30 | 31 | 1. OS: [e.g. Windows] 32 | 2. Python Version [e.g. 1.10] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/workflows/deploy-docs.yml: -------------------------------------------------------------------------------- 1 | name: Deploy MkDocs 2 | on: 3 | push: 4 | branches: 5 | - main 6 | paths: 7 | - 'mkdocs.yml' 8 | - 'docs/**' 9 | - '.github/workflows/deploy-docs.yml' 10 | permissions: 11 | contents: write 12 | jobs: 13 | deploy: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Configure Git Credentials 18 | run: | 19 | git config user.name github-actions[bot] 20 | git config user.email 41898282+github-actions[bot]@users.noreply.github.com 21 | - uses: actions/setup-python@v5 22 | with: 23 | python-version: 3.x 24 | - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV 25 | - uses: actions/cache@v4 26 | with: 27 | key: mkdocs-material-${{ env.cache_id }} 28 | path: .cache 29 | restore-keys: | 30 | mkdocs-material- 31 | - run: pip install \ 32 | mkdocs-material \ 33 | mkdocs-git-committers-plugin-2 34 | - run: mkdocs gh-deploy --force -------------------------------------------------------------------------------- /.github/workflows/node-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | on: 7 | push: 8 | branches: [ main ] 9 | pull_request: 10 | branches: [ main ] 11 | schedule: 12 | - cron: '0 0 */7 * *' 13 | workflow_dispatch: 14 | 15 | defaults: 16 | run: 17 | working-directory: ./nodejs 18 | 19 | jobs: 20 | build: 21 | runs-on: ubuntu-latest 22 | strategy: 23 | matrix: 24 | node-version: ['14.x', '16.x', '18.x', '20.x'] 25 | steps: 26 | - uses: actions/checkout@v3 27 | - name: Set up NodeJS ${{ matrix.python-version }} 28 | uses: actions/setup-node@v3 29 | with: 30 | node-version: ${{ matrix.node-version }} 31 | - name: Install dependencies 32 | run: npm ci 33 | - name: Test package 34 | run: npm test 35 | -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: Python Package 5 | 6 | on: 7 | push: 8 | branches: [ main ] 9 | pull_request: 10 | branches: [ main ] 11 | schedule: 12 | - cron: '0 0 */7 * *' 13 | workflow_dispatch: 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-latest 18 | strategy: 19 | matrix: 20 | python-version: ['3.8', '3.9', '3.10', '3.11'] 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v2 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | pip install -r python/requirements.txt 31 | - name: Test with pytest 32 | run: | 33 | cd python 34 | pytest tests -vv -s 35 | - name: Install package 36 | run: | 37 | pip install FlightRadar24 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | dist 3 | *.pytest_cache 4 | *.egg-info 5 | .idea/ 6 | venv/ 7 | node_modules/ 8 | .env 9 | .cache -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jean Loui Bernard Silva de Jesus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FlightRadarAPI 2 | Unofficial SDK for [FlightRadar24](https://www.flightradar24.com/) for Python 3 and Node.js. 3 | 4 | This SDK should only be used for your own educational purposes. If you are interested in accessing Flightradar24 data commercially, please contact business@fr24.com. See more information at [Flightradar24's terms and conditions](https://www.flightradar24.com/terms-and-conditions). 5 | 6 | **Official FR24 API**: https://fr24api.flightradar24.com/ 7 | 8 | [![Python Package](https://github.com/JeanExtreme002/FlightRadarAPI/workflows/Python%20Package/badge.svg)](https://github.com/JeanExtreme002/FlightRadarAPI/actions) 9 | [![Pypi](https://img.shields.io/pypi/v/FlightRadarAPI?logo=pypi)](https://pypi.org/project/FlightRadarAPI/) 10 | [![License](https://img.shields.io/pypi/l/FlightRadarAPI)](https://github.com/JeanExtreme002/FlightRadarAPI) 11 | [![Python Version](https://img.shields.io/badge/python-3.7+-8A2BE2)](https://pypi.org/project/FlightRadarAPI/) 12 | [![Npm](https://img.shields.io/npm/v/flightradarapi?logo=npm&color=red)](https://www.npmjs.com/package/flightradarapi) 13 | [![Downloads](https://static.pepy.tech/personalized-badge/flightradarapi?period=total&units=international_system&left_color=grey&right_color=orange&left_text=downloads)](https://pypi.org/project/FlightRadarAPI/) 14 | [![Frequency](https://img.shields.io/pypi/dm/flightradarapi?style=flat&label=frequency)](https://pypi.org/project/FlightRadarAPI/) 15 | 16 | ## Installing FlightRadarAPI 17 | **For Python with pip:** 18 | ``` 19 | pip install FlightRadarAPI 20 | ``` 21 | 22 | **For Node.js with npm:** 23 | ``` 24 | npm install flightradarapi 25 | ``` 26 | 27 | ## Documentation 28 | Explore the documentation of FlightRadarAPI package, for Python or NodeJS, through [this site](https://JeanExtreme002.github.io/FlightRadarAPI/). 29 | -------------------------------------------------------------------------------- /docs/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeanExtreme002/FlightRadarAPI/61e76f5900c4292487f89d0287b828a94138682b/docs/assets/favicon.ico -------------------------------------------------------------------------------- /docs/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeanExtreme002/FlightRadarAPI/61e76f5900c4292487f89d0287b828a94138682b/docs/assets/logo.png -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # FlightRadarAPI Documentation 2 | 3 | Unofficial SDK for [FlightRadar24](https://www.flightradar24.com/) for Python 3 and Node.js. 4 | 5 | This SDK should only be used for your own educational purposes. If you are interested in accessing Flightradar24 data commercially, please contact [business@fr24.com](business@fr24.com). 6 | 7 | See more information at [Flightradar24's terms and conditions](https://www.flightradar24.com/terms-and-conditions). 8 | 9 | [![Python Package](https://github.com/JeanExtreme002/FlightRadarAPI/workflows/Python%20Package/badge.svg)](https://github.com/JeanExtreme002/FlightRadarAPI/actions) 10 | [![Pypi](https://img.shields.io/pypi/v/FlightRadarAPI?logo=pypi)](https://pypi.org/project/FlightRadarAPI/) 11 | [![License](https://img.shields.io/pypi/l/FlightRadarAPI)](https://github.com/JeanExtreme002/FlightRadarAPI) 12 | [![Python Version](https://img.shields.io/badge/python-3.7+-8A2BE2)](https://pypi.org/project/FlightRadarAPI/) 13 | [![Npm](https://img.shields.io/npm/v/flightradarapi?logo=npm&color=red)](https://www.npmjs.com/package/flightradarapi) 14 | [![Downloads](https://static.pepy.tech/personalized-badge/flightradarapi?period=total&units=international_system&left_color=grey&right_color=orange&left_text=downloads)](https://pypi.org/project/FlightRadarAPI/) 15 | [![Frequency](https://img.shields.io/pypi/dm/flightradarapi?style=flat&label=frequency)](https://pypi.org/project/FlightRadarAPI/) 16 | 17 | !!! info 18 | This is NOT an official FlightRadar24 API. You can access their official API [here](https://fr24api.flightradar24.com/). 19 | 20 |
21 | 22 | - :octicons-file-code-16:{ .lg .middle } __100% Free and Open Source!__ 23 | 24 | --- 25 | 26 | The code is open source and available for inspection on GitHub. 27 | 28 | 29 | - :material-sticker-check-outline:{ .lg .middle } __Python and Node.js__ 30 | 31 | --- 32 | 33 | Packages are avaiable for use on both Python and Node.js 34 | 35 |
36 | 37 |
38 | 39 | 40 | 41 |
42 | 43 | ## Installation 44 | 45 | === "Python" 46 | 47 | To install FlightRadarAPI for Python using [pip](https://pypi.org/project/FlightRadarAPI/), run the following command in your terminal: 48 | 49 | ```bash 50 | pip install FlightRadarAPI 51 | ``` 52 | 53 | === "Node.js" 54 | 55 | To install FlightRadarAPI for Node.js using [npm](https://www.npmjs.com/package/flightradarapi), run the following command in your terminal: 56 | 57 | ```bash 58 | npm install flightradarapi 59 | ``` 60 | -------------------------------------------------------------------------------- /docs/nodejs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Node.js 3 | description: API Documentation for Node.js 4 | --- 5 | 6 | ## Installation 7 | 8 | To install the FlightRadarAPI for Node.js, use the following npm command: 9 | 10 | ```bash 11 | npm install flightradarapi 12 | ``` 13 | 14 | ## Basic Usage 15 | 16 | Start by importing the `FlightRadar24API` class and creating an instance of it: 17 | 18 | ```javascript 19 | const { FlightRadar24API } = require("flightradarapi"); 20 | const frApi = new FlightRadar24API(); 21 | ``` 22 | 23 | ### Fetching Data 24 | 25 | You can fetch various types of data using the following methods: 26 | 27 | - **Flights list:** 28 | 29 | ```javascript 30 | let flights = await frApi.getFlights(...); // Returns a list of Flight objects 31 | ``` 32 | 33 | - **Airports list:** 34 | 35 | ```javascript 36 | let airports = await frApi.getAirports(...); // Returns a list of Airport objects 37 | ``` 38 | 39 | - **Airlines list:** 40 | 41 | ```javascript 42 | let airlines = await frApi.getAirlines(); 43 | ``` 44 | 45 | - **Zones list:** 46 | 47 | ```javascript 48 | let zones = await frApi.getZones(); 49 | ``` 50 | 51 | ### Fetching Detailed Information 52 | 53 | Fetch more information about a specific flight or airport using the following methods: 54 | 55 | - **Flight details:** 56 | 57 | ```javascript 58 | let flightDetails = await frApi.getFlightDetails(flight); 59 | flight.setFlightDetails(flightDetails); 60 | 61 | console.log("Flying to", flight.destinationAirportName); 62 | ``` 63 | 64 | - **Airport details:** 65 | 66 | ```javascript 67 | let airportDetails = await frApi.getAirportDetails(icao); 68 | ``` 69 | 70 | !!! note 71 | Arrivals and departures can have a limit `flightLimit` (max value is 100) to display. When you need to reach more than 100 flights you can use additional parameter `page` to view other pages. 72 | 73 | ## Advanced Usage 74 | 75 | ### Fetching Flights Above a Specific Position 76 | 77 | Use the `getBoundsByPoint(...)` method to fetch flights above a specific position. This method takes `latitude` and `longitude` for your position and `radius` for the distance in meters from your position to designate a tracking area. 78 | 79 | ```javascript 80 | // Your point is 52°34'04.7"N 13°16'57.5"E from Google Maps and radius 2km 81 | let bounds = frApi.getBoundsByPoint(52.567967, 13.282644, 2000); 82 | 83 | let flights = await frApi.getFlights(null, bounds); 84 | ``` 85 | 86 | ### Filtering Flights and Airports 87 | 88 | Use the `getFlights(...)` method to search for flights by area line, bounds (customized coordinates or obtained by the `getZones()` method), aircraft registration or aircraft type. 89 | 90 | ```javascript 91 | let airlineIcao = "UAE"; 92 | let aircraftType = "B77W"; 93 | 94 | // You may also set a custom region, such as: bounds = "73,-12,-156,38" 95 | let zone = (await frApi.getZones())["northamerica"]; 96 | let bounds = frApi.getBounds(zone); 97 | 98 | let emiratesFlights = await frApi.getFlights( 99 | airlineIcao, 100 | bounds, 101 | null, 102 | aircraftType, 103 | ); 104 | ``` 105 | 106 | ### Fetching Airport by ICAO or IATA 107 | 108 | ```javascript 109 | let luklaAirport = await frApi.getAirport("VNLK", true); 110 | ``` 111 | 112 | ### Calculating Distance Between Flights and Airports 113 | 114 | The `Flight` and `Airport` classes inherit from `Entity`, which contains the `getDistanceFrom(...)` method. This method returns the distance between the self instance and another entity in kilometers. 115 | 116 | ```javascript 117 | let airport = await frApi.getAirport("KJFK"); 118 | let distance = flight.getDistanceFrom(airport); 119 | 120 | console.log("The flight is", distance, "km away from the airport."); 121 | ``` 122 | 123 | ## Downloading Flight Data :material-information-outline:{ title="This requires a premium subscription" } 124 | 125 | You can download flight data in either CSV or KML format. The method `getHistoryData(...)` is used for this purpose. It takes three parameters: 126 | 127 | !!! warning inline end 128 | If an invalid time is provided, a blank document will be returned. 129 | 130 | | Parameter | Description | 131 | | ------------- | ------------- | 132 | | `flight_id` | The ID of the flight. This can be obtained from any other function that returns flight details. | 133 | | `file_type` | The format of the file to download. This can be either "CSV" or "KML". | 134 | | `time` | The scheduled time of departure (STD) of the flight in UTC, as a Unix timestamp. | 135 | 136 | Here is an example of how to use this method: 137 | 138 | ```javascript 139 | let historyData = await frApi.getHistoryData(flight, "csv", 1706529600); 140 | 141 | const buffer = Buffer.from(historyData); 142 | fs.writeFile("history_data.csv", buffer); 143 | ``` 144 | 145 | ### Setting and Getting Real-time Flight Tracker Parameters 146 | 147 | Set it by using the `setFlightTrackerConfig(...)` method. It receives a `FlightTrackerConfig` dataclass instance, but you can also use keyword arguments directly to the method. 148 | 149 | Get the current configuration with the `getFlightTrackerConfig()` method, that returns a `FlightTrackerConfig` instance. Note: creating a new `FlightTrackerConfig` instance means resetting all parameters to default. 150 | 151 | ```javascript 152 | let flightTracker = frApi.getFlightTrackerConfig(); 153 | flightTracker.limit = 10 154 | 155 | frApi.setFlightTrackerConfig(flightTracker, ...); 156 | 157 | let flights = await frApi.getFlights(...); // Returns only 10 flights 158 | ``` 159 | -------------------------------------------------------------------------------- /docs/projects.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Projects using FlightRadarAPI 3 | description: Nice projects that use FlightRadarAPI 4 | --- 5 | 6 |
7 | 8 | - [__Automatic Calibration in Crowd-sourced Network of Spectrum Sensors__](https://dl.acm.org/doi/10.1145/3626111.3628187) 9 | 10 | --- 11 | 12 | By Ali Abedi, Joshua Sanz, Anant Sahai, from University of California (UC), Berkeley 13 | 14 | 15 | - [__Airline Status Tracker for top 10 Airport Departures in North America__](https://people.ischool.berkeley.edu/~frank.song/flight.html) 16 | 17 | --- 18 | 19 | By Adeleke Coker, Frank Song, Greg Chi, from University of California (UC), Berkeley, School of Information 20 | 21 | - [__Flight Tracker with Weather__](https://magpi.raspberrypi.com/articles/flight-tracker-with-weather) 22 | 23 | --- 24 | 25 | By Adam Paulson, a Reddit user going by the name C0wsaysmoo 26 | 27 | - [__Design and implementation project of an aircraft mobile node module for the SeamSAT-LEO constellation simulator__](https://upcommons.upc.edu/bitstream/handle/2117/394691/TFG.pdf?sequence=2&isAllowed=y) 28 | 29 | --- 30 | 31 | By David Anton Dobarro, graduate in Aerospace Technologies Engineering from Universitat Politècnica de Catalunya 32 | 33 | - [__Fridge Flight Tracker__](https://blog.colinwaddell.com/flight-tracker/) 34 | 35 | --- 36 | 37 | By Colin Waddell, a Glasgow based programmer, website designer and electronics specialist 38 | 39 | - [__Plane spotting Telegram Bot__](https://github.com/ViLsonCake/avgeek-telegram-bot) 40 | 41 | --- 42 | 43 | By Timofey Dmitrenko, aviation geek and Java Spring Boot programmer 44 | 45 |
46 | 47 | [Contribute Your Own :writing_hand:](https://github.com/JeanExtreme002/FlightRadarAPI/edit/main/docs/projects.md){ .md-button .md-button--primary } 48 | -------------------------------------------------------------------------------- /docs/python.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Python 3 | description: API Documentation for Python 4 | --- 5 | 6 | ## Installation 7 | 8 | To install the FlightRadarAPI for Python, use the following pip command: 9 | 10 | ```bash 11 | pip install FlightRadarAPI 12 | ``` 13 | 14 | ## Basic Usage 15 | 16 | Start by importing the `FlightRadar24API` class and creating an instance of it: 17 | 18 | ```python 19 | from FlightRadar24 import FlightRadar24API 20 | fr_api = FlightRadar24API() 21 | ``` 22 | 23 | ### Fetching Data 24 | 25 | You can fetch various types of data using the following methods: 26 | 27 | - **Flights list:** 28 | 29 | ```python 30 | flights = fr_api.get_flights(...) # Returns a list of Flight objects 31 | ``` 32 | 33 | - **Airports list:** 34 | 35 | ```python 36 | airports = fr_api.get_airports(...) # Returns a list of Airport objects 37 | ``` 38 | 39 | - **Airlines list:** 40 | 41 | ```python 42 | airlines = fr_api.get_airlines() 43 | ``` 44 | 45 | - **Zones list:** 46 | 47 | ```python 48 | zones = fr_api.get_zones() 49 | ``` 50 | 51 | ### Fetching Detailed Information 52 | 53 | Fetch more information about a specific flight or airport using the following methods: 54 | 55 | - **Flight details:** 56 | 57 | ```python 58 | flight_details = fr_api.get_flight_details(flight) 59 | flight.set_flight_details(flight_details) 60 | 61 | print("Flying to", flight.destination_airport_name) 62 | ``` 63 | 64 | - **Airport details:** 65 | 66 | ```python 67 | airport_details = fr_api.get_airport_details(icao) 68 | ``` 69 | 70 | !!! note 71 | Arrivals and departures can have a limit `flightLimit` (max value is 100) to display. When you need to reach more than 100 flights you can use additional parameter `page` to view other pages. 72 | 73 | ## Advanced Usage 74 | 75 | ### Fetching Flights Above a Specific Position 76 | 77 | Use the `get_bounds_by_point(...)` method to fetch flights above a specific position. This method takes `latitude` and `longitude` for your position and `radius` for the distance in meters from your position to designate a tracking area. 78 | 79 | ```python 80 | # Your point is 52°34'04.7"N 13°16'57.5"E from Google Maps and radius 2km 81 | bounds = fr_api.get_bounds_by_point(52.567967, 13.282644, 2000) 82 | 83 | flights = fr_api.get_flights(bounds = bounds) 84 | ``` 85 | 86 | ### Filtering Flights and Airports 87 | 88 | Use the `get_flights(...)` method to search for flights by area line, bounds (customized coordinates or obtained by the `get_zones()` method), aircraft registration or aircraft type. 89 | 90 | ```python 91 | airline_icao = "UAE" 92 | aircraft_type = "B77W" 93 | 94 | # You may also set a custom region, such as: bounds = "73,-12,-156,38" 95 | zone = fr_api.get_zones()["northamerica"] 96 | bounds = fr_api.get_bounds(zone) 97 | 98 | emirates_flights = fr_api.get_flights( 99 | aircraft_type = aircraft_type, 100 | airline = airline_icao, 101 | bounds = bounds 102 | ) 103 | ``` 104 | 105 | ### Fetching Airport by ICAO or IATA 106 | 107 | ```python 108 | lukla_airport = fr_api.get_airport(code = "VNLK", details = True) 109 | ``` 110 | 111 | ### Calculating Distance Between Flights and Airports 112 | 113 | The `Flight` and `Airport` classes inherit from `Entity`, which contains the `get_distance_from(...)` method. This method returns the distance between the self instance and another entity in kilometers. 114 | 115 | ```python 116 | airport = fr_api.get_airport("KJFK") 117 | distance = flight.get_distance_from(airport) 118 | 119 | print(f"The flight is {distance} km away from the airport.") 120 | ``` 121 | 122 | ### Downloading Flight Data :material-information-outline:{ title="This requires a premium subscription" } 123 | 124 | 125 | ```py 126 | history_data = fr_api.get_history_data(flight, file_type="csv", time=1706529600) 127 | 128 | with open("history_data.csv", "w") as file: 129 | file.write(history_data) 130 | ``` 131 | 132 | !!! warning inline end 133 | If an invalid time is provided, a blank document will be returned. 134 | 135 | | Parameter | Description | 136 | | ------------- | ------------- | 137 | | `flight_id` | The ID of the flight. This can be obtained from any other function that returns flight details. | 138 | | `file_type` | The format of the file to download. This can be either "CSV" or "KML". | 139 | | `time` | The scheduled time of departure (STD) of the flight in UTC, as a Unix timestamp. | 140 | 141 | 142 | ### Setting and Getting Real-time Flight Tracker Parameters 143 | 144 | Set it by using the `set_flight_tracker_config(...)` method. It receives a `FlightTrackerConfig` dataclass instance, but you can also use keyword arguments directly to the method. 145 | 146 | Get the current configuration with the `get_flight_tracker_config()` method, that returns a `FlightTrackerConfig` instance. Note: creating a new `FlightTrackerConfig` instance means resetting all parameters to default. 147 | 148 | ```python 149 | flight_tracker = fr_api.get_flight_tracker_config() 150 | flight_tracker.limit = 10 151 | 152 | fr_api.set_flight_tracker_config(flight_tracker, ...) 153 | 154 | flights = fr_api.get_flights(...) # Returns only 10 flights 155 | ``` 156 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: FlightRadarAPI 2 | repo_url: https://github.com/JeanExtreme002/FlightRadarAPI 3 | docs_dir: docs 4 | site_description: Documentation for the FlightRadarAPI 5 | site_author: JeanExtreme002 6 | edit_uri: edit/main/docs/ 7 | 8 | nav: 9 | - Home: index.md 10 | - Python: python.md 11 | - Node.js: nodejs.md 12 | - Projects Using FlightRadarAPI: projects.md 13 | 14 | theme: 15 | name: material 16 | logo: assets/logo.png 17 | favicon: assets/favicon.ico 18 | 19 | features: 20 | - navigation.instant 21 | - navigation.instant.progress 22 | - content.code.copy 23 | - content.action.edit 24 | - search.suggest 25 | - search.highlight 26 | - search.share 27 | - content.tooltips 28 | - content.tabs.link 29 | 30 | icon: 31 | repo: fontawesome/brands/github 32 | annotation: material/arrow-right-circle 33 | 34 | palette: 35 | - primary: indigo 36 | - accent: yellow 37 | 38 | extra: 39 | social: 40 | - icon: fontawesome/brands/github 41 | link: https://github.com/JeanExtreme002 42 | 43 | markdown_extensions: 44 | - admonition 45 | - pymdownx.details 46 | - attr_list 47 | - md_in_html 48 | - abbr 49 | - attr_list 50 | - pymdownx.snippets 51 | - pymdownx.emoji: 52 | emoji_index: !!python/name:material.extensions.emoji.twemoji 53 | emoji_generator: !!python/name:material.extensions.emoji.to_svg 54 | - pymdownx.tabbed: 55 | alternate_style: true 56 | - pymdownx.superfences 57 | 58 | plugins: 59 | - git-committers: 60 | repository: JeanExtreme002/FlightRadarAPI 61 | branch: main 62 | - search -------------------------------------------------------------------------------- /nodejs/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "commonjs": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": "google", 8 | "parserOptions": { 9 | "ecmaVersion": "latest" 10 | }, 11 | "rules": { 12 | "max-len": ["error", {"code": 130}], 13 | "quotes": ["warn", "double"], 14 | "indent": ["warn", 4], 15 | "brace-style": ["warn", "stroustrup"], 16 | "valid-jsdoc": ["warn"], 17 | "require-jsdoc": ["warn"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /nodejs/.npmignore: -------------------------------------------------------------------------------- 1 | .eslintrc.json 2 | tests/ -------------------------------------------------------------------------------- /nodejs/FlightRadar24/api.js: -------------------------------------------------------------------------------- 1 | const Core = require("./core"); 2 | const APIRequest = require("./request"); 3 | const Airport = require("./entities/airport"); 4 | const Flight = require("./entities/flight"); 5 | const FlightTrackerConfig = require("./flightTrackerConfig"); 6 | const {AirportNotFoundError, LoginError} = require("./errors"); 7 | const {isNumeric} = require("./util"); 8 | 9 | 10 | /** 11 | * Main class of the FlightRadarAPI 12 | */ 13 | class FlightRadar24API { 14 | /** 15 | * Constructor of FlightRadar24API class 16 | */ 17 | constructor() { 18 | this.__flightTrackerConfig = new FlightTrackerConfig(); 19 | this.__loginData = null; 20 | } 21 | 22 | /** 23 | * Return a list with all airlines. 24 | * 25 | * @return {object} 26 | */ 27 | async getAirlines() { 28 | const response = new APIRequest(Core.airlinesDataUrl, null, Core.jsonHeaders); 29 | await response.receive(); 30 | 31 | return (await response.getContent())["rows"]; 32 | } 33 | 34 | /** 35 | * Download the logo of an airline from FlightRadar24 and return it as bytes. 36 | * 37 | * @param {string} iata - IATA of the airline 38 | * @param {string} icao - ICAO of the airline 39 | * @return {[object, string]} 40 | */ 41 | async getAirlineLogo(iata, icao) { 42 | iata = iata.toUpperCase(); 43 | icao = icao.toUpperCase(); 44 | 45 | const firstLogoUrl = Core.airlineLogoUrl.format(iata, icao); 46 | 47 | // Try to get the image by the first URL option. 48 | let response = new APIRequest(firstLogoUrl, null, Core.imageHeaders, null, null, [403]); 49 | await response.receive(); 50 | 51 | let statusCode = response.getStatusCode(); 52 | 53 | if (!statusCode.toString().startsWith("4")) { 54 | const splitUrl = firstLogoUrl.split("."); 55 | return [(await response.getContent()), splitUrl[splitUrl.length - 1]]; 56 | } 57 | 58 | // Get the image by the second airline logo URL. 59 | const secondLogoUrl = Core.alternativeAirlineLogoUrl.format(icao); 60 | 61 | response = new APIRequest(secondLogoUrl, null, Core.imageHeaders); 62 | await response.receive(); 63 | 64 | statusCode = response.getStatusCode(); 65 | 66 | if (!statusCode.toString().startsWith("4")) { 67 | const splitUrl = secondLogoUrl.split("."); 68 | return [(await response.getContent()), splitUrl[splitUrl.length - 1]]; 69 | } 70 | } 71 | 72 | /** 73 | * Return basic information about a specific airport. 74 | * 75 | * @param {string} code - ICAO or IATA of the airport 76 | * @param {boolean} details - If true, it returns flights with detailed information 77 | * @return {Airport} 78 | */ 79 | async getAirport(code, details = false) { 80 | if (4 < code.length || code.length < 3) { 81 | throw new Error("The code '" + code + "' is invalid. It must be the IATA or ICAO of the airport."); 82 | } 83 | 84 | if (details) { 85 | const airport = new Airport(); 86 | 87 | const airportDetails = await this.getAirportDetails(code); 88 | airport.setAirportDetails(airportDetails); 89 | 90 | return airport; 91 | } 92 | 93 | const response = new APIRequest(Core.airportDataUrl.format(code), null, Core.jsonHeaders); 94 | await response.receive(); 95 | 96 | const info = (await response.getContent())["details"]; 97 | 98 | if (info === undefined) { 99 | throw new AirportNotFoundError("Could not find an airport by the code '" + code + "'."); 100 | } 101 | return new Airport({}, info); 102 | } 103 | 104 | /** 105 | * Return the airport details from FlightRadar24. 106 | * 107 | * @param {string} code - ICAO or IATA of the airport 108 | * @param {number} [flightLimit=100] - Limit of flights related to the airport 109 | * @param {number} [page=1] - Page of result to display 110 | * @return {object} 111 | */ 112 | async getAirportDetails(code, flightLimit = 100, page = 1) { 113 | if (4 < code.length || code.length < 3) { 114 | throw new Error("The code '" + code + "' is invalid. It must be the IATA or ICAO of the airport."); 115 | } 116 | 117 | const requestParams = {"format": "json"}; 118 | 119 | if (this.__loginData != null) { 120 | requestParams["token"] = this.__loginData["cookies"]["_frPl"]; 121 | } 122 | 123 | // Insert the method parameters into the dictionary for the request. 124 | requestParams["code"] = code; 125 | requestParams["limit"] = flightLimit; 126 | requestParams["page"] = page; 127 | 128 | // Request details from the FlightRadar24. 129 | const response = new APIRequest(Core.apiAirportDataUrl, requestParams, Core.jsonHeaders, null, null, [400]); 130 | await response.receive(); 131 | 132 | const content = await response.getContent(); 133 | 134 | if (response.getStatusCode() === 400 && content?.["errors"] !== undefined) { 135 | const errors = content["errors"]?.["errors"]?.["parameters"]; 136 | const limit = errors?.["limit"]; 137 | 138 | if (limit !== undefined) { 139 | throw new Error(limit["notBetween"]); 140 | } 141 | throw new AirportNotFoundError("Could not find an airport by the code '" + code + "'.", errors); 142 | } 143 | 144 | const result = content["result"]["response"]; 145 | 146 | // Check whether it received data of an airport. 147 | const data = result?.["airport"]?.["pluginData"]; 148 | const dataCount = typeof data === "object" ? Object.entries(data).length : 0; 149 | 150 | const runways = data?.["runways"]; 151 | const runwaysCount = typeof runways === "object" ? Object.entries(runways).length : 0; 152 | 153 | if (data?.["details"] === undefined && runwaysCount == 0 && dataCount <= 3) { 154 | throw new AirportNotFoundError("Could not find an airport by the code '" + code + "'."); 155 | } 156 | 157 | // Return the airport details. 158 | return result; 159 | } 160 | 161 | /** 162 | * Return airport disruptions. 163 | * 164 | * @return {object} 165 | */ 166 | async getAirportDisruptions() { 167 | const response = new APIRequest(Core.airportDisruptionsUrl, null, Core.jsonHeaders); 168 | await response.receive(); 169 | 170 | return await response.getContent(); 171 | } 172 | 173 | /** 174 | * Return a list with all airports. 175 | * 176 | * @return {Array} 177 | */ 178 | async getAirports() { 179 | const response = new APIRequest(Core.airportsDataUrl, null, Core.jsonHeaders); 180 | await response.receive(); 181 | 182 | const content = await response.getContent(); 183 | const airports = []; 184 | 185 | for (const airportData of content["rows"]) { 186 | const airport = new Airport(airportData); 187 | airports.push(airport); 188 | } 189 | return airports; 190 | } 191 | 192 | /** 193 | * Return the bookmarks from the FlightRadar24 account. 194 | * 195 | * @return {object} 196 | */ 197 | async getBookmarks() { 198 | if (!this.isLoggedIn()) { 199 | throw new LoginError("You must log in to your account."); 200 | } 201 | 202 | const headers = {...Core.jsonHeaders}; 203 | headers["accesstoken"] = this.getLoginData()["accessToken"]; 204 | 205 | const cookies = this.__loginData["cookies"]; 206 | 207 | const response = new APIRequest(Core.bookmarksUrl, null, headers, null, cookies); 208 | await response.receive(); 209 | 210 | return await response.getContent(); 211 | } 212 | 213 | /** 214 | * Convert coordinate dictionary to a string "y1, y2, x1, x2". 215 | * 216 | * @param {object} zone - Dictionary containing the following keys: tl_y, tl_x, br_y, br_x 217 | * @return {string} 218 | */ 219 | getBounds(zone) { 220 | return "" + zone["tl_y"] + "," + zone["br_y"] + "," + zone["tl_x"] + "," + zone["br_x"]; 221 | } 222 | 223 | /** 224 | * Convert a point coordinate and a radius to a string "y1, y2, x1, x2". 225 | * 226 | * @param {number} latitude - Latitude of the point 227 | * @param {number} longitude - Longitude of the point 228 | * @param {number} radius - Radius in meters to create area around the point 229 | * @return {string} 230 | */ 231 | getBoundsByPoint(latitude, longitude, radius) { 232 | const halfSideInKm = Math.abs(radius) / 1000; 233 | 234 | Math.rad2deg = (x) => x * (180 / Math.PI); 235 | Math.radians = (x) => x * (Math.PI / 180); 236 | 237 | const lat = Math.radians(latitude); 238 | const lon = Math.radians(longitude); 239 | 240 | const approxEarthRadius = 6371; 241 | const hypotenuseDistance = Math.sqrt(2 * (Math.pow(halfSideInKm, 2))); 242 | 243 | const latMin = Math.asin( 244 | Math.sin(lat) * Math.cos(hypotenuseDistance / approxEarthRadius) + 245 | Math.cos(lat) * 246 | Math.sin(hypotenuseDistance / approxEarthRadius) * 247 | Math.cos(225 * (Math.PI / 180)), 248 | ); 249 | const lonMin = lon + Math.atan2( 250 | Math.sin(225 * (Math.PI / 180)) * 251 | Math.sin(hypotenuseDistance / approxEarthRadius) * 252 | Math.cos(lat), 253 | Math.cos(hypotenuseDistance / approxEarthRadius) - 254 | Math.sin(lat) * Math.sin(latMin), 255 | ); 256 | 257 | const latMax = Math.asin( 258 | Math.sin(lat) * Math.cos(hypotenuseDistance / approxEarthRadius) + 259 | Math.cos(lat) * 260 | Math.sin(hypotenuseDistance / approxEarthRadius) * 261 | Math.cos(45 * (Math.PI / 180)), 262 | ); 263 | const lonMax = lon + Math.atan2( 264 | Math.sin(45 * (Math.PI / 180)) * 265 | Math.sin(hypotenuseDistance / approxEarthRadius) * 266 | Math.cos(lat), 267 | Math.cos(hypotenuseDistance / approxEarthRadius) - 268 | Math.sin(lat) * Math.sin(latMax), 269 | ); 270 | 271 | const zone = { 272 | "tl_y": Math.rad2deg(latMax), 273 | "br_y": Math.rad2deg(latMin), 274 | "tl_x": Math.rad2deg(lonMin), 275 | "br_x": Math.rad2deg(lonMax), 276 | }; 277 | return this.getBounds(zone); 278 | } 279 | 280 | /** 281 | * Download the flag of a country from FlightRadar24 and return it as bytes. 282 | * 283 | * @param {string} country - Country name 284 | * @return {[object, string]} 285 | */ 286 | async getCountryFlag(country) { 287 | const flagUrl = Core.countryFlagUrl.format(country.toLowerCase().replace(" ", "-")); 288 | const headers = {...Core.imageHeaders}; 289 | 290 | if (headers.hasOwnProperty("origin")) { 291 | delete headers["origin"]; // Does not work for this request. 292 | } 293 | 294 | const response = new APIRequest(flagUrl, null, headers); 295 | await response.receive(); 296 | 297 | const statusCode = response.getStatusCode(); 298 | 299 | if (!statusCode.toString().startsWith("4")) { 300 | const splitUrl = flagUrl.split("."); 301 | return [(await response.getContent()), splitUrl[splitUrl.length - 1]]; 302 | } 303 | } 304 | 305 | /** 306 | * Return the flight details from Data Live FlightRadar24. 307 | * 308 | * @param {Flight} flight - A Flight instance 309 | * @return {object} 310 | */ 311 | async getFlightDetails(flight) { 312 | const response = new APIRequest(Core.flightDataUrl.format(flight.id), null, Core.jsonHeaders); 313 | await response.receive(); 314 | 315 | return (await response.getContent()); 316 | } 317 | 318 | /** 319 | * Return a list of flights. See more options at setFlightTrackerConfig() method. 320 | * 321 | * @param {string} [airline] - The airline ICAO. Ex: "DAL" 322 | * @param {string} [bounds] - Coordinates (y1, y2 ,x1, x2). Ex: "75.78,-75.78,-427.56,427.56" 323 | * @param {string} [registration] - Aircraft registration 324 | * @param {string} [aircraftType] - Aircraft model code. Ex: "B737" 325 | * @param {boolean} [details] - If true, it returns flights with detailed information 326 | * @return {Array} 327 | */ 328 | async getFlights(airline = null, bounds = null, registration = null, aircraftType = null, details = false) { 329 | const requestParams = {...this.__flightTrackerConfig}; 330 | 331 | if (this.__loginData != null) { 332 | requestParams["enc"] = this.__loginData["cookies"]["_frPl"]; 333 | } 334 | 335 | // Insert the method parameters into the dictionary for the request. 336 | if (airline != null) { 337 | requestParams["airline"] = airline; 338 | } 339 | if (bounds != null) { 340 | requestParams["bounds"] = bounds.replace(",", "%2C"); 341 | } 342 | if (registration != null) { 343 | requestParams["reg"] = registration; 344 | } 345 | if (aircraftType != null) { 346 | requestParams["type"] = aircraftType; 347 | } 348 | 349 | // Get all flights from Data Live FlightRadar24. 350 | const response = new APIRequest(Core.realTimeFlightTrackerDataUrl, requestParams, Core.jsonHeaders); 351 | await response.receive(); 352 | 353 | const content = await response.getContent(); 354 | const flights = []; 355 | 356 | for (const flightId in content) { 357 | if (!Object.prototype.hasOwnProperty.call(content, flightId)) { // guard-for-in 358 | continue; 359 | } 360 | 361 | const flightInfo = content[flightId]; 362 | 363 | // Get flights only. 364 | if (!isNumeric(flightId[0])) { 365 | continue; 366 | } 367 | 368 | const flight = new Flight(flightId, flightInfo); 369 | flights.push(flight); 370 | 371 | // Set flight details. 372 | if (details) { 373 | const flightDetails = await this.getFlightDetails(flight); 374 | flight.setFlightDetails(flightDetails); 375 | } 376 | } 377 | 378 | return flights; 379 | } 380 | 381 | /** 382 | * Return a copy of the current config of the Real Time Flight Tracker, used by getFlights() method. 383 | * 384 | * @return {FlightTrackerConfig} 385 | */ 386 | getFlightTrackerConfig() { 387 | return new FlightTrackerConfig({...this.__flightTrackerConfig}); 388 | } 389 | 390 | /** 391 | * Download historical data of a flight. 392 | * 393 | * @param {Flight} flight - A Flight instance. 394 | * @param {string} fileType - Must be "CSV" or "KML" 395 | * @param {number} timestamp - A Unix timestamp 396 | */ 397 | async getHistoryData(flight, fileType, timestamp) { 398 | if (!this.isLoggedIn()) { 399 | throw new LoginError("You must log in to your account."); 400 | } 401 | 402 | fileType = fileType.toLowerCase(); 403 | 404 | if (!["csv", "kml"].includes(fileType)) { 405 | throw new Error("File type '" + fileType + "' is not supported. Only CSV and KML are supported."); 406 | } 407 | 408 | const response = new APIRequest( 409 | Core.historicalDataUrl.format(flight.id, fileType, timestamp), 410 | null, Core.jsonHeaders, null, self.__loginData["cookies"], 411 | ); 412 | await response.receive(); 413 | 414 | const content = await response.getContent(); 415 | return content; 416 | } 417 | 418 | /** 419 | * Return the user data. 420 | * 421 | * @return {object} 422 | */ 423 | getLoginData() { 424 | if (!this.isLoggedIn()) { 425 | throw new LoginError("You must log in to your account."); 426 | } 427 | return {...this.__loginData["userData"]}; 428 | } 429 | 430 | /** 431 | * Return the most tracked data. 432 | * 433 | * @return {object} 434 | */ 435 | async getMostTracked() { 436 | const response = new APIRequest(Core.mostTrackedUrl, null, Core.jsonHeaders); 437 | await response.receive(); 438 | 439 | return await response.getContent(); 440 | } 441 | 442 | /** 443 | * Return boundaries of volcanic eruptions and ash clouds impacting aviation. 444 | * 445 | * @return {object} 446 | */ 447 | async getVolcanicEruptions() { 448 | const response = new APIRequest(Core.volcanicEruptionDataUrl, null, Core.jsonHeaders); 449 | await response.receive(); 450 | 451 | return await response.getContent(); 452 | } 453 | 454 | /** 455 | * Return all major zones on the globe. 456 | * 457 | * @return {object} 458 | */ 459 | async getZones() { 460 | const response = new APIRequest(Core.zonesDataUrl, null, Core.jsonHeaders); 461 | await response.receive(); 462 | 463 | const zones = await response.getContent(); 464 | 465 | if (zones.hasOwnProperty("version")) { 466 | delete zones["version"]; 467 | } 468 | return zones; 469 | } 470 | 471 | /** 472 | * Return the search result. 473 | * 474 | * @param {string} query 475 | * @param {number} [limit=50] 476 | * @return {object} 477 | */ 478 | async search(query, limit = 50) { 479 | const url = Core.searchDataUrl.format(query, limit); 480 | 481 | const response = new APIRequest(url, null, Core.jsonHeaders); 482 | await response.receive(); 483 | 484 | const content = await response.getContent(); 485 | 486 | let results = content["results"]; 487 | results = results == null ? [] : results; 488 | 489 | let stats = content["stats"]; 490 | stats = stats == null ? {} : stats; 491 | 492 | let countDict = stats["count"]; 493 | countDict = countDict == null ? {} : countDict; 494 | 495 | let index = 0; 496 | let countedTotal = 0; 497 | 498 | const data = {}; 499 | 500 | for (const name in countDict) { 501 | if (!Object.prototype.hasOwnProperty.call(countDict, name)) { // guard-for-in 502 | continue; 503 | } 504 | 505 | const count = countDict[name]; 506 | 507 | data[name] = []; 508 | 509 | while (index < (countedTotal + count) && (index < results.length)) { 510 | data[name].push(results[index]); 511 | index++; 512 | } 513 | countedTotal += count; 514 | } 515 | 516 | return data; 517 | } 518 | 519 | /** 520 | * Check if the user is logged into the FlightRadar24 account. 521 | * 522 | * @return {boolean} 523 | */ 524 | isLoggedIn() { 525 | return this.__loginData != null; 526 | } 527 | 528 | /** 529 | * Log in to a FlightRadar24 account. 530 | * 531 | * @param {string} user - Your email. 532 | * @param {string} password - Your password. 533 | * @return {undefined} 534 | */ 535 | async login(user, password) { 536 | const data = { 537 | "email": user, 538 | "password": password, 539 | "remember": "true", 540 | "type": "web", 541 | }; 542 | 543 | const response = new APIRequest(Core.userLoginUrl, null, Core.jsonHeaders, data); 544 | await response.receive(); 545 | 546 | const statusCode = response.getStatusCode(); 547 | const content = await response.getContent(); 548 | 549 | if (!statusCode.toString().startsWith("2") || !content["success"]) { 550 | if (typeof content === "object") { 551 | throw new LoginError(content["message"]); 552 | } 553 | else { 554 | throw new LoginError("Your email or password is incorrect"); 555 | } 556 | } 557 | 558 | this.__loginData = { 559 | "userData": content["userData"], 560 | "cookies": response.getCookies(), 561 | }; 562 | } 563 | 564 | /** 565 | * Log out of the FlightRadar24 account. 566 | * 567 | * @return {boolean} - Return a boolean indicating that it successfully logged out of the server. 568 | */ 569 | async logout() { 570 | if (this.__loginData == null) { 571 | return true; 572 | } 573 | 574 | const cookies = this.__loginData["cookies"]; 575 | this.__loginData = null; 576 | 577 | const response = new APIRequest(Core.userLoginUrl, null, Core.jsonHeaders, null, cookies); 578 | await response.receive(); 579 | 580 | return response.getStatusCode().toString().startsWith("2"); 581 | } 582 | 583 | /** 584 | * Set config for the Real Time Flight Tracker, used by getFlights() method. 585 | * 586 | * @param {FlightTrackerConfig} [flightTrackerConfig] - If null, set to the default config. 587 | * @param {object} [config={}] - Config as an JSON object 588 | * @return {undefined} 589 | */ 590 | async setFlightTrackerConfig(flightTrackerConfig = null, config = {}) { 591 | if (flightTrackerConfig != null) { 592 | this.__flightTrackerConfig = flightTrackerConfig; 593 | } 594 | 595 | for (const key in config) { 596 | if (Object.prototype.hasOwnProperty.call(config, key)) { // guard-for-in 597 | const value = config[key].toString(); 598 | this.__flightTrackerConfig[key] = value; 599 | } 600 | } 601 | } 602 | } 603 | 604 | module.exports = FlightRadar24API; 605 | -------------------------------------------------------------------------------- /nodejs/FlightRadar24/core.js: -------------------------------------------------------------------------------- 1 | String.prototype.format = function() { 2 | const args = arguments; 3 | let index = 0; 4 | 5 | return this.replace(/{}/g, function(match, position) { 6 | return (typeof args[index] == "undefined") ? match : args[index++]; 7 | }); 8 | }; 9 | 10 | 11 | /** 12 | * Class which contains all URLs used by the package. 13 | */ 14 | class Core { 15 | /** 16 | * Constructor of the Core class 17 | */ 18 | constructor() { 19 | this.apiFlightradarBaseUrl = "https://api.flightradar24.com/common/v1"; 20 | this.cdnFlightradarBaseUrl = "https://cdn.flightradar24.com"; 21 | this.flightRadarBaseUrl = "https://www.flightradar24.com"; 22 | this.dataLiveBaseUrl = "https://data-live.flightradar24.com"; 23 | this.dataCloudBaseUrl = "https://data-cloud.flightradar24.com"; 24 | 25 | // User login URL. 26 | this.userLoginUrl = this.flightRadarBaseUrl + "/user/login"; 27 | this.userLogoutUrl = this.flightRadarBaseUrl + "/user/logout"; 28 | 29 | // Search data URL 30 | this.searchDataUrl = this.flightRadarBaseUrl + "/v1/search/web/find?query={}&limit={}"; 31 | 32 | // Flights data URLs. 33 | this.realTimeFlightTrackerDataUrl = this.dataCloudBaseUrl + "/zones/fcgi/feed.js"; 34 | this.flightDataUrl = this.dataLiveBaseUrl + "/clickhandler/?flight={}"; 35 | 36 | // Historical data URL. 37 | this.historicalDataUrl = this.flightradarBaseUrl + "/download/?flight={}&file={}&trailLimit=0&history={}"; 38 | 39 | // Airports data URLs. 40 | this.apiAirportDataUrl = this.apiFlightradarBaseUrl + "/airport.json"; 41 | this.airportDataUrl = this.flightRadarBaseUrl + "/airports/traffic-stats/?airport={}"; 42 | this.airportsDataUrl = this.flightRadarBaseUrl + "/_json/airports.php"; 43 | 44 | // Airlines data URL. 45 | this.airlinesDataUrl = this.flightRadarBaseUrl + "/_json/airlines.php"; 46 | 47 | // Zones data URL. 48 | this.zonesDataUrl = this.flightRadarBaseUrl + "/js/zones.js.php"; 49 | 50 | // Weather data URL. 51 | this.volcanicEruptionDataUrl = this.flightRadarBaseUrl + "/weather/volcanic"; 52 | 53 | // Most tracked URL 54 | this.mostTrackedUrl = this.flightRadarBaseUrl + "/flights/most-tracked"; 55 | 56 | // Airport disruptions URL. 57 | this.airportDisruptionsUrl = this.flightRadarBaseUrl + "/webapi/v1/airport-disruptions"; 58 | 59 | // Bookmarks URL. 60 | this.bookmarksUrl = this.flightRadarBaseUrl + "/webapi/v1/bookmarks"; 61 | 62 | // Country flag image URL. 63 | this.countryFlagUrl = this.flightRadarBaseUrl + "/static/images/data/flags-small/{}.svg"; 64 | 65 | // Airline logo image URL. 66 | this.airlineLogoUrl = this.cdnFlightradarBaseUrl + "/assets/airlines/logotypes/{}_{}.png"; 67 | this.alternativeAirlineLogoUrl = this.flightRadarBaseUrl + "/static/images/data/operators/{}_logo0.png"; 68 | 69 | this.headers = { 70 | "accept-encoding": "gzip, br", 71 | "accept-language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7", 72 | "cache-control": "max-age=0", 73 | "origin": "https://www.flightradar24.com", 74 | "referer": "https://www.flightradar24.com/", 75 | "sec-fetch-dest": "empty", 76 | "sec-fetch-mode": "cors", 77 | "sec-fetch-site": "same-site", 78 | "user-agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36", 79 | }; 80 | 81 | this.jsonHeaders = {accept: "application/json", ...this.headers}; 82 | 83 | this.imageHeaders = {accept: "image/gif, image/jpg, image/jpeg, image/png", ...this.headers}; 84 | } 85 | } 86 | 87 | module.exports = new Core(); 88 | -------------------------------------------------------------------------------- /nodejs/FlightRadar24/entities/airport.js: -------------------------------------------------------------------------------- 1 | const Entity = require("./entity"); 2 | 3 | 4 | /** 5 | * Airport representation. 6 | */ 7 | class Airport extends Entity { 8 | /** 9 | * Constructor of Airport class. 10 | * 11 | * The parameters below are optional. You can just create an Airport instance with no information 12 | * and use the setAirportDetails(...) method for having an instance with detailed information. 13 | * 14 | * @param {object} [basicInfo] - Basic information about the airport received from FlightRadar24 15 | * @param {object} [info] - Dictionary with more information about the airport received from FlightRadar24 16 | */ 17 | constructor(basicInfo = {}, info = {}) { 18 | super(); 19 | 20 | if (basicInfo && Object.keys(basicInfo).length > 0) { 21 | this.__setPosition(basicInfo["lat"], basicInfo["lon"]); 22 | this.__initializeWithBasicInfo(basicInfo); 23 | } 24 | 25 | if (info && Object.keys(info).length > 0) { 26 | this.__setPosition(info["position"]["latitude"], info["position"]["longitude"]); 27 | this.__initializeWithInfo(info); 28 | } 29 | } 30 | 31 | /** 32 | * Initialize instance with basic information about the airport. 33 | * 34 | * @param {object} basicInfo 35 | */ 36 | __initializeWithBasicInfo(basicInfo) { 37 | this.altitude = basicInfo["alt"]; 38 | 39 | this.name = basicInfo["name"]; 40 | this.icao = basicInfo["icao"]; 41 | this.iata = basicInfo["iata"]; 42 | 43 | this.country = basicInfo["country"]; 44 | } 45 | 46 | /** 47 | * Initialize instance with extra information about the airport. 48 | * 49 | * @param {object} info 50 | */ 51 | __initializeWithInfo(info) { 52 | this.altitude = info["position"]["altitude"]; 53 | 54 | this.name = info["name"]; 55 | this.icao = info["code"]["icao"]; 56 | this.iata = info["code"]["iata"]; 57 | 58 | // Location information. 59 | const position = info["position"]; 60 | 61 | this.country = position["country"]["name"]; 62 | this.countryCode = this.__getInfo(position["country"]?.["code"]); 63 | this.city = this.__getInfo(position["region"]?.["city"]); 64 | 65 | // Timezone information. 66 | const timezone = info["timezone"]; 67 | 68 | this.timezoneName = this.__getInfo(timezone?.["name"]); 69 | this.timezoneOffset = this.__getInfo(timezone?.["offset"]); 70 | this.timezoneOffsetHours = this.__getInfo(timezone?.["offsetHours"]); 71 | this.timezoneAbbr = this.__getInfo(timezone?.["abbr"]); 72 | this.timezoneAbbrName = this.__getInfo(timezone?.["abbrName"]); 73 | 74 | // Other information. 75 | this.visible = this.__getInfo(info["visible"]); 76 | this.website = this.__getInfo(info["website"]); 77 | } 78 | 79 | /** 80 | * Set airport details to the instance. Use FlightRadar24API.getAirportDetails(...) method to get it. 81 | * 82 | * @param {object} airportDetails 83 | */ 84 | setAirportDetails(airportDetails) { 85 | // Get airport data. 86 | const airport = airportDetails?.["airport"]?.["pluginData"]; 87 | 88 | // Get information about the airport. 89 | const details = airport?.["details"]; 90 | 91 | // Get location information. 92 | const position = details?.["position"]; 93 | const code = details?.["code"]; 94 | const country = position?.["country"]; 95 | const region = position?.["region"]; 96 | 97 | // Get reviews of the airport. 98 | const flightDiary = airport?.["flightdiary"]; 99 | const ratings = flightDiary?.["ratings"]; 100 | 101 | // Get schedule information. 102 | const schedule = airport?.["schedule"]; 103 | 104 | // Get timezone information. 105 | const timezone = details?.["timezone"]; 106 | 107 | // Get aircraft count. 108 | const aircraftCount = airport?.["aircraftCount"]; 109 | const aircraftOnGround = aircraftCount?.["onGround"]; 110 | 111 | // Get URLs for more information about the airport. 112 | const urls = details?.["url"]; 113 | 114 | // Basic airport information. 115 | this.name = this.__getInfo(details?.["name"]); 116 | this.iata = this.__getInfo(code?.["iata"]); 117 | this.icao = this.__getInfo(code?.["icao"]); 118 | this.altitude = this.__getInfo(position?.["elevation"]); 119 | this.latitude = this.__getInfo(position?.["latitude"]); 120 | this.longitude = this.__getInfo(position?.["longitude"]); 121 | 122 | // Airport location. 123 | this.country = this.__getInfo(country?.["name"]); 124 | this.country_code = this.__getInfo(country?.["code"]); 125 | this.country_id = this.__getInfo(country?.["id"]); 126 | this.city = this.__getInfo(region?.["city"]); 127 | 128 | // Airport timezone. 129 | this.timezoneAbbr = this.__getInfo(timezone?.["abbr"]); 130 | this.timezoneAbbrName = this.__getInfo(timezone?.["abbrName"]); 131 | this.timezoneName = this.__getInfo(timezone?.["name"]); 132 | this.timezoneOffset = this.__getInfo(timezone?.["offset"]); 133 | 134 | if (typeof this.timezoneOffset === "number") { 135 | this.timezoneOffsetHours = Math.trunc(this.timezoneOffset / 60 / 60); 136 | this.timezoneOffsetHours = this.timezoneOffsetHours + ":00"; 137 | } 138 | else { 139 | this.timezoneOffsetHours = this.__getInfo(None); 140 | } 141 | 142 | // Airport reviews. 143 | this.reviewsUrl = flightDiary?.["url"]; 144 | 145 | if (this.reviewsUrl && typeof this.reviewsUrl === "string") { 146 | this.reviewsUrl = "https://www.flightradar24.com" + this.reviewsUrl; 147 | } 148 | else { 149 | this.reviewsUrl = this.__getInfo(this.reviewsUrl); 150 | } 151 | 152 | this.reviews = this.__getInfo(flightDiary?.["reviews"]); 153 | this.evaluation = this.__getInfo(flightDiary?.["evaluation"]); 154 | 155 | this.averageRating = this.__getInfo(ratings?.["avg"]); 156 | this.totalRating = this.__getInfo(ratings?.["total"]); 157 | 158 | // Weather information. 159 | this.weather = this.__getInfo(airport?.["weather"], {}); 160 | 161 | // Runway information. 162 | this.runways = this.__getInfo(airport?.["runways"], []); 163 | 164 | // Aircraft count information. 165 | this.aircraftOnGround = this.__getInfo(aircraftOnGround?.["total"]); 166 | this.aircraftVisibleOnGround = this.__getInfo(aircraftOnGround?.["visible"]); 167 | 168 | // Schedule information. 169 | this.arrivals = this.__getInfo(schedule?.["arrivals"], {}); 170 | this.departures = this.__getInfo(schedule?.["departures"], {}); 171 | 172 | // Link for the homepage and more information 173 | this.website = this.__getInfo(urls?.["homepage"]); 174 | this.wikipedia = this.__getInfo(urls?.["wikipedia"]); 175 | 176 | // Other information. 177 | this.visible = this.__getInfo(details?.["visible"]); 178 | this.images = this.__getInfo(details?.["airportImages"], {}); 179 | } 180 | } 181 | 182 | module.exports = Airport; 183 | -------------------------------------------------------------------------------- /nodejs/FlightRadar24/entities/entity.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Representation of a real entity, at some location. 3 | */ 4 | class Entity { 5 | static __defaultText = "N/A"; 6 | 7 | /** 8 | * Constructor of Entity class. 9 | * 10 | * @param {number} latitude 11 | * @param {number} longitude 12 | */ 13 | constructor(latitude = null, longitude = null) { 14 | this.__setPosition(latitude, longitude); 15 | } 16 | 17 | __setPosition(latitude, longitude) { 18 | this.latitude = latitude; 19 | this.longitude = longitude; 20 | } 21 | 22 | __getInfo(info, replaceBy = undefined) { 23 | replaceBy = replaceBy === undefined ? this.__defaultText : replaceBy; 24 | return (info || info === 0) && (info !== this.__defaultText) ? info : replaceBy; 25 | } 26 | 27 | /** 28 | * Return the distance from another entity (in kilometers). 29 | * 30 | * @param {Entity} entity 31 | * @return {number} 32 | */ 33 | getDistanceFrom(entity) { 34 | Math.radians = (x) => x * (Math.PI / 180); 35 | 36 | const lat1 = Math.radians(this.latitude); 37 | const lon1 = Math.radians(this.longitude); 38 | 39 | const lat2 = Math.radians(entity.latitude); 40 | const lon2 = Math.radians(entity.longitude); 41 | 42 | return Math.acos(Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1)) * 6371; 43 | } 44 | } 45 | 46 | module.exports = Entity; 47 | -------------------------------------------------------------------------------- /nodejs/FlightRadar24/entities/flight.js: -------------------------------------------------------------------------------- 1 | const Entity = require("./entity"); 2 | 3 | /** 4 | * Flight representation. 5 | */ 6 | class Flight extends Entity { 7 | /** 8 | * Constructor of Flight class. 9 | * 10 | * @param {*} flightId - The flight ID specifically used by FlightRadar24 11 | * @param {*} info - Dictionary with received data from FlightRadar24 12 | */ 13 | constructor(flightId, info) { 14 | super(); 15 | 16 | this.__setPosition(this.__getInfo(info[1]), this.__getInfo(info[2])); 17 | 18 | this.id = flightId; 19 | this.icao24bit = this.__getInfo(info[0]); 20 | this.heading = this.__getInfo(info[3]); 21 | this.altitude = this.__getInfo(info[4]); 22 | this.groundSpeed = this.__getInfo(info[5]); 23 | this.squawk = this.__getInfo(info[6]); 24 | this.aircraftCode = this.__getInfo(info[8]); 25 | this.registration = this.__getInfo(info[9]); 26 | this.time = this.__getInfo(info[10]); 27 | this.originAirportIata = this.__getInfo(info[11]); 28 | this.destinationAirportIata = this.__getInfo(info[12]); 29 | this.number = this.__getInfo(info[13]); 30 | this.airlineIata = this.__getInfo(info[13].slice(0, 2)); 31 | this.onGround = this.__getInfo(info[14]); 32 | this.verticalSpeed =this.__getInfo(info[15]); 33 | this.callsign = this.__getInfo(info[16]); 34 | this.airlineIcao = this.__getInfo(info[18]); 35 | } 36 | 37 | /** 38 | * Check one or more flight information. 39 | * 40 | * You can use the prefix "max" or "min" in the parameter 41 | * to compare numeric data with ">" or "<". 42 | * 43 | * Example: checkInfo({minAltitude: 6700, maxAltitude: 13000, airlineIcao: "THY"}) 44 | * 45 | * @param {object} info 46 | * @return {boolean} 47 | */ 48 | checkInfo(info) { 49 | const comparisonFunctions = {"max": Math.max, "min": Math.min}; 50 | 51 | for (let key in info) { 52 | if (!Object.prototype.hasOwnProperty.call(info, key)) { // guard-for-in 53 | continue; 54 | } 55 | 56 | let prefix = key.slice(0, 3); 57 | const value = info[key]; 58 | 59 | // Separate the comparison prefix if it exists. 60 | if ((prefix === "max") || (prefix === "min")) { 61 | key = key[3].toLowerCase() + key.slice(4, key.length); 62 | } 63 | else { 64 | prefix = null; 65 | } 66 | 67 | // Check if the value is greater than or less than the attribute value. 68 | if (this.hasOwnProperty(key) && prefix) { 69 | if (comparisonFunctions[prefix](value, this[key]) !== value) { 70 | return false; 71 | } 72 | } 73 | 74 | // Check if the value is equal. 75 | else if (this.hasOwnProperty(key) && value !== this[key]) { 76 | return false; 77 | } 78 | } 79 | return true; 80 | } 81 | 82 | /** 83 | * Return the formatted altitude, with the unit of measure. 84 | * 85 | * @return {string} 86 | */ 87 | getAltitude() { 88 | return this.altitude.toString() + " ft"; 89 | } 90 | 91 | /** 92 | * Return the formatted flight level, with the unit of measure. 93 | * 94 | * @return {string} 95 | */ 96 | getFlightLevel() { 97 | if (this.altitude >= 10000) { 98 | return this.altitude.toString().slice(0, 3) + " FL"; 99 | } 100 | return this.getAltitude(); 101 | } 102 | 103 | /** 104 | * Return the formatted ground speed, with the unit of measure. 105 | * 106 | * @return {string} 107 | */ 108 | getGroundSpeed() { 109 | const sufix = this.groundSpeed > 1 ? "s" : ""; 110 | return this.groundSpeed.toString() + " kt" + sufix; 111 | } 112 | 113 | /** 114 | * Return the formatted heading, with the unit of measure. 115 | * 116 | * @return {string} 117 | */ 118 | getHeading() { 119 | return this.heading.toString() + "°"; 120 | } 121 | 122 | /** 123 | * Return the formatted vertical speed, with the unit of measure. 124 | * 125 | * @return {string} 126 | */ 127 | getVerticalSpeed() { 128 | return this.verticalSpeed.toString() + " fpm"; 129 | } 130 | 131 | /** 132 | * Set flight details to the instance. Use FlightRadar24API.getFlightDetails(...) method to get it. 133 | * 134 | * @param {object} flightDetails 135 | * @return {undefined} 136 | */ 137 | setFlightDetails(flightDetails) { 138 | // Get aircraft data. 139 | const aircraft = flightDetails["aircraft"]; 140 | 141 | // Get airline data. 142 | const airline = flightDetails?.["airline"]; 143 | 144 | // Get airport data. 145 | const airport = flightDetails?.["airport"]; 146 | 147 | // Get destination data. 148 | const destAirport = airport?.["destination"]; 149 | const destAirportCode = destAirport?.["code"]; 150 | const destAirportInfo = destAirport?.["info"]; 151 | const destAirportPosition = destAirport?.["position"]; 152 | const destAirportCountry = destAirportPosition?.["country"]; 153 | const destAirportTimezone = destAirport?.["timezone"]; 154 | 155 | // Get origin data. 156 | const origAirport = airport?.["origin"]; 157 | const origAirportCode = origAirport?.["code"]; 158 | const origAirportInfo = origAirport?.["info"]; 159 | const origAirportPosition = origAirport?.["position"]; 160 | const origAirportCountry = origAirportPosition?.["country"]; 161 | const origAirportTimezone = origAirport?.["timezone"]; 162 | 163 | // Get flight history. 164 | const history = flightDetails?.["flightHistory"]; 165 | 166 | // Get flight status. 167 | const status = flightDetails?.["status"]; 168 | 169 | // Aircraft information. 170 | this.aircraftAge = this.__getInfo(aircraft?.["age"]); 171 | this.aircraftCountryId = this.__getInfo(aircraft?.["countryId"]); 172 | this.aircraftHistory = this.__getInfo(history?.["aircraft"], []); 173 | this.aircraftImages = this.__getInfo(aircraft?.["images"], []); 174 | this.aircraftModel = this.__getInfo(aircraft?.["model"]?.["text"]); 175 | 176 | // Airline information. 177 | this.airlineName = this.__getInfo(airline?.["name"]); 178 | this.airlineShortName = this.__getInfo(airline?.["short"]); 179 | 180 | // Destination airport position. 181 | this.destinationAirportAltitude = this.__getInfo(destAirportPosition?.["altitude"]); 182 | this.destinationAirportCountryCode = this.__getInfo(destAirportCountry?.["code"]); 183 | this.destinationAirportCountryName = this.__getInfo(destAirportCountry?.["name"]); 184 | this.destinationAirportLatitude = this.__getInfo(destAirportPosition?.["latitude"]); 185 | this.destinationAirportLongitude = this.__getInfo(destAirportPosition?.["longitude"]); 186 | 187 | // Destination airport information. 188 | this.destinationAirportIcao = this.__getInfo(destAirportCode?.["icao"]); 189 | this.destinationAirportBaggage = this.__getInfo(destAirportInfo?.["baggage"]); 190 | this.destinationAirportGate = this.__getInfo(destAirportInfo?.["gate"]); 191 | this.destinationAirportName = this.__getInfo(destAirport?.["name"]); 192 | this.destinationAirportTerminal = this.__getInfo(destAirportInfo?.["terminal"]); 193 | this.destinationAirportVisible = this.__getInfo(destAirport?.["visible"]); 194 | this.destinationAirportWebsite = this.__getInfo(destAirport?.["website"]); 195 | 196 | // Destination airport timezone. 197 | this.destinationAirportTimezoneAbbr = this.__getInfo(destAirportTimezone?.["abbr"]); 198 | this.destinationAirportTimezoneAbbrName = this.__getInfo(destAirportTimezone?.["abbrName"]); 199 | this.destinationAirportTimezoneName = this.__getInfo(destAirportTimezone?.["name"]); 200 | this.destinationAirportTimezoneOffset = this.__getInfo(destAirportTimezone?.["offset"]); 201 | this.destinationAirportTimezoneOffsetHours = this.__getInfo(destAirportTimezone?.["offsetHours"]); 202 | 203 | // Origin airport position. 204 | this.originAirportAltitude = this.__getInfo(origAirportPosition?.["altitude"]); 205 | this.originAirportCountryCode = this.__getInfo(origAirportCountry?.["code"]); 206 | this.originAirportCountryName = this.__getInfo(origAirportCountry?.["name"]); 207 | this.originAirportLatitude = this.__getInfo(origAirportPosition?.["latitude"]); 208 | this.originAirportLongitude = this.__getInfo(origAirportPosition?.["longitude"]); 209 | 210 | // Origin airport information. 211 | this.originAirportIcao = this.__getInfo(origAirportCode?.["icao"]); 212 | this.originAirportBaggage = this.__getInfo(origAirportInfo?.["baggage"]); 213 | this.originAirportGate = this.__getInfo(origAirportInfo?.["gate"]); 214 | this.originAirportName = this.__getInfo(origAirport?.["name"]); 215 | this.originAirportTerminal = this.__getInfo(origAirportInfo?.["terminal"]); 216 | this.originAirportVisible = this.__getInfo(origAirport?.["visible"]); 217 | this.originAirportWebsite = this.__getInfo(origAirport?.["website"]); 218 | 219 | // Origin airport timezone. 220 | this.originAirportTimezoneAbbr = this.__getInfo(origAirportTimezone?.["abbr"]); 221 | this.originAirportTimezoneAbbrName = this.__getInfo(origAirportTimezone?.["abbrName"]); 222 | this.originAirportTimezoneName = this.__getInfo(origAirportTimezone?.["name"]); 223 | this.originAirportTimezoneOffset = this.__getInfo(origAirportTimezone?.["offset"]); 224 | this.originAirportTimezoneOffsetHours = this.__getInfo(origAirportTimezone?.["offsetHours"]); 225 | 226 | // Flight status. 227 | this.statusIcon = this.__getInfo(status?.["icon"]); 228 | this.statusText = this.__getInfo(status?.["text"]); 229 | 230 | // Time details. 231 | this.timeDetails = this.__getInfo(flightDetails?.["time"], {}); 232 | 233 | // Flight trail. 234 | this.trail = this.__getInfo(flightDetails?.["trail"], []); 235 | } 236 | } 237 | 238 | module.exports = Flight; 239 | -------------------------------------------------------------------------------- /nodejs/FlightRadar24/errors.js: -------------------------------------------------------------------------------- 1 | class AirportNotFoundError extends Error { 2 | constructor(message) { 3 | super(message); 4 | 5 | this.name = this.constructor.name; 6 | 7 | Error.captureStackTrace(this, this.constructor); 8 | } 9 | } 10 | 11 | class CloudflareError extends Error { 12 | constructor(message, response) { 13 | super(message); 14 | 15 | this.name = this.constructor.name; 16 | this.response = response; 17 | 18 | Error.captureStackTrace(this, this.constructor); 19 | } 20 | } 21 | 22 | class LoginError extends Error { 23 | constructor(message) { 24 | super(message); 25 | 26 | this.name = this.constructor.name; 27 | 28 | Error.captureStackTrace(this, this.constructor); 29 | } 30 | } 31 | 32 | module.exports = {AirportNotFoundError, CloudflareError, LoginError}; 33 | -------------------------------------------------------------------------------- /nodejs/FlightRadar24/flightTrackerConfig.js: -------------------------------------------------------------------------------- 1 | const {isNumeric} = require("./util"); 2 | 3 | 4 | const proxyHandler = { 5 | set: function(target, key, value) { 6 | if (!target.hasOwnProperty(key)) { 7 | throw new Error("Unknown option: '" + key + "'"); 8 | } 9 | if ((typeof value !== "number") && (!isNumeric(value))) { 10 | throw new Error("Value must be a decimal. Got '" + key + "'"); 11 | } 12 | target[key] = value.toString(); 13 | }, 14 | }; 15 | 16 | 17 | /** 18 | * Data class with settings of the Real Time Flight Tracker. 19 | */ 20 | class FlightTrackerConfig { 21 | faa = "1"; 22 | satellite = "1"; 23 | mlat = "1"; 24 | flarm = "1"; 25 | adsb = "1"; 26 | gnd = "1"; 27 | air = "1"; 28 | vehicles = "1"; 29 | estimated = "1"; 30 | maxage = "14400"; 31 | gliders = "1"; 32 | stats = "1"; 33 | limit = "5000"; 34 | 35 | /** 36 | * Constructor of FlighTrackerConfig class. 37 | * 38 | * @param {object} data 39 | */ 40 | constructor(data) { 41 | for (const key in data) { 42 | if (!Object.prototype.hasOwnProperty.call(data, key)) { // guard-for-in 43 | continue; 44 | } 45 | const value = data[key]; 46 | 47 | if (this.hasOwnProperty(key) && (typeof value === "number" || isNumeric(value))) { 48 | this[key] = value; 49 | } 50 | } 51 | return new Proxy(this, proxyHandler); 52 | } 53 | } 54 | 55 | module.exports = FlightTrackerConfig; 56 | -------------------------------------------------------------------------------- /nodejs/FlightRadar24/index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Main class of the FlightRadarAPI 3 | */ 4 | export class FlightRadar24API { 5 | private __flightTrackerConfig: FlightTrackerConfig; 6 | private __loginData: {userData: any; cookies: any;} | null; 7 | 8 | /** 9 | * Constructor of FlightRadar24API class 10 | */ 11 | constructor(); 12 | 13 | /** 14 | * Return a list with all airlines. 15 | */ 16 | getAirlines(): Promise; 17 | 18 | /** 19 | * Download the logo of an airline from FlightRadar24 and return it as bytes. 20 | * 21 | * @param {string} iata - IATA of the airline 22 | * @param {string} icao - ICAO of the airline 23 | */ 24 | getAirlineLogo( 25 | iata: string, 26 | icao: string, 27 | ): Promise<[object, string] | undefined>; 28 | 29 | /** 30 | * Return basic information about a specific airport. 31 | * 32 | * @param {string} code - ICAO or IATA of the airport 33 | * @param {boolean} details - If true, it returns flights with detailed information 34 | */ 35 | getAirport(code: string, details?: boolean): Promise; 36 | 37 | /** 38 | * Return the airport details from FlightRadar24. 39 | * 40 | * @param {string} code - ICAO or IATA of the airport 41 | * @param {number} [flightLimit=100] - Limit of flights related to the airport 42 | * @param {number} [page=1] - Page of result to display 43 | */ 44 | getAirportDetails( 45 | code: string, 46 | flightLimit?: number, 47 | page?: number, 48 | ): Promise; 49 | 50 | /** 51 | * Return airport disruptions. 52 | */ 53 | getAirportDisruptions(): Promise; 54 | 55 | /** 56 | * Return a list with all airports. 57 | */ 58 | getAirports(): Promise; 59 | 60 | /** 61 | * Return the bookmarks from the FlightRadar24 account. 62 | */ 63 | getBookmarks(): Promise; 64 | 65 | /** 66 | * Convert coordinate dictionary to a string "y1, y2, x1, x2". 67 | * 68 | * @param {object} zone - Dictionary containing the following keys: tl_y, tl_x, br_y, br_x 69 | */ 70 | getBounds(zone: { 71 | tl_y: number; 72 | br_y: number; 73 | tl_x: number; 74 | br_x: number; 75 | }): string; 76 | 77 | /** 78 | * Convert a point coordinate and a radius to a string "y1, y2, x1, x2". 79 | * 80 | * @param {number} latitude - Latitude of the point 81 | * @param {number} longitude - Longitude of the point 82 | * @param {number} radius - Radius in meters to create area around the point 83 | */ 84 | getBoundsByPoint( 85 | latitude: number, 86 | longitude: number, 87 | radius: number, 88 | ): string; 89 | 90 | /** 91 | * Download the flag of a country from FlightRadar24 and return it as bytes. 92 | * 93 | * @param {string} country - Country name 94 | */ 95 | getCountryFlag(country: string): Promise<[object, string] | undefined>; 96 | 97 | /** 98 | * Return the flight details from Data Live FlightRadar24. 99 | * 100 | * @param {Flight} flight - A Flight instance 101 | */ 102 | getFlightDetails(flight: Flight): Promise; 103 | 104 | /** 105 | * Return a list of flights. See more options at setFlightTrackerConfig() method. 106 | * 107 | * @param {string} [airline] - The airline ICAO. Ex: "DAL" 108 | * @param {string} [bounds] - Coordinates (y1, y2 ,x1, x2). Ex: "75.78,-75.78,-427.56,427.56" 109 | * @param {string} [registration] - Aircraft registration 110 | * @param {string} [aircraftType] - Aircraft model code. Ex: "B737" 111 | * @param {boolean} [details] - If true, it returns flights with detailed information 112 | */ 113 | getFlights( 114 | airline?: string | null, 115 | bounds?: string | null, 116 | registration?: string | null, 117 | aircraftType?: string | null, 118 | details?: boolean, 119 | ): Promise; 120 | 121 | /** 122 | * Return a copy of the current config of the Real Time Flight Tracker, used by getFlights() method. 123 | */ 124 | getFlightTrackerConfig(): FlightTrackerConfig; 125 | 126 | /** 127 | * Download historical data of a flight. 128 | * 129 | * @param {Flight} flight - A Flight instance. 130 | * @param {string} fileType - Must be "CSV" or "KML" 131 | * @param {number} timestamp - A Unix timestamp 132 | */ 133 | getHistoryData( 134 | flight: Flight, 135 | fileType: string, 136 | timestamp: number, 137 | ): Promise; 138 | 139 | /** 140 | * Return the user data. 141 | */ 142 | getLoginData(): object; 143 | 144 | /** 145 | * Return the most tracked data. 146 | */ 147 | getMostTracked(): Promise; 148 | 149 | /** 150 | * Return boundaries of volcanic eruptions and ash clouds impacting aviation. 151 | */ 152 | getVolcanicEruptions(): Promise; 153 | 154 | /** 155 | * Return all major zones on the globe. 156 | */ 157 | getZones(): Promise; 158 | 159 | /** 160 | * Return the search result. 161 | * 162 | * @param {string} query 163 | * @param {number} [limit=50] 164 | */ 165 | search(query: string, limit?: number): Promise; 166 | 167 | /** 168 | * Check if the user is logged into the FlightRadar24 account. 169 | */ 170 | isLoggedIn(): boolean; 171 | 172 | /** 173 | * Log in to a FlightRadar24 account. 174 | * 175 | * @param {string} user - Your email. 176 | * @param {string} password - Your password. 177 | */ 178 | login(user: string, password: string): Promise; 179 | 180 | /** 181 | * Log out of the FlightRadar24 account. 182 | */ 183 | logout(): Promise; 184 | 185 | /** 186 | * Set config for the Real Time Flight Tracker, used by getFlights() method. 187 | * 188 | * @param {FlightTrackerConfig} [flightTrackerConfig] - If null, set to the default config. 189 | * @param {object} [config={}] - Config as an JSON object 190 | */ 191 | setFlightTrackerConfig( 192 | flightTrackerConfig: FlightTrackerConfig | null, 193 | config?: object, 194 | ): Promise; 195 | } 196 | 197 | /** 198 | * Data class with settings of the Real Time Flight Tracker. 199 | */ 200 | export class FlightTrackerConfig { 201 | faa: string; 202 | satellite: string; 203 | mlat: string; 204 | flarm: string; 205 | adsb: string; 206 | gnd: string; 207 | air: string; 208 | vehicles: string; 209 | estimated: string; 210 | maxage: string; 211 | gliders: string; 212 | stats: string; 213 | limit: string; 214 | 215 | /** 216 | * Constructor of FlighTrackerConfig class. 217 | * 218 | * @param {object} data 219 | */ 220 | constructor(data: object); 221 | } 222 | 223 | /** 224 | * Representation of a real entity, at some location. 225 | */ 226 | export class Entity { 227 | latitude: number | null; 228 | longitude: number | null; 229 | 230 | /** 231 | * Constructor of Entity class. 232 | * 233 | * @param {number} latitude 234 | * @param {number} longitude 235 | */ 236 | constructor(latitude?: number | null, longitude?: number | null); 237 | 238 | private __setPosition( 239 | latitude: number | null, 240 | longitude: number | null, 241 | ): void; 242 | 243 | private __getInfo(info: any, replaceBy?: any): any; 244 | 245 | /** 246 | * Return the distance from another entity (in kilometers). 247 | * 248 | * @param {Entity} entity 249 | * @return {number} 250 | */ 251 | getDistanceFrom(entity: Entity): number; 252 | } 253 | 254 | /** 255 | * Airport representation. 256 | */ 257 | export class Airport extends Entity { 258 | latitude: number; 259 | longitude: number; 260 | altitude: number; 261 | name: string; 262 | icao: string; 263 | iata: string; 264 | country: string; 265 | 266 | /** 267 | * Constructor of Airport class. 268 | * 269 | * The parameters below are optional. You can just create an Airport instance with no information 270 | * and use the setAirportDetails(...) method for having an instance with detailed information. 271 | * 272 | * @param {object} [basicInfo] - Basic information about the airport received from FlightRadar24 273 | * @param {object} [info] - Dictionary with more information about the airport received from FlightRadar24 274 | */ 275 | constructor(basicInfo?: object, info?: object); 276 | 277 | /** 278 | * Initialize instance with basic information about the airport. 279 | * 280 | * @param {object} basicInfo 281 | */ 282 | private __initializeWithBasicInfo(basicInfo: object): void; 283 | 284 | /** 285 | * Initialize instance with extra information about the airport. 286 | * 287 | * @param {object} info 288 | */ 289 | private __initializeWithInfo(info: object): void; 290 | 291 | /** 292 | * Set airport details to the instance. Use FlightRadar24API.getAirportDetails(...) method to get it. 293 | * 294 | * @param {object} airportDetails 295 | */ 296 | setAirportDetails(airportDetails: object): void; 297 | } 298 | 299 | /** 300 | * Flight representation. 301 | */ 302 | export class Flight extends Entity { 303 | latitude: number; 304 | longitude: number; 305 | altitude: number; 306 | id: string; 307 | aircraftCode: string; 308 | airlineIcao: string; 309 | airlineIata: string; 310 | callsign: string; 311 | destinationAirportIata: string; 312 | groundSpeed: number; 313 | heading: number; 314 | number: string; 315 | icao24bit: string; 316 | squawk: string; 317 | registration: string; 318 | time: number; 319 | originAirportIata: string; 320 | onGround: number; 321 | verticalSpeed: number; 322 | 323 | /** 324 | * Constructor of Flight class. 325 | * 326 | * @param {*} flightId - The flight ID specifically used by FlightRadar24 327 | * @param {*} info - Dictionary with received data from FlightRadar24 328 | */ 329 | constructor(flightId: string, info: object); 330 | 331 | /** 332 | * Check one or more flight information. 333 | * 334 | * You can use the prefix "max" or "min" in the parameter 335 | * to compare numeric data with ">" or "<". 336 | * 337 | * Example: checkInfo({minAltitude: 6700, maxAltitude: 13000, airlineIcao: "THY"}) 338 | * 339 | * @param {object} info 340 | */ 341 | checkInfo(info: object): boolean; 342 | 343 | /** 344 | * Return the formatted altitude, with the unit of measure. 345 | */ 346 | getAltitude(): string; 347 | 348 | /** 349 | * Return the formatted flight level, with the unit of measure. 350 | */ 351 | getFlightLevel(): string; 352 | 353 | /** 354 | * Return the formatted ground speed, with the unit of measure. 355 | */ 356 | getGroundSpeed(): string; 357 | 358 | /** 359 | * Return the formatted heading, with the unit of measure. 360 | */ 361 | getHeading(): string; 362 | 363 | /** 364 | * Return the formatted vertical speed, with the unit of measure. 365 | */ 366 | getVerticalSpeed(): string; 367 | 368 | /** 369 | * Set flight details to the instance. Use FlightRadar24API.getFlightDetails(...) method to get it. 370 | * 371 | * @param {object} flightDetails 372 | */ 373 | setFlightDetails(flightDetails: object): void; 374 | } 375 | 376 | export class AirportNotFoundError extends Error { 377 | constructor(message?: string); 378 | } 379 | 380 | export class CloudflareError extends Error { 381 | constructor(message?: string); 382 | } 383 | 384 | export class LoginError extends Error { 385 | constructor(message?: string); 386 | } 387 | 388 | export const author: string; 389 | export const version: string; -------------------------------------------------------------------------------- /nodejs/FlightRadar24/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Unofficial SDK for FlightRadar24. 3 | * 4 | * This SDK provides flight and airport data available to the public 5 | * on the FlightRadar24 website. 6 | * 7 | * See more information at: 8 | * https://www.flightradar24.com/premium/ 9 | * https://www.flightradar24.com/terms-and-conditions 10 | */ 11 | 12 | const {AirportNotFoundError, CloudflareError, LoginError} = require("./errors"); 13 | const FlightRadar24API = require("./api"); 14 | const FlightTrackerConfig = require("./flightTrackerConfig"); 15 | const Airport = require("./entities/airport"); 16 | const Entity = require("./entities/entity"); 17 | const Flight = require("./entities/flight"); 18 | 19 | const author = "Jean Loui Bernard Silva de Jesus"; 20 | const version = "1.3.33"; 21 | 22 | module.exports = { 23 | FlightRadar24API, 24 | FlightTrackerConfig, 25 | Airport, Entity, Flight, 26 | AirportNotFoundError, CloudflareError, LoginError, 27 | author, version, 28 | }; 29 | -------------------------------------------------------------------------------- /nodejs/FlightRadar24/request.js: -------------------------------------------------------------------------------- 1 | const {CloudflareError} = require("./errors"); 2 | 3 | const FormData = require("form-data"); 4 | const fetch = (...args) => import("node-fetch").then(({default: fetch}) => fetch(...args)); 5 | 6 | 7 | /** 8 | * Class to make requests to the FlightRadar24. 9 | */ 10 | class APIRequest { 11 | /** 12 | * Constructor of the APIRequest class. 13 | * 14 | * @param {string} [url] 15 | * @param {object} [params] 16 | * @param {object} [headers] 17 | * @param {object} [data] 18 | * @param {object} [cookies] 19 | * @param {object} [excludeStatusCodes=[]] 20 | */ 21 | constructor(url, params = null, headers = null, data = null, cookies = null, excludeStatusCodes = []) { 22 | this.requestParams = { 23 | "params": params, 24 | "headers": headers, 25 | "data": data, 26 | "cookies": cookies, 27 | }; 28 | 29 | this.requestMethod = data == null ? "GET" : "POST"; 30 | this.__excludeStatusCodes = excludeStatusCodes; 31 | 32 | if (params != null && Object.keys(params).length > 0) { 33 | url += "?"; 34 | 35 | for (const key in params) { 36 | if (Object.prototype.hasOwnProperty.call(params, key)) { // guard-for-in 37 | url += key + "=" + params[key] + "&"; 38 | } 39 | } 40 | url = url.slice(0, -1); 41 | } 42 | 43 | this.url = url; 44 | 45 | this.__response = {}; 46 | this.__content = null; 47 | } 48 | 49 | /** 50 | * Send the request and receive a response. 51 | * 52 | * @return {this} 53 | */ 54 | async receive() { 55 | const settings = { 56 | method: this.requestMethod, 57 | headers: this.requestParams["headers"], 58 | cookies: this.requestParams["cookies"], 59 | }; 60 | 61 | if (settings["method"] == "POST") { 62 | const formData = new FormData(); 63 | 64 | Object.entries(this.requestParams["data"]).forEach(([key, value]) => { 65 | formData.append(key, value); 66 | }); 67 | 68 | settings["body"] = formData; 69 | } 70 | 71 | this.__response = await fetch(this.url, settings); 72 | 73 | if (this.getStatusCode() == 520) { 74 | throw new CloudflareError( 75 | "An unexpected error has occurred. Perhaps you are making too many calls?", 76 | this.__response, 77 | ); 78 | } 79 | 80 | if (!this.__excludeStatusCodes.includes(this.getStatusCode())) { 81 | if (![200, 201, 202].includes(this.getStatusCode())) { 82 | throw new Error( 83 | "Received status code '" + 84 | this.getStatusCode() + ": " + 85 | this.__response.statusText + "' for the URL " + 86 | this.url, 87 | ); 88 | } 89 | } 90 | return this; 91 | } 92 | 93 | /** 94 | * Return the received content from the request. 95 | */ 96 | async getContent() { 97 | if (this.__content !== null) { 98 | return this.__content; 99 | } 100 | 101 | let contentType = this.getHeaders()["content-type"]; 102 | contentType = contentType == null ? "" : contentType; 103 | 104 | if (contentType.includes("application/json")) { 105 | this.__content = await this.__response.json(); 106 | } 107 | else if (contentType.includes("text")) { 108 | this.__content = await this.__response.text(); 109 | } 110 | else { 111 | this.__content = await this.__response.arrayBuffer(); 112 | } 113 | return this.__content; 114 | } 115 | 116 | /** 117 | * Return the received cookies from the request. 118 | */ 119 | getCookies() { 120 | const rawCookies = this.__response.headers.raw()["set-cookie"]; 121 | const cookies = {}; 122 | 123 | if (rawCookies == null) { 124 | return {}; 125 | } 126 | 127 | rawCookies.forEach((string) => { 128 | const keyAndValue = string.split(";")[0].split("="); 129 | cookies[keyAndValue[0]] = keyAndValue[1]; 130 | }); 131 | 132 | return cookies; 133 | } 134 | 135 | /** 136 | * Return the headers of the response. 137 | */ 138 | getHeaders() { 139 | const headersAsDict = {}; 140 | 141 | this.__response.headers.forEach((value, key) => { 142 | headersAsDict[key] = value; 143 | }); 144 | return headersAsDict; 145 | } 146 | 147 | /** 148 | * Return the received response object. 149 | */ 150 | getResponseObject() { 151 | return this.__response; 152 | } 153 | 154 | /** 155 | * Return the status code of the response. 156 | */ 157 | getStatusCode() { 158 | return this.__response.status; 159 | } 160 | } 161 | 162 | module.exports = APIRequest; 163 | -------------------------------------------------------------------------------- /nodejs/FlightRadar24/util.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Check if the string is an integer 3 | * 4 | * @param {string} text 5 | * @return {boolean} 6 | */ 7 | function isNumeric(text) { 8 | if (text.length === 0) { 9 | return false; 10 | } 11 | 12 | for (let index = 0; index < text.length; index++) { 13 | if (!"0123456789".includes(text[index])) { 14 | return false; 15 | } 16 | } 17 | return true; 18 | } 19 | 20 | module.exports = {isNumeric}; 21 | -------------------------------------------------------------------------------- /nodejs/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jean Loui Bernard Silva de Jesus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /nodejs/README.md: -------------------------------------------------------------------------------- 1 | # FlightRadarAPI 2 | Unofficial SDK for [FlightRadar24](https://www.flightradar24.com/) for Python 3 and Node.js. 3 | 4 | This SDK should only be used for your own educational purposes. If you are interested in accessing Flightradar24 data commercially, please contact business@fr24.com. See more information at [Flightradar24's terms and conditions](https://www.flightradar24.com/terms-and-conditions). 5 | 6 | **Official FR24 API**: https://fr24api.flightradar24.com/ 7 | 8 | [![Node.js Package](https://github.com/JeanExtreme002/FlightRadarAPI/actions/workflows/node-package.yml/badge.svg)](https://github.com/JeanExtreme002/FlightRadarAPI/actions) 9 | [![Pypi](https://img.shields.io/pypi/v/FlightRadarAPI?logo=pypi)](https://pypi.org/project/FlightRadarAPI/) 10 | [![License](https://img.shields.io/pypi/l/FlightRadarAPI)](https://github.com/JeanExtreme002/FlightRadarAPI) 11 | [![Python Version](https://img.shields.io/badge/python-3.7+-8A2BE2)](https://pypi.org/project/FlightRadarAPI/) 12 | [![Npm](https://img.shields.io/npm/v/flightradarapi?logo=npm&color=red)](https://www.npmjs.com/package/flightradarapi) 13 | [![Downloads](https://static.pepy.tech/personalized-badge/flightradarapi?period=total&units=international_system&left_color=grey&right_color=orange&left_text=downloads)](https://pypi.org/project/FlightRadarAPI/) 14 | [![Frequency](https://img.shields.io/pypi/dm/flightradarapi?style=flat&label=frequency)](https://pypi.org/project/FlightRadarAPI/) 15 | 16 | ## Installing FlightRadarAPI: 17 | ``` 18 | $ npm install flightradarapi 19 | ``` 20 | 21 | ## Basic Usage: 22 | 23 | Import the class `FlightRadar24API` and create an instance of it. 24 | ```javascript 25 | const { FlightRadar24API } = require("flightradarapi"); 26 | const frApi = new FlightRadar24API(); 27 | ``` 28 | 29 | **Getting flights list:** 30 | ```javascript 31 | let flights = await frApi.getFlights(...); // Returns a list of Flight objects 32 | ``` 33 | 34 | **Getting airports list:** 35 | ```javascript 36 | let airports = await frApi.getAirports(...); // Returns a list of Airport objects 37 | ``` 38 | 39 | **Getting airlines list:** 40 | ```javascript 41 | let airlines = await frApi.getAirlines(); 42 | ``` 43 | 44 | **Getting zones list:** 45 | ```javascript 46 | let zones = await frApi.getZones(); 47 | ``` 48 | 49 | ## Documentation 50 | Explore the documentation of FlightRadarAPI package, for Python or NodeJS, through [this site](https://JeanExtreme002.github.io/FlightRadarAPI/). 51 | -------------------------------------------------------------------------------- /nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flightradarapi", 3 | "version": "1.3.33", 4 | "description": "SDK for FlightRadar24", 5 | "main": "./FlightRadar24/index.js", 6 | "scripts": { 7 | "test": "mocha tests --timeout 10000", 8 | "lint": "eslint ." 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/JeanExtreme002/FlightRadarAPI.git" 13 | }, 14 | "keywords": [ 15 | "flightradar24", 16 | "api", 17 | "radar", 18 | "aviation", 19 | "flights", 20 | "airports", 21 | "airlines", 22 | "aircraft" 23 | ], 24 | "author": "Jean Loui Bernard Silva de Jesus", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/JeanExtreme002/FlightRadarAPI/issues" 28 | }, 29 | "homepage": "https://github.com/JeanExtreme002/FlightRadarAPI#readme", 30 | "dependencies": { 31 | "form-data": "^4.0.0", 32 | "node-fetch": "^3.3.2" 33 | }, 34 | "devDependencies": { 35 | "chai": "^4.3.10", 36 | "eslint": "^8.56.0", 37 | "eslint-config-google": "^0.14.0", 38 | "mocha": "^10.2.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /nodejs/tests/testApi.js: -------------------------------------------------------------------------------- 1 | const {FlightRadar24API, version} = require(".."); 2 | const expect = require("chai").expect; 3 | 4 | 5 | describe("Testing FlightRadarAPI version " + version, function() { 6 | const frApi = new FlightRadar24API(); 7 | 8 | describe("Getting Airlines", function() { 9 | const expected = 100; 10 | 11 | it("Expected at least " + expected + " airlines.", async function() { 12 | const results = await frApi.getAirlines(); 13 | expect(results.length).to.be.above(expected - 1); 14 | }); 15 | }); 16 | 17 | describe("Getting Airport By IATA", function() { 18 | const expected = ["ATL", "LAX", "DXB", "DFW"]; 19 | 20 | it("Expected finding the following airports: " + expected.join(", ") + ".", async function() { 21 | for (const iata of expected) { 22 | const airport = await frApi.getAirport(iata); 23 | expect(airport.iata).to.be.equal(iata); 24 | } 25 | }); 26 | }); 27 | 28 | describe("Getting Airport Details", function() { 29 | const expected = ["ATL", "LAX", "DXB", "DFW"]; 30 | const targetKeys = ["airport", "airlines", "aircraftImages"]; 31 | 32 | it("Expected getting details of the following airports: " + expected.join(", ") + ".", async function() { 33 | for (const iata of expected) { 34 | const details = await frApi.getAirportDetails(iata, 1); 35 | expect(details).to.include.all.keys(targetKeys); 36 | expect(details["airport"]["pluginData"]).to.include.all.keys("details"); 37 | } 38 | }); 39 | }); 40 | 41 | describe("Getting Airports", function() { 42 | const expected = 1000; 43 | 44 | it("Expected at least " + expected + " airports.", async function() { 45 | const results = await frApi.getAirports(); 46 | expect(results.length).to.be.above(expected - 1); 47 | }); 48 | }); 49 | 50 | describe("Getting Zones", function() { 51 | const expected = 5; 52 | const targetKeys = ["tl_y", "tl_x", "br_y", "br_x"]; 53 | 54 | it("Expected at least " + expected + " zones.", async function() { 55 | const results = await frApi.getZones(); 56 | expect(Object.entries(results).length).to.be.above(expected - 1); 57 | 58 | for (const key in results) { 59 | if (Object.prototype.hasOwnProperty.call(results, key)) { // guard-for-in 60 | const zone = results[key]; 61 | expect(zone).to.include.all.keys(targetKeys); 62 | } 63 | } 64 | }); 65 | }); 66 | 67 | describe("Getting Flights", function() { 68 | const expected = 100; 69 | 70 | it("Expected at least " + expected + " flights.", async function() { 71 | const results = await frApi.getFlights(); 72 | expect(results.length).to.be.above(expected - 1); 73 | }); 74 | }); 75 | 76 | describe("Getting Flight Details", function() { 77 | const targetKeys = ["airport", "airline", "aircraft", "time", "status", "trail"]; 78 | 79 | it("Expected getting the following information: " + targetKeys.join(", ") + ".", async function() { 80 | const flights = await frApi.getFlights(); 81 | const middle = Math.trunc(flights.length / 2); 82 | 83 | const someFlights = flights.slice(middle - 2, middle + 2); 84 | 85 | for (const flight of someFlights) { 86 | const details = await frApi.getFlightDetails(flight); 87 | expect(details).to.include.all.keys(targetKeys); 88 | } 89 | }); 90 | }); 91 | 92 | describe("Getting Flights by Airline", function() { 93 | const expected = 3; 94 | const targetAirlines = ["SWA", "GLO", "AZU", "UAL", "THY"]; 95 | 96 | let message = "Expected getting flights from at least " + expected; 97 | message += " of the following airlines: " + targetAirlines.join(", ") + "."; 98 | 99 | it(message, async function() { 100 | let count = 0; 101 | 102 | for (const airline of targetAirlines) { 103 | const flights = await frApi.getFlights(airline); 104 | 105 | for (const flight of flights) { 106 | expect(flight.airlineIcao).to.be.equal(airline); 107 | } 108 | 109 | count = flights.length > 0 ? count + 1 : count; 110 | } 111 | expect(count).to.be.above(expected - 1); 112 | }); 113 | }); 114 | 115 | describe("Getting Flights by Bounds", function() { 116 | const expected = 30; 117 | const targetZones = ["northamerica", "southamerica"]; 118 | 119 | it("Expected at least " + expected + " flights at: " + targetZones.join(", ") + ".", async function() { 120 | const zones = await frApi.getZones(); 121 | 122 | for (const zoneName of targetZones) { 123 | const zone = zones[zoneName]; 124 | 125 | const bounds = frApi.getBounds(zone); 126 | const flights = await frApi.getFlights(null, bounds); 127 | 128 | for (const flight of flights) { 129 | expect(flight.latitude).to.be.below(zone["tl_y"]); 130 | expect(flight.latitude).to.be.above(zone["br_y"]); 131 | 132 | expect(flight.longitude).to.be.below(zone["br_x"]); 133 | expect(flight.latitude).to.be.above(zone["tl_x"]); 134 | } 135 | expect(flights.length).to.be.above(expected - 1); 136 | } 137 | }); 138 | }); 139 | 140 | describe("Getting Airline Logo", function() { 141 | const targetAirlines = [["WN", "SWA"], ["G3", "GLO"], ["AD", "AZU"], ["AA", "AAL"], ["TK", "THY"]]; 142 | const expected = targetAirlines.length * 0.8; 143 | 144 | const icao = []; 145 | 146 | for (const airline of targetAirlines) { 147 | icao.push(airline[1]); 148 | } 149 | 150 | let message = "Expected getting logos from at least " + Math.trunc(expected); 151 | message += " of the following airlines: " + icao.join(", ") + "."; 152 | 153 | it(message, async function() { 154 | let found = 0; 155 | 156 | for (const airline of targetAirlines) { 157 | const result = await frApi.getAirlineLogo(airline[0], airline[1]); 158 | found = result != null && result[0].byteLength > 512 ? found + 1 : found; 159 | } 160 | expect(found).to.be.above(expected - 1); 161 | }); 162 | }); 163 | 164 | describe("Getting Country Flag", function() { 165 | const targetCountries = ["United States", "Brazil", "Egypt", "Japan", "South Korea", "Canada"]; 166 | const expected = targetCountries.length * 0.8; 167 | 168 | let message = "Expected getting flags from at least " + Math.trunc(expected); 169 | message += " of the following countries: " + targetCountries.join(", ") + "."; 170 | 171 | it(message, async function() { 172 | let found = 0; 173 | 174 | for (const country of targetCountries) { 175 | const result = await frApi.getCountryFlag(country); 176 | found = result != null && result[0].byteLength > 512 ? found + 1 : found; 177 | } 178 | expect(found).to.be.above(expected - 1); 179 | }); 180 | }); 181 | 182 | describe("Getting Bounds by Point", function() { 183 | const expected = "52.58594974202871,52.54997688140807,13.253064418048115,13.3122478541492"; 184 | 185 | it("Formula for calculating bounds is correct.", async function() { 186 | const bounds = frApi.getBoundsByPoint(52.567967, 13.282644, 2000); 187 | expect(bounds).to.be.equal(expected); 188 | }); 189 | }); 190 | }); 191 | -------------------------------------------------------------------------------- /python/.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | 3 | max-line-length = 130 4 | ignore = W503, E701, E722 5 | 6 | per-file-ignores = 7 | # __init__.py files are allowed to have unused imports and lines-too-long 8 | */__init__.py:F401 9 | 10 | # Unused imports are allowed in the tests/package.py module and they must come after setting the current working directory. 11 | tests/package.py:F401, E402 -------------------------------------------------------------------------------- /python/FlightRadar24/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | Unofficial SDK for FlightRadar24. 5 | 6 | This SDK provides flight and airport data available to the public 7 | on the FlightRadar24 website. 8 | 9 | See more information at: 10 | https://www.flightradar24.com/premium/ 11 | https://www.flightradar24.com/terms-and-conditions 12 | """ 13 | 14 | __author__ = "Jean Loui Bernard Silva de Jesus" 15 | __version__ = "1.3.34" 16 | 17 | from .api import FlightRadar24API, FlightTrackerConfig 18 | from .entities import Airport, Entity, Flight 19 | -------------------------------------------------------------------------------- /python/FlightRadar24/api.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from typing import Any, Dict, List, Optional, Tuple, Union 4 | 5 | import dataclasses 6 | import math 7 | 8 | from .core import Core 9 | from .entities.airport import Airport 10 | from .entities.flight import Flight 11 | from .errors import AirportNotFoundError, LoginError 12 | from .request import APIRequest 13 | 14 | 15 | @dataclasses.dataclass 16 | class FlightTrackerConfig(object): 17 | """ 18 | Data class with settings of the Real Time Flight Tracker. 19 | """ 20 | faa: str = "1" 21 | satellite: str = "1" 22 | mlat: str = "1" 23 | flarm: str = "1" 24 | adsb: str = "1" 25 | gnd: str = "1" 26 | air: str = "1" 27 | vehicles: str = "1" 28 | estimated: str = "1" 29 | maxage: str = "14400" 30 | gliders: str = "1" 31 | stats: str = "1" 32 | limit: str = "5000" 33 | 34 | 35 | class FlightRadar24API(object): 36 | """ 37 | Main class of the FlightRadarAPI 38 | """ 39 | 40 | def __init__(self, user: Optional[str] = None, password: Optional[str] = None, timeout: int = 10): 41 | """ 42 | Constructor of the FlightRadar24API class. 43 | 44 | :param user: Your email (optional) 45 | :param password: Your password (optional) 46 | """ 47 | self.__flight_tracker_config = FlightTrackerConfig() 48 | self.__login_data: Optional[Dict] = None 49 | 50 | self.timeout: int = timeout 51 | 52 | if user is not None and password is not None: 53 | self.login(user, password) 54 | 55 | def get_airlines(self) -> List[Dict]: 56 | """ 57 | Return a list with all airlines. 58 | """ 59 | response = APIRequest(Core.airlines_data_url, headers=Core.json_headers, timeout=self.timeout) 60 | return response.get_content()["rows"] 61 | 62 | def get_airline_logo(self, iata: str, icao: str) -> Optional[Tuple[bytes, str]]: 63 | """ 64 | Download the logo of an airline from FlightRadar24 and return it as bytes. 65 | """ 66 | iata, icao = iata.upper(), icao.upper() 67 | 68 | first_logo_url = Core.airline_logo_url.format(iata, icao) 69 | 70 | # Try to get the image by the first URL option. 71 | response = APIRequest(first_logo_url, headers=Core.image_headers, exclude_status_codes=[403,], timeout=self.timeout) 72 | status_code = response.get_status_code() 73 | 74 | if not str(status_code).startswith("4"): 75 | return response.get_content(), first_logo_url.split(".")[-1] 76 | 77 | # Get the image by the second airline logo URL. 78 | second_logo_url = Core.alternative_airline_logo_url.format(icao) 79 | 80 | response = APIRequest(second_logo_url, headers=Core.image_headers, timeout=self.timeout) 81 | status_code = response.get_status_code() 82 | 83 | if not str(status_code).startswith("4"): 84 | return response.get_content(), second_logo_url.split(".")[-1] 85 | 86 | def get_airport(self, code: str, *, details: bool = False) -> Airport: 87 | """ 88 | Return basic information about a specific airport. 89 | 90 | :param code: ICAO or IATA of the airport 91 | :param details: If True, it returns an Airport instance with detailed information. 92 | """ 93 | if 4 < len(code) or len(code) < 3: 94 | raise ValueError(f"The code '{code}' is invalid. It must be the IATA or ICAO of the airport.") 95 | 96 | if details: 97 | airport = Airport() 98 | 99 | airport_details = self.get_airport_details(code) 100 | airport.set_airport_details(airport_details) 101 | 102 | return airport 103 | 104 | response = APIRequest(Core.airport_data_url.format(code), headers=Core.json_headers, timeout=self.timeout) 105 | content = response.get_content() 106 | 107 | if not content or not isinstance(content, dict) or not content.get("details"): 108 | raise AirportNotFoundError(f"Could not find an airport by the code '{code}'.") 109 | 110 | return Airport(info=content["details"]) 111 | 112 | def get_airport_details(self, code: str, flight_limit: int = 100, page: int = 1) -> Dict: 113 | """ 114 | Return the airport details from FlightRadar24. 115 | 116 | :param code: ICAO or IATA of the airport 117 | :param flight_limit: Limit of flights related to the airport 118 | :param page: Page of result to display 119 | """ 120 | if 4 < len(code) or len(code) < 3: 121 | raise ValueError(f"The code '{code}' is invalid. It must be the IATA or ICAO of the airport.") 122 | 123 | request_params = {"format": "json"} 124 | 125 | if self.__login_data is not None: 126 | request_params["token"] = self.__login_data["cookies"]["_frPl"] 127 | 128 | # Insert the method parameters into the dictionary for the request. 129 | request_params["code"] = code 130 | request_params["limit"] = flight_limit 131 | request_params["page"] = page 132 | 133 | # Request details from the FlightRadar24. 134 | response = APIRequest(Core.api_airport_data_url, request_params, Core.json_headers, exclude_status_codes=[400,], timeout=self.timeout) 135 | content: Dict = response.get_content() 136 | 137 | if response.get_status_code() == 400 and content.get("errors"): 138 | errors = content["errors"]["errors"]["parameters"] 139 | 140 | if errors.get("limit"): 141 | raise ValueError(errors["limit"]["notBetween"]) 142 | 143 | raise AirportNotFoundError(f"Could not find an airport by the code '{code}'.", errors) 144 | 145 | result = content["result"]["response"] 146 | 147 | # Check whether it received data of an airport. 148 | data = result.get("airport", dict()).get("pluginData", dict()) 149 | 150 | if "details" not in data and len(data.get("runways", [])) == 0 and len(data) <= 3: 151 | raise AirportNotFoundError(f"Could not find an airport by the code '{code}'.") 152 | 153 | # Return the airport details. 154 | return result 155 | 156 | def get_airport_disruptions(self) -> Dict: 157 | """ 158 | Return airport disruptions. 159 | """ 160 | response = APIRequest(Core.airport_disruptions_url, headers=Core.json_headers, timeout=self.timeout) 161 | return response.get_content() 162 | 163 | def get_airports(self) -> List[Airport]: 164 | """ 165 | Return a list with all airports. 166 | """ 167 | response = APIRequest(Core.airports_data_url, headers=Core.json_headers, timeout=self.timeout) 168 | 169 | airports: List[Airport] = list() 170 | 171 | for airport_data in response.get_content()["rows"]: 172 | airport = Airport(basic_info=airport_data) 173 | airports.append(airport) 174 | 175 | return airports 176 | 177 | def get_bookmarks(self) -> Dict: 178 | """ 179 | Get the bookmarks from the FlightRadar24 account. 180 | """ 181 | if not self.is_logged_in(): 182 | raise LoginError("You must log in to your account.") 183 | 184 | headers = Core.json_headers.copy() 185 | headers["accesstoken"] = self.get_login_data()["accessToken"] 186 | 187 | cookies = self.__login_data["cookies"] 188 | 189 | response = APIRequest(Core.bookmarks_url, headers=headers, cookies=cookies, timeout=self.timeout) 190 | return response.get_content() 191 | 192 | def get_bounds(self, zone: Dict[str, float]) -> str: 193 | """ 194 | Convert coordinate dictionary to a string "y1, y2, x1, x2". 195 | 196 | :param zone: Dictionary containing the following keys: tl_y, tl_x, br_y, br_x 197 | """ 198 | return "{},{},{},{}".format(zone["tl_y"], zone["br_y"], zone["tl_x"], zone["br_x"]) 199 | 200 | def get_bounds_by_point(self, latitude: float, longitude: float, radius: float) -> str: 201 | """ 202 | Convert a point coordinate and a radius to a string "y1, y2, x1, x2". 203 | 204 | :param latitude: Latitude of the point 205 | :param longitude: Longitude of the point 206 | :param radius: Radius in meters to create area around the point 207 | """ 208 | half_side_in_km = abs(radius) / 1000 209 | 210 | lat = math.radians(latitude) 211 | lon = math.radians(longitude) 212 | 213 | approx_earth_radius = 6371 214 | hypotenuse_distance = math.sqrt(2 * (math.pow(half_side_in_km, 2))) 215 | 216 | lat_min = math.asin( 217 | math.sin(lat) * math.cos(hypotenuse_distance / approx_earth_radius) 218 | + math.cos(lat) 219 | * math.sin(hypotenuse_distance / approx_earth_radius) 220 | * math.cos(225 * (math.pi / 180)), 221 | ) 222 | lon_min = lon + math.atan2( 223 | math.sin(225 * (math.pi / 180)) 224 | * math.sin(hypotenuse_distance / approx_earth_radius) 225 | * math.cos(lat), 226 | math.cos(hypotenuse_distance / approx_earth_radius) 227 | - math.sin(lat) * math.sin(lat_min), 228 | ) 229 | 230 | lat_max = math.asin( 231 | math.sin(lat) * math.cos(hypotenuse_distance / approx_earth_radius) 232 | + math.cos(lat) 233 | * math.sin(hypotenuse_distance / approx_earth_radius) 234 | * math.cos(45 * (math.pi / 180)), 235 | ) 236 | lon_max = lon + math.atan2( 237 | math.sin(45 * (math.pi / 180)) 238 | * math.sin(hypotenuse_distance / approx_earth_radius) 239 | * math.cos(lat), 240 | math.cos(hypotenuse_distance / approx_earth_radius) 241 | - math.sin(lat) * math.sin(lat_max), 242 | ) 243 | 244 | rad2deg = math.degrees 245 | 246 | zone = { 247 | "tl_y": rad2deg(lat_max), 248 | "br_y": rad2deg(lat_min), 249 | "tl_x": rad2deg(lon_min), 250 | "br_x": rad2deg(lon_max) 251 | } 252 | return self.get_bounds(zone) 253 | 254 | def get_country_flag(self, country: str) -> Optional[Tuple[bytes, str]]: 255 | """ 256 | Download the flag of a country from FlightRadar24 and return it as bytes. 257 | 258 | :param country: Country name 259 | """ 260 | flag_url = Core.country_flag_url.format(country.lower().replace(" ", "-")) 261 | headers = Core.image_headers.copy() 262 | 263 | if "origin" in headers: 264 | headers.pop("origin") # Does not work for this request. 265 | 266 | response = APIRequest(flag_url, headers=headers, timeout=self.timeout) 267 | status_code = response.get_status_code() 268 | 269 | if not str(status_code).startswith("4"): 270 | return response.get_content(), flag_url.split(".")[-1] 271 | 272 | def get_flight_details(self, flight: Flight) -> Dict[Any, Any]: 273 | """ 274 | Return the flight details from Data Live FlightRadar24. 275 | 276 | :param flight: A Flight instance 277 | """ 278 | response = APIRequest(Core.flight_data_url.format(flight.id), headers=Core.json_headers, timeout=self.timeout) 279 | return response.get_content() 280 | 281 | def get_flights( 282 | self, 283 | airline: Optional[str] = None, 284 | bounds: Optional[str] = None, 285 | registration: Optional[str] = None, 286 | aircraft_type: Optional[str] = None, 287 | *, 288 | details: bool = False 289 | ) -> List[Flight]: 290 | """ 291 | Return a list of flights. See more options at set_flight_tracker_config() method. 292 | 293 | :param airline: The airline ICAO. Ex: "DAL" 294 | :param bounds: Coordinates (y1, y2 ,x1, x2). Ex: "75.78,-75.78,-427.56,427.56" 295 | :param registration: Aircraft registration 296 | :param aircraft_type: Aircraft model code. Ex: "B737" 297 | :param details: If True, it returns flights with detailed information 298 | """ 299 | request_params = dataclasses.asdict(self.__flight_tracker_config) 300 | 301 | if self.__login_data is not None: 302 | request_params["enc"] = self.__login_data["cookies"]["_frPl"] 303 | 304 | # Insert the method parameters into the dictionary for the request. 305 | if airline: request_params["airline"] = airline 306 | if bounds: request_params["bounds"] = bounds.replace(",", "%2C") 307 | if registration: request_params["reg"] = registration 308 | if aircraft_type: request_params["type"] = aircraft_type 309 | 310 | # Get all flights from Data Live FlightRadar24. 311 | response = APIRequest(Core.real_time_flight_tracker_data_url, request_params, Core.json_headers, timeout=self.timeout) 312 | response = response.get_content() 313 | 314 | flights: List[Flight] = list() 315 | 316 | for flight_id, flight_info in response.items(): 317 | 318 | # Get flights only. 319 | if not flight_id[0].isnumeric(): 320 | continue 321 | 322 | flight = Flight(flight_id, flight_info) 323 | flights.append(flight) 324 | 325 | # Set flight details. 326 | if details: 327 | flight_details = self.get_flight_details(flight) 328 | flight.set_flight_details(flight_details) 329 | 330 | return flights 331 | 332 | def get_flight_tracker_config(self) -> FlightTrackerConfig: 333 | """ 334 | Return a copy of the current config of the Real Time Flight Tracker, used by get_flights() method. 335 | """ 336 | return dataclasses.replace(self.__flight_tracker_config) 337 | 338 | def get_history_data(self, flight: Flight, file_type: str, timestamp: int) -> Dict: 339 | """ 340 | Download historical data of a flight. 341 | 342 | :param flight: A Flight instance 343 | :param file_type: Must be "CSV" or "KML" 344 | :param timestamp: A Unix timestamp 345 | """ 346 | if not self.is_logged_in(): 347 | raise LoginError("You must log in to your account.") 348 | 349 | file_type = file_type.lower() 350 | 351 | if file_type not in ["csv", "kml"]: 352 | raise ValueError(f"File type '{file_type}' is not supported. Only CSV and KML are supported.") 353 | 354 | response = APIRequest( 355 | Core.historical_data_url.format(flight.id, file_type, timestamp), 356 | headers=Core.json_headers, cookies=self.__login_data["cookies"], 357 | timeout=self.timeout 358 | ) 359 | 360 | content = response.get_content() 361 | return str(content.decode("utf-8")) 362 | 363 | def get_login_data(self) -> Dict[Any, Any]: 364 | """ 365 | Return the user data. 366 | """ 367 | if not self.is_logged_in(): 368 | raise LoginError("You must log in to your account.") 369 | 370 | return self.__login_data["userData"].copy() 371 | 372 | def get_most_tracked(self) -> Dict: 373 | """ 374 | Return the most tracked data. 375 | """ 376 | response = APIRequest(Core.most_tracked_url, headers=Core.json_headers, timeout=self.timeout) 377 | return response.get_content() 378 | 379 | def get_volcanic_eruptions(self) -> Dict: 380 | """ 381 | Return boundaries of volcanic eruptions and ash clouds impacting aviation. 382 | """ 383 | response = APIRequest(Core.volcanic_eruption_data_url, headers=Core.json_headers, timeout=self.timeout) 384 | return response.get_content() 385 | 386 | def get_zones(self) -> Dict[str, Dict]: 387 | """ 388 | Return all major zones on the globe. 389 | """ 390 | response = APIRequest(Core.zones_data_url, headers=Core.json_headers, timeout=self.timeout) 391 | zones = response.get_content() 392 | 393 | if "version" in zones: 394 | zones.pop("version") 395 | 396 | return zones 397 | 398 | def search(self, query: str, limit: int = 50) -> Dict: 399 | """ 400 | Return the search result. 401 | """ 402 | response = APIRequest(Core.search_data_url.format(query, limit), headers=Core.json_headers, timeout=self.timeout) 403 | results = response.get_content().get("results", []) 404 | stats = response.get_content().get("stats", {}) 405 | 406 | i = 0 407 | counted_total = 0 408 | data = {} 409 | for name, count in stats.get("count", {}).items(): 410 | data[name] = [] 411 | while i < counted_total + count and i < len(results): 412 | data[name].append(results[i]) 413 | i += 1 414 | counted_total += count 415 | return data 416 | 417 | def is_logged_in(self) -> bool: 418 | """ 419 | Check if the user is logged into the FlightRadar24 account. 420 | """ 421 | return self.__login_data is not None 422 | 423 | def login(self, user: str, password: str) -> None: 424 | """ 425 | Log in to a FlightRadar24 account. 426 | 427 | :param user: Your email. 428 | :param password: Your password. 429 | """ 430 | data = { 431 | "email": user, 432 | "password": password, 433 | "remember": "true", 434 | "type": "web" 435 | } 436 | 437 | response = APIRequest(Core.user_login_url, headers=Core.json_headers, data=data, timeout=self.timeout) 438 | status_code = response.get_status_code() 439 | content = response.get_content() 440 | 441 | if not str(status_code).startswith("2") or not content.get("success"): 442 | if isinstance(content, dict): raise LoginError(content["message"]) 443 | else: raise LoginError("Your email or password is incorrect") 444 | 445 | self.__login_data = { 446 | "userData": content["userData"], 447 | "cookies": response.get_cookies(), 448 | } 449 | 450 | def logout(self) -> bool: 451 | """ 452 | Log out of the FlightRadar24 account. 453 | 454 | Return a boolean indicating that it successfully logged out of the server. 455 | """ 456 | if self.__login_data is None: return True 457 | 458 | cookies = self.__login_data["cookies"] 459 | self.__login_data = None 460 | 461 | response = APIRequest(Core.user_login_url, headers=Core.json_headers, cookies=cookies, timeout=self.timeout) 462 | return str(response.get_status_code()).startswith("2") 463 | 464 | def set_flight_tracker_config( 465 | self, 466 | flight_tracker_config: Optional[FlightTrackerConfig] = None, 467 | **config: Union[int, str] 468 | ) -> None: 469 | """ 470 | Set config for the Real Time Flight Tracker, used by get_flights() method. 471 | """ 472 | if flight_tracker_config is not None: 473 | self.__flight_tracker_config = flight_tracker_config 474 | 475 | current_config_dict = dataclasses.asdict(self.__flight_tracker_config) 476 | 477 | for key, value in config.items(): 478 | value = str(value) 479 | 480 | if key not in current_config_dict: 481 | raise KeyError(f"Unknown option: '{key}'") 482 | 483 | if not value.isdecimal(): 484 | raise TypeError(f"Value must be a decimal. Got '{key}'") 485 | 486 | setattr(self.__flight_tracker_config, key, value) 487 | -------------------------------------------------------------------------------- /python/FlightRadar24/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from abc import ABC 4 | 5 | 6 | class Core(ABC): 7 | 8 | # Base URLs. 9 | api_flightradar_base_url = "https://api.flightradar24.com/common/v1" 10 | cdn_flightradar_base_url = "https://cdn.flightradar24.com" 11 | flightradar_base_url = "https://www.flightradar24.com" 12 | data_live_base_url = "https://data-live.flightradar24.com" 13 | data_cloud_base_url = "https://data-cloud.flightradar24.com" 14 | 15 | # User login URL. 16 | user_login_url = flightradar_base_url + "/user/login" 17 | user_logout_url = flightradar_base_url + "/user/logout" 18 | 19 | # Search data URL 20 | search_data_url = flightradar_base_url + "/v1/search/web/find?query={}&limit={}" 21 | 22 | # Flights data URLs. 23 | real_time_flight_tracker_data_url = data_cloud_base_url + "/zones/fcgi/feed.js" 24 | flight_data_url = data_live_base_url + "/clickhandler/?flight={}" 25 | 26 | # Historical data URL. 27 | historical_data_url = flightradar_base_url + "/download/?flight={}&file={}&trailLimit=0&history={}" 28 | 29 | # Airports data URLs. 30 | api_airport_data_url = api_flightradar_base_url + "/airport.json" 31 | airport_data_url = flightradar_base_url + "/airports/traffic-stats/?airport={}" 32 | airports_data_url = flightradar_base_url + "/_json/airports.php" 33 | 34 | # Airlines data URL. 35 | airlines_data_url = flightradar_base_url + "/_json/airlines.php" 36 | 37 | # Zones data URL. 38 | zones_data_url = flightradar_base_url + "/js/zones.js.php" 39 | 40 | # Weather data URL. 41 | volcanic_eruption_data_url = flightradar_base_url + "/weather/volcanic" 42 | 43 | # Most tracked URL 44 | most_tracked_url = flightradar_base_url + "/flights/most-tracked" 45 | 46 | # Airport disruptions URL. 47 | airport_disruptions_url = flightradar_base_url + "/webapi/v1/airport-disruptions" 48 | 49 | # Bookmarks URL. 50 | bookmarks_url = flightradar_base_url + "/webapi/v1/bookmarks" 51 | 52 | # Country flag image URL. 53 | country_flag_url = flightradar_base_url + "/static/images/data/flags-small/{}.svg" 54 | 55 | # Airline logo image URL. 56 | airline_logo_url = cdn_flightradar_base_url + "/assets/airlines/logotypes/{}_{}.png" 57 | alternative_airline_logo_url = flightradar_base_url + "/static/images/data/operators/{}_logo0.png" 58 | 59 | headers = { 60 | "accept-encoding": "gzip, br", 61 | "accept-language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7", 62 | "cache-control": "max-age=0", 63 | "origin": "https://www.flightradar24.com", 64 | "referer": "https://www.flightradar24.com/", 65 | "sec-fetch-dest": "empty", 66 | "sec-fetch-mode": "cors", 67 | "sec-fetch-site": "same-site", 68 | "user-agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36" 69 | } 70 | 71 | json_headers = headers.copy() 72 | json_headers["accept"] = "application/json" 73 | 74 | image_headers = headers.copy() 75 | image_headers["accept"] = "image/gif, image/jpg, image/jpeg, image/png" 76 | -------------------------------------------------------------------------------- /python/FlightRadar24/entities/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from .airport import Airport 4 | from .entity import Entity 5 | from .flight import Flight 6 | -------------------------------------------------------------------------------- /python/FlightRadar24/entities/airport.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from typing import Any, Dict, Optional 4 | from .entity import Entity 5 | 6 | 7 | class Airport(Entity): 8 | """ 9 | Airport representation. 10 | """ 11 | def __init__(self, basic_info: Dict = dict(), info: Dict = dict()): 12 | """ 13 | Constructor of the Airport class. 14 | 15 | The parameters below are optional. You can just create an Airport instance with no information 16 | and use the set_airport_details(...) method for having an instance with detailed information. 17 | 18 | :param basic_info: Basic information about the airport received from FlightRadar24 19 | :param info: Dictionary with more information about the airport received from FlightRadar24 20 | """ 21 | if basic_info: self.__initialize_with_basic_info(basic_info) 22 | if info: self.__initialize_with_info(info) 23 | 24 | def __repr__(self) -> str: 25 | template = "<({}) {} - Altitude: {} - Latitude: {} - Longitude: {}>" 26 | return template.format(self.icao, self.name, self.altitude, self.latitude, self.longitude) 27 | 28 | def __str__(self) -> str: 29 | return self.__repr__() 30 | 31 | def __get_info(self, info: Any, default: Optional[Any] = None) -> Any: 32 | default = default if default is not None else self._default_text 33 | return info if info is not None and info != self._default_text else default 34 | 35 | def __initialize_with_basic_info(self, basic_info: Dict): 36 | """ 37 | Initialize instance with basic information about the airport. 38 | """ 39 | super().__init__( 40 | latitude=basic_info["lat"], 41 | longitude=basic_info["lon"] 42 | ) 43 | self.altitude = basic_info["alt"] 44 | 45 | self.name = basic_info["name"] 46 | self.icao = basic_info["icao"] 47 | self.iata = basic_info["iata"] 48 | 49 | self.country = basic_info["country"] 50 | 51 | def __initialize_with_info(self, info: Dict): 52 | """ 53 | Initialize instance with extra information about the airport. 54 | """ 55 | super().__init__( 56 | latitude=info["position"]["latitude"], 57 | longitude=info["position"]["longitude"] 58 | ) 59 | self.altitude = info["position"]["altitude"] 60 | 61 | self.name = info["name"] 62 | self.icao = info["code"]["icao"] 63 | self.iata = info["code"]["iata"] 64 | 65 | # Location information. 66 | position = info["position"] 67 | 68 | self.country = position["country"]["name"] 69 | self.country_code = self.__get_info(position.get("country", dict()).get("code")) 70 | self.city = self.__get_info(position.get("region", dict())).get("city") 71 | 72 | # Timezone information. 73 | timezone = info.get("timezone", dict()) 74 | 75 | self.timezone_name = self.__get_info(timezone.get("name")) 76 | self.timezone_offset = self.__get_info(timezone.get("offset")) 77 | self.timezone_offset_hours = self.__get_info(timezone.get("offsetHours")) 78 | self.timezone_abbr = self.__get_info(timezone.get("abbr")) 79 | self.timezone_abbr_name = self.__get_info(timezone.get("abbrName")) 80 | 81 | # Other information. 82 | self.visible = self.__get_info(info.get("visible")) 83 | self.website = self.__get_info(info.get("website")) 84 | 85 | def set_airport_details(self, airport_details: Dict) -> None: 86 | """ 87 | Set airport details to the instance. Use FlightRadar24API.get_airport_details(...) method to get it. 88 | """ 89 | # Get airport data. 90 | airport = self.__get_info(airport_details.get("airport"), dict()) 91 | airport = self.__get_info(airport.get("pluginData"), dict()) 92 | 93 | # Get information about the airport. 94 | details = self.__get_info(airport.get("details"), dict()) 95 | 96 | # Get location information. 97 | position = self.__get_info(details.get("position"), dict()) 98 | code = self.__get_info(details.get("code"), dict()) 99 | country = self.__get_info(position.get("country"), dict()) 100 | region = self.__get_info(position.get("region"), dict()) 101 | 102 | # Get reviews of the airport. 103 | flight_diary = self.__get_info(airport.get("flightdiary"), dict()) 104 | ratings = self.__get_info(flight_diary.get("ratings"), dict()) 105 | 106 | # Get schedule information. 107 | schedule = self.__get_info(airport.get("schedule"), dict()) 108 | 109 | # Get timezone information. 110 | timezone = self.__get_info(details.get("timezone"), dict()) 111 | 112 | # Get aircraft count. 113 | aircraft_count = self.__get_info(airport.get("aircraftCount"), dict()) 114 | aircraft_on_ground = self.__get_info(aircraft_count.get("onGround"), dict()) 115 | 116 | # Get URLs for more information about the airport. 117 | urls = self.__get_info(details.get("url"), dict()) 118 | 119 | # Basic airport information. 120 | self.name = self.__get_info(details.get("name")) 121 | self.iata = self.__get_info(code.get("iata")) 122 | self.icao = self.__get_info(code.get("icao")) 123 | self.altitude = self.__get_info(position.get("elevation")) 124 | self.latitude = self.__get_info(position.get("latitude")) 125 | self.longitude = self.__get_info(position.get("longitude")) 126 | 127 | # Airport location. 128 | self.country = self.__get_info(country.get("name")) 129 | self.country_code = self.__get_info(country.get("code")) 130 | self.country_id = self.__get_info(country.get("id")) 131 | self.city = self.__get_info(region.get("city")) 132 | 133 | # Airport timezone. 134 | self.timezone_abbr = self.__get_info(timezone.get("abbr")) 135 | self.timezone_abbr_name = self.__get_info(timezone.get("abbrName")) 136 | self.timezone_name = self.__get_info(timezone.get("name")) 137 | self.timezone_offset = self.__get_info(timezone.get("offset")) 138 | 139 | if isinstance(self.timezone_offset, int): 140 | self.timezone_offset_hours = int(self.timezone_offset / 60 / 60) 141 | self.timezone_offset_hours = f"{self.timezone_offset_hours}:00" 142 | else: self.timezone_offset_hours = self.__get_info(None) 143 | 144 | # Airport reviews. 145 | self.reviews_url = flight_diary.get("url") 146 | 147 | if self.reviews_url and isinstance(self.reviews_url, str): 148 | self.reviews_url = "https://www.flightradar24.com" + self.reviews_url 149 | else: 150 | self.reviews_url = self.__get_info(self.reviews_url) 151 | 152 | self.reviews = self.__get_info(flight_diary.get("reviews")) 153 | self.evaluation = self.__get_info(flight_diary.get("evaluation")) 154 | 155 | self.average_rating = self.__get_info(ratings.get("avg")) 156 | self.total_rating = self.__get_info(ratings.get("total")) 157 | 158 | # Weather information. 159 | self.weather = self.__get_info(airport.get("weather"), dict()) 160 | 161 | # Runway information. 162 | self.runways = airport.get("runways", list()) 163 | 164 | # Aircraft count information. 165 | self.aircraft_on_ground = self.__get_info(aircraft_on_ground.get("total")) 166 | self.aircraft_visible_on_ground = self.__get_info(aircraft_on_ground.get("visible")) 167 | 168 | # Schedule information. 169 | self.arrivals = self.__get_info(schedule.get("arrivals"), dict()) 170 | self.departures = self.__get_info(schedule.get("departures"), dict()) 171 | 172 | # Link for the homepage and more information 173 | self.website = self.__get_info(urls.get("homepage")) 174 | self.wikipedia = self.__get_info(urls.get("wikipedia")) 175 | 176 | # Other information. 177 | self.visible = self.__get_info(details.get("visible")) 178 | self.images = self.__get_info(details.get("airportImages"), dict()) 179 | -------------------------------------------------------------------------------- /python/FlightRadar24/entities/entity.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from abc import ABC 4 | from math import acos, cos, radians, sin 5 | 6 | 7 | class Entity(ABC): 8 | """ 9 | Representation of a real entity, at some location. 10 | """ 11 | 12 | _default_text = "N/A" 13 | 14 | def __init__(self, latitude: float, longitude: float): 15 | """ 16 | Constructor of the Entity class. 17 | """ 18 | self.latitude = latitude 19 | self.longitude = longitude 20 | 21 | def get_distance_from(self, entity: "Entity") -> float: 22 | """ 23 | Return the distance from another entity (in kilometers). 24 | """ 25 | lat1, lon1 = self.latitude, self.longitude 26 | lat2, lon2 = entity.latitude, entity.longitude 27 | 28 | lat1, lon1 = radians(lat1), radians(lon1) 29 | lat2, lon2 = radians(lat2), radians(lon2) 30 | 31 | return acos(sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(lon2 - lon1)) * 6371 32 | -------------------------------------------------------------------------------- /python/FlightRadar24/entities/flight.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from typing import Any, Dict, List, Optional 4 | from .entity import Entity 5 | 6 | 7 | class Flight(Entity): 8 | """ 9 | Flight representation. 10 | """ 11 | def __init__(self, flight_id: str, info: List[Any]): 12 | """ 13 | Constructor of the Flight class. 14 | 15 | :param flight_id: The flight ID specifically used by FlightRadar24 16 | :param info: Dictionary with received data from FlightRadar24 17 | """ 18 | super().__init__( 19 | latitude=self.__get_info(info[1]), 20 | longitude=self.__get_info(info[2]) 21 | ) 22 | 23 | self.id = flight_id 24 | self.icao_24bit = self.__get_info(info[0]) 25 | self.heading = self.__get_info(info[3]) 26 | self.altitude = self.__get_info(info[4]) 27 | self.ground_speed = self.__get_info(info[5]) 28 | self.squawk = self.__get_info(info[6]) 29 | self.aircraft_code = self.__get_info(info[8]) 30 | self.registration = self.__get_info(info[9]) 31 | self.time = self.__get_info(info[10]) 32 | self.origin_airport_iata = self.__get_info(info[11]) 33 | self.destination_airport_iata = self.__get_info(info[12]) 34 | self.number = self.__get_info(info[13]) 35 | self.airline_iata = self.__get_info(info[13][:2]) 36 | self.on_ground = self.__get_info(info[14]) 37 | self.vertical_speed = self.__get_info(info[15]) 38 | self.callsign = self.__get_info(info[16]) 39 | self.airline_icao = self.__get_info(info[18]) 40 | 41 | def __repr__(self) -> str: 42 | return self.__str__() 43 | 44 | def __str__(self) -> str: 45 | template = "<({}) {} - Altitude: {} - Ground Speed: {} - Heading: {}>" 46 | return template.format(self.aircraft_code, self.registration, self.altitude, self.ground_speed, self.heading) 47 | 48 | def __get_info(self, info: Any, default: Optional[Any] = None) -> Any: 49 | default = default if default is not None else self._default_text 50 | return info if info is not None and info != self._default_text else default 51 | 52 | def check_info(self, **info: Any) -> bool: 53 | """ 54 | Check one or more flight information. 55 | 56 | You can use the prefix "max_" or "min_" in the parameter 57 | to compare numeric data with ">" or "<". 58 | 59 | Example: check_info(min_altitude = 6700, max_altitude = 13000, airline_icao = "THY") 60 | """ 61 | 62 | comparison_functions = {"max": max, "min": min} 63 | 64 | for key, value in info.items(): 65 | 66 | # Separate the comparison prefix if it exists. 67 | prefix, key = key.split("_", maxsplit=1) if key[:4] == "max_" or key[:4] == "min_" else (None, key) 68 | 69 | # Check if the value is greater than or less than the attribute value. 70 | if prefix and key in self.__dict__: 71 | if comparison_functions[prefix](value, self.__dict__[key]) != value: return False 72 | 73 | # Check if the value is equal. 74 | elif key in self.__dict__ and value != self.__dict__[key]: return False 75 | 76 | return True 77 | 78 | def get_altitude(self) -> str: 79 | """ 80 | Return the formatted altitude, with the unit of measure. 81 | """ 82 | return "{} ft".format(self.altitude) 83 | 84 | def get_flight_level(self) -> str: 85 | """ 86 | Return the formatted flight level, with the unit of measure. 87 | """ 88 | return str(self.altitude)[:3] + " FL" if self.altitude >= 10000 else self.get_altitude() 89 | 90 | def get_ground_speed(self) -> str: 91 | """ 92 | Return the formatted ground speed, with the unit of measure. 93 | """ 94 | return "{} kt".format(self.ground_speed) + ("s" if self.ground_speed > 1 else "") 95 | 96 | def get_heading(self) -> str: 97 | """ 98 | Return the formatted heading, with the unit of measure. 99 | """ 100 | return str(self.heading) + "°" 101 | 102 | def get_vertical_speed(self) -> str: 103 | """ 104 | Return the formatted vertical speed, with the unit of measure. 105 | """ 106 | return "{} fpm".format(self.vertical_speed) 107 | 108 | def set_flight_details(self, flight_details: Dict) -> None: 109 | """ 110 | Set flight details to the instance. Use FlightRadar24API.get_flight_details(...) method to get it. 111 | """ 112 | # Get aircraft data. 113 | aircraft = self.__get_info(flight_details.get("aircraft"), dict()) 114 | 115 | # Get airline data. 116 | airline = self.__get_info(flight_details.get("airline"), dict()) 117 | 118 | # Get airport data. 119 | airport = self.__get_info(flight_details.get("airport"), dict()) 120 | 121 | # Get destination data. 122 | dest_airport = self.__get_info(airport.get("destination"), dict()) 123 | dest_airport_code = self.__get_info(dest_airport.get("code"), dict()) 124 | dest_airport_info = self.__get_info(dest_airport.get("info"), dict()) 125 | dest_airport_position = self.__get_info(dest_airport.get("position"), dict()) 126 | dest_airport_country = self.__get_info(dest_airport_position.get("country"), dict()) 127 | dest_airport_timezone = self.__get_info(dest_airport.get("timezone"), dict()) 128 | 129 | # Get origin data. 130 | orig_airport = self.__get_info(airport.get("origin"), dict()) 131 | orig_airport_code = self.__get_info(orig_airport.get("code"), dict()) 132 | orig_airport_info = self.__get_info(orig_airport.get("info"), dict()) 133 | orig_airport_position = self.__get_info(orig_airport.get("position"), dict()) 134 | orig_airport_country = self.__get_info(orig_airport_position.get("country"), dict()) 135 | orig_airport_timezone = self.__get_info(orig_airport.get("timezone"), dict()) 136 | 137 | # Get flight history. 138 | history = self.__get_info(flight_details.get("flightHistory"), dict()) 139 | 140 | # Get flight status. 141 | status = self.__get_info(flight_details.get("status"), dict()) 142 | 143 | # Aircraft information. 144 | self.aircraft_age = self.__get_info(aircraft.get("age")) 145 | self.aircraft_country_id = self.__get_info(aircraft.get("countryId")) 146 | self.aircraft_history = history.get("aircraft", list()) 147 | self.aircraft_images = aircraft.get("images", list()) 148 | self.aircraft_model = self.__get_info(self.__get_info(aircraft.get("model"), dict()).get("text")) 149 | 150 | # Airline information. 151 | self.airline_name = self.__get_info(airline.get("name")) 152 | self.airline_short_name = self.__get_info(airline.get("short")) 153 | 154 | # Destination airport position. 155 | self.destination_airport_altitude = self.__get_info(dest_airport_position.get("altitude")) 156 | self.destination_airport_country_code = self.__get_info(dest_airport_country.get("code")) 157 | self.destination_airport_country_name = self.__get_info(dest_airport_country.get("name")) 158 | self.destination_airport_latitude = self.__get_info(dest_airport_position.get("latitude")) 159 | self.destination_airport_longitude = self.__get_info(dest_airport_position.get("longitude")) 160 | 161 | # Destination airport information. 162 | self.destination_airport_icao = self.__get_info(dest_airport_code.get("icao")) 163 | self.destination_airport_baggage = self.__get_info(dest_airport_info.get("baggage")) 164 | self.destination_airport_gate = self.__get_info(dest_airport_info.get("gate")) 165 | self.destination_airport_name = self.__get_info(dest_airport.get("name")) 166 | self.destination_airport_terminal = self.__get_info(dest_airport_info.get("terminal")) 167 | self.destination_airport_visible = self.__get_info(dest_airport.get("visible")) 168 | self.destination_airport_website = self.__get_info(dest_airport.get("website")) 169 | 170 | # Destination airport timezone. 171 | self.destination_airport_timezone_abbr = self.__get_info(dest_airport_timezone.get("abbr")) 172 | self.destination_airport_timezone_abbr_name = self.__get_info(dest_airport_timezone.get("abbrName")) 173 | self.destination_airport_timezone_name = self.__get_info(dest_airport_timezone.get("name")) 174 | self.destination_airport_timezone_offset = self.__get_info(dest_airport_timezone.get("offset")) 175 | self.destination_airport_timezone_offset_hours = self.__get_info(dest_airport_timezone.get("offsetHours")) 176 | 177 | # Origin airport position. 178 | self.origin_airport_altitude = self.__get_info(orig_airport_position.get("altitude")) 179 | self.origin_airport_country_code = self.__get_info(orig_airport_country.get("code")) 180 | self.origin_airport_country_name = self.__get_info(orig_airport_country.get("name")) 181 | self.origin_airport_latitude = self.__get_info(orig_airport_position.get("latitude")) 182 | self.origin_airport_longitude = self.__get_info(orig_airport_position.get("longitude")) 183 | 184 | # Origin airport information. 185 | self.origin_airport_icao = self.__get_info(orig_airport_code.get("icao")) 186 | self.origin_airport_baggage = self.__get_info(orig_airport_info.get("baggage")) 187 | self.origin_airport_gate = self.__get_info(orig_airport_info.get("gate")) 188 | self.origin_airport_name = self.__get_info(orig_airport.get("name")) 189 | self.origin_airport_terminal = self.__get_info(orig_airport_info.get("terminal")) 190 | self.origin_airport_visible = self.__get_info(orig_airport.get("visible")) 191 | self.origin_airport_website = self.__get_info(orig_airport.get("website")) 192 | 193 | # Origin airport timezone. 194 | self.origin_airport_timezone_abbr = self.__get_info(orig_airport_timezone.get("abbr")) 195 | self.origin_airport_timezone_abbr_name = self.__get_info(orig_airport_timezone.get("abbrName")) 196 | self.origin_airport_timezone_name = self.__get_info(orig_airport_timezone.get("name")) 197 | self.origin_airport_timezone_offset = self.__get_info(orig_airport_timezone.get("offset")) 198 | self.origin_airport_timezone_offset_hours = self.__get_info(orig_airport_timezone.get("offsetHours")) 199 | 200 | # Flight status. 201 | self.status_icon = self.__get_info(status.get("icon")) 202 | self.status_text = self.__get_info(status.get("text")) 203 | 204 | # Time details. 205 | self.time_details = self.__get_info(flight_details.get("time"), dict()) 206 | 207 | # Flight trail. 208 | self.trail = flight_details.get("trail", list()) 209 | -------------------------------------------------------------------------------- /python/FlightRadar24/errors.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | class AirportNotFoundError(Exception): 4 | pass 5 | 6 | 7 | class CloudflareError(Exception): 8 | def __init__(self, message, response): 9 | self.message = message 10 | self.response = response 11 | 12 | def __str__(self): 13 | return self.message 14 | 15 | 16 | class LoginError(Exception): 17 | pass 18 | -------------------------------------------------------------------------------- /python/FlightRadar24/request.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from typing import Dict, List, Optional, Union 4 | 5 | import brotli 6 | import json 7 | import gzip 8 | 9 | import requests 10 | import requests.structures 11 | 12 | from .errors import CloudflareError 13 | 14 | 15 | class APIRequest(object): 16 | """ 17 | Class to make requests to the FlightRadar24. 18 | """ 19 | __content_encodings = { 20 | "": lambda x: x, 21 | "br": brotli.decompress, 22 | "gzip": gzip.decompress 23 | } 24 | 25 | def __init__( 26 | self, 27 | url: str, 28 | params: Optional[Dict] = None, 29 | headers: Optional[Dict] = None, 30 | timeout: int = 30, 31 | data: Optional[Dict] = None, 32 | cookies: Optional[Dict] = None, 33 | exclude_status_codes: List[int] = list() 34 | ): 35 | """ 36 | Constructor of the APIRequest class. 37 | 38 | :param url: URL for the request 39 | :param params: params that will be inserted on the URL for the request 40 | :param headers: headers for the request 41 | :param data: data for the request. If "data" is None, request will be a GET. Otherwise, it will be a POST 42 | :param cookies: cookies for the request 43 | :param exclude_status_codes: raise for status code except those on the excluded list 44 | """ 45 | self.url = url 46 | 47 | self.request_params = { 48 | "params": params, 49 | "headers": headers, 50 | "timeout": timeout, 51 | "data": data, 52 | "cookies": cookies 53 | } 54 | 55 | request_method = requests.get if data is None else requests.post 56 | 57 | if params: url += "?" + "&".join(["{}={}".format(k, v) for k, v in params.items()]) 58 | self.__response = request_method(url, headers=headers, cookies=cookies, data=data, timeout=timeout) 59 | 60 | if self.get_status_code() == 520: 61 | raise CloudflareError( 62 | message="An unexpected error has occurred. Perhaps you are making too many calls?", 63 | response=self.__response 64 | ) 65 | 66 | if self.get_status_code() not in exclude_status_codes: 67 | self.__response.raise_for_status() 68 | 69 | def get_content(self) -> Union[Dict, bytes]: 70 | """ 71 | Return the received content from the request. 72 | """ 73 | content = self.__response.content 74 | 75 | content_encoding = self.__response.headers.get("Content-Encoding", "") 76 | content_type = self.__response.headers["Content-Type"] 77 | 78 | # Try to decode the content. 79 | try: content = self.__content_encodings[content_encoding](content) 80 | except Exception: pass 81 | 82 | # Return a dictionary if the content type is JSON. 83 | if "application/json" in content_type: 84 | return json.loads(content) 85 | 86 | return content 87 | 88 | def get_cookies(self) -> Dict: 89 | """ 90 | Return the received cookies from the request. 91 | """ 92 | return self.__response.cookies.get_dict() 93 | 94 | def get_headers(self) -> requests.structures.CaseInsensitiveDict: 95 | """ 96 | Return the headers of the response. 97 | """ 98 | return self.__response.headers 99 | 100 | def get_response_object(self) -> requests.models.Response: 101 | """ 102 | Return the received response object. 103 | """ 104 | return self.__response 105 | 106 | def get_status_code(self) -> int: 107 | """ 108 | Return the status code of the response. 109 | """ 110 | return self.__response.status_code 111 | -------------------------------------------------------------------------------- /python/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jean Loui Bernard Silva de Jesus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /python/README.md: -------------------------------------------------------------------------------- 1 | # FlightRadarAPI 2 | Unofficial SDK for [FlightRadar24](https://www.flightradar24.com/) for Python 3 and Node.js. 3 | 4 | This SDK should only be used for your own educational purposes. If you are interested in accessing Flightradar24 data commercially, please contact business@fr24.com. See more information at [Flightradar24's terms and conditions](https://www.flightradar24.com/terms-and-conditions). 5 | 6 | **Official FR24 API**: https://fr24api.flightradar24.com/ 7 | 8 | [![Python Package](https://github.com/JeanExtreme002/FlightRadarAPI/workflows/Python%20Package/badge.svg)](https://github.com/JeanExtreme002/FlightRadarAPI/actions) 9 | [![Pypi](https://img.shields.io/pypi/v/FlightRadarAPI?logo=pypi)](https://pypi.org/project/FlightRadarAPI/) 10 | [![License](https://img.shields.io/pypi/l/FlightRadarAPI)](https://github.com/JeanExtreme002/FlightRadarAPI) 11 | [![Python Version](https://img.shields.io/badge/python-3.7+-8A2BE2)](https://pypi.org/project/FlightRadarAPI/) 12 | [![Npm](https://img.shields.io/npm/v/flightradarapi?logo=npm&color=red)](https://www.npmjs.com/package/flightradarapi) 13 | [![Downloads](https://static.pepy.tech/personalized-badge/flightradarapi?period=total&units=international_system&left_color=grey&right_color=orange&left_text=downloads)](https://pypi.org/project/FlightRadarAPI/) 14 | [![Frequency](https://img.shields.io/pypi/dm/flightradarapi?style=flat&label=frequency)](https://pypi.org/project/FlightRadarAPI/) 15 | 16 | ## Installing FlightRadarAPI: 17 | ``` 18 | $ pip install FlightRadarAPI 19 | ``` 20 | 21 | ## Basic Usage: 22 | Import the class `FlightRadar24API` and create an instance of it. 23 | ```py 24 | from FlightRadar24 import FlightRadar24API 25 | fr_api = FlightRadar24API() 26 | ``` 27 | 28 | **Getting flights list:** 29 | ```py 30 | flights = fr_api.get_flights(...) # Returns a list of Flight objects 31 | ``` 32 | **Getting airports list:** 33 | ```py 34 | airports = fr_api.get_airports(...) # Returns a list of Airport objects 35 | ``` 36 | **Getting airlines list:** 37 | ```py 38 | airlines = fr_api.get_airlines() 39 | ``` 40 | **Getting zones list:** 41 | ```py 42 | zones = fr_api.get_zones() 43 | ``` 44 | 45 | ## Documentation 46 | Explore the documentation of FlightRadarAPI package, for Python or NodeJS, through [this site](https://JeanExtreme002.github.io/FlightRadarAPI/). 47 | -------------------------------------------------------------------------------- /python/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "FlightRadarAPI" 3 | dynamic = ["version"] 4 | description = "SDK for FlightRadar24" 5 | authors = [ 6 | { name = "Jean Loui Bernard Silva de Jesus", email = "jeanextreme002@gmail.com" }, 7 | ] 8 | license = "MIT" 9 | readme = "README.md" 10 | keywords = ["flightradar24", "api", "radar", "aviation", "flights", "airports", "airlines", "aircraft"] 11 | classifiers = [ 12 | "License :: OSI Approved :: MIT License", 13 | "Programming Language :: Python :: 3 :: Only", 14 | "Programming Language :: Python :: 3.7", 15 | "Programming Language :: Python :: 3.8", 16 | "Programming Language :: Python :: 3.9", 17 | "Programming Language :: Python :: 3.10", 18 | "Programming Language :: Python :: 3.11" 19 | ] 20 | exclude = ["tests", ".flake8"] 21 | requires-python = ">=3.7" 22 | dependencies = [ 23 | "Brotli", 24 | "requests", 25 | ] 26 | 27 | [tool.hatch.build.targets.wheel] 28 | packages = ["FlightRadar24"] 29 | 30 | [project.optional-dependencies] 31 | tests = [ 32 | "pytest", 33 | ] 34 | 35 | [project.urls] 36 | "Homepage" = "https://github.com/JeanExtreme002/FlightRadarAPI" 37 | "Source Code" = "https://github.com/JeanExtreme002/FlightRadarAPI" 38 | "Documentation" = "https://jeanextreme002.github.io/FlightRadarAPI/" 39 | "Bug Reports" = "https://github.com/JeanExtreme002/FlightRadarAPI/issues" 40 | 41 | [tool.hatch.version] 42 | path = "FlightRadar24/__init__.py" 43 | 44 | [build-system] 45 | requires = ["hatchling"] 46 | build-backend = "hatchling.build" 47 | -------------------------------------------------------------------------------- /python/requirements.txt: -------------------------------------------------------------------------------- 1 | Brotli 2 | pytest 3 | requests 4 | -------------------------------------------------------------------------------- /python/tests/conftest.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | current_dir = os.getcwd() 5 | sys.path.append(current_dir) 6 | -------------------------------------------------------------------------------- /python/tests/package.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from FlightRadar24 import __version__ as version 4 | from FlightRadar24 import FlightRadar24API 5 | from FlightRadar24.errors import CloudflareError 6 | -------------------------------------------------------------------------------- /python/tests/test_api.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from package import CloudflareError, FlightRadar24API, version 4 | from util import repeat_test 5 | 6 | print("Testing FlightRadarAPI version %s." % version) 7 | 8 | repeat_test_config = { 9 | "attempts": 2, 10 | "after": 5, 11 | "errors": [CloudflareError] 12 | } 13 | 14 | fr_api = FlightRadar24API() 15 | 16 | 17 | @repeat_test(**repeat_test_config) 18 | def test_get_airlines(expect=100): 19 | results = fr_api.get_airlines() 20 | assert len(results) >= expect 21 | 22 | 23 | @repeat_test(**repeat_test_config) 24 | def test_get_airport(airports=["ATL", "LAX", "DXB", "DFW"]): 25 | for airport in airports: 26 | assert fr_api.get_airport(airport).iata == airport 27 | 28 | 29 | @repeat_test(**repeat_test_config) 30 | def test_get_airport_details(airports=["ATL", "LAX", "DXB", "DFW"]): 31 | data = ["airport", "airlines", "aircraftImages"] 32 | 33 | for airport in airports: 34 | details = fr_api.get_airport_details(airport, flight_limit=1) 35 | assert all([key in details for key in data]) and details["airport"]["pluginData"]["details"] 36 | 37 | 38 | @repeat_test(**repeat_test_config) 39 | def test_get_airports(expect=1000): 40 | results = fr_api.get_airports() 41 | assert len(results) >= expect 42 | 43 | 44 | @repeat_test(**repeat_test_config) 45 | def test_get_zones(expect=5): 46 | results = fr_api.get_zones() 47 | 48 | assert len(results) >= expect 49 | 50 | for zone, data in results.items(): 51 | assert all([key in data for key in ["tl_y", "tl_x", "br_y", "br_x"]]) 52 | 53 | 54 | @repeat_test(**repeat_test_config) 55 | def test_get_flights(expect=100): 56 | results = fr_api.get_flights() 57 | assert len(results) >= expect 58 | 59 | 60 | @repeat_test(**repeat_test_config) 61 | def test_get_flight_details(): 62 | data = ["airport", "airline", "aircraft", "time", "status", "trail"] 63 | 64 | flights = fr_api.get_flights() 65 | middle = len(flights) // 2 66 | 67 | flights = flights[middle - 2: middle + 2] 68 | 69 | for flight in flights: 70 | details = fr_api.get_flight_details(flight) 71 | assert all([key in details for key in data]) and details["aircraft"] 72 | 73 | 74 | @repeat_test(**repeat_test_config) 75 | def test_get_flights_by_airline(airlines=["SWA", "GLO", "AZU", "UAL", "THY"], expect=3): 76 | count = 0 77 | 78 | for airline in airlines: 79 | flights = fr_api.get_flights(airline=airline) 80 | 81 | for flight in flights: 82 | assert flight.airline_icao == airline 83 | 84 | if len(flights) > 0: count += 1 85 | 86 | assert count >= expect 87 | 88 | 89 | @repeat_test(**repeat_test_config) 90 | def test_get_flights_by_bounds(target_zones=["northamerica", "southamerica"], expect=30): 91 | zones = fr_api.get_zones() 92 | 93 | for zone in target_zones: 94 | zone = zones[zone] 95 | bounds = fr_api.get_bounds(zone) 96 | 97 | flights = fr_api.get_flights(bounds=bounds) 98 | 99 | for flight in flights: 100 | assert zone["tl_y"] >= flight.latitude >= zone["br_y"] 101 | assert zone["tl_x"] <= flight.longitude <= zone["br_x"] 102 | 103 | assert len(flights) >= expect 104 | 105 | 106 | @repeat_test(**repeat_test_config) 107 | def test_get_airline_logo(airlines=[["WN", "SWA"], ["G3", "GLO"], ["AD", "AZU"], ["AA", "AAL"], ["TK", "THY"]]): 108 | expected = len(airlines) * 0.8 109 | found = 0 110 | 111 | for airline in airlines: 112 | result = fr_api.get_airline_logo(*airline) 113 | if result and len(result[0]) > 512: found += 1 114 | 115 | assert found >= expected 116 | 117 | 118 | @repeat_test(**repeat_test_config) 119 | def test_get_country_flag(countries=["United States", "Brazil", "Egypt", "Japan", "South Korea", "Canada"]): 120 | expected = len(countries) * 0.8 121 | found = 0 122 | 123 | for country in countries: 124 | result = fr_api.get_country_flag(country) 125 | if result and len(result[0]) > 512: found += 1 126 | 127 | assert found >= expected 128 | 129 | 130 | def test_get_bounds_by_point(): 131 | expected = "52.58594974202871,52.54997688140807,13.253064418048115,13.3122478541492" 132 | actual = fr_api.get_bounds_by_point(52.567967, 13.282644, 2000) 133 | 134 | assert actual == expected 135 | -------------------------------------------------------------------------------- /python/tests/util.py: -------------------------------------------------------------------------------- 1 | from typing import Callable, List, Optional 2 | import time 3 | 4 | 5 | def raise_multiple(errors: List) -> None: 6 | """ 7 | Raise a stack of errors. 8 | """ 9 | if len(errors) == 0: return 10 | 11 | try: raise errors.pop() 12 | finally: raise_multiple(errors) 13 | 14 | 15 | def repeat_test(attempts: int, after: int, errors: Optional[List[Exception]] = None) -> Callable: 16 | """ 17 | Decorator to repeat a test N times for specific errors. 18 | 19 | :param attempts: Number of attempts for testing 20 | :param after: Time in seconds to wait for each attempt 21 | :param errors: If None, repeat test for any error 22 | """ 23 | def _repeat_test(test_function: Callable) -> Callable: 24 | def wrapper(*args, **kwargs): 25 | nonlocal attempts, errors 26 | 27 | error_list: List[Exception] = list() 28 | 29 | for attempt in range(attempts): 30 | try: 31 | return test_function(*args, **kwargs) 32 | 33 | except Exception as error: 34 | if errors is not None and error not in errors: raise error 35 | if after is not None: time.sleep(after) 36 | 37 | error_list.append(error) 38 | 39 | raise raise_multiple(error_list) 40 | return wrapper 41 | return _repeat_test 42 | --------------------------------------------------------------------------------