How to access ADS-B data from OpenSky live API?
Warning
OpenSky data are subject to particular terms of use. In particular, if you plan to use data for commercial purposes, you should contact them.
Anonymous access to the OpenSky live API is possible, but functionalities may be limited. The first thing to do once you have an account is to put your credentials in you configuration file. Add the following lines to the [opensky] section of your configuration file.
[opensky]
username =
password =
You can check the path to your configuration file here. The path is different according to OS versions so do not assume anything and check the contents of the variable.
>>> import traffic
>>> traffic.config_file
PosixPath('/home/xo/.config/traffic/traffic.conf')
State vectors
The most basic usage for the OpenSky REST API is to get the instant position for all aircraft. This part actually does not require authentication.
import matplotlib.pyplot as plt
import pandas as pd
from cartes.crs import EuroPP
from cartes.utils.features import countries
from traffic.data import opensky
sv = opensky.api_states()
with plt.style.context('traffic'):
fig, ax = plt.subplots(subplot_kw=dict(projection=EuroPP()))
ax.add_feature(countries())
ax.gridlines()
ax.set_extent((-7, 15, 40, 55))
ax.spines['geo'].set_visible(False)
sv.plot(ax, s=10, color="#4c78a8")
now = pd.Timestamp("now", tz="utc")
ax.set_title(
f"Snapshot generated at {now:%Y-%m-%d %H:%MZ}",
fontsize=14
)
Flight tables
Flight tables are accessible by airport (use the ICAO code) given temporal bounds:
# Have you seen Santa Claus coming to Toulouse?
opensky.api_arrival("LFBO", "2021-12-24 20:00", "2021-12-25 06:00")
---------------------------------------------------------------------------
ReadTimeout Traceback (most recent call last)
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_transports/default.py:72, in map_httpcore_exceptions()
71 try:
---> 72 yield
73 except Exception as exc:
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_transports/default.py:236, in HTTPTransport.handle_request(self, request)
235 with map_httpcore_exceptions():
--> 236 resp = self._pool.handle_request(req)
238 assert isinstance(resp.stream, typing.Iterable)
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpcore/_sync/connection_pool.py:216, in ConnectionPool.handle_request(self, request)
215 self._close_connections(closing)
--> 216 raise exc from None
218 # Return the response. Note that in this case we still have to manage
219 # the point at which the response is closed.
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpcore/_sync/connection_pool.py:196, in ConnectionPool.handle_request(self, request)
194 try:
195 # Send the request on the assigned connection.
--> 196 response = connection.handle_request(
197 pool_request.request
198 )
199 except ConnectionNotAvailable:
200 # In some cases a connection may initially be available to
201 # handle a request, but then become unavailable.
202 #
203 # In this case we clear the connection and try again.
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpcore/_sync/connection.py:101, in HTTPConnection.handle_request(self, request)
99 raise exc
--> 101 return self._connection.handle_request(request)
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpcore/_sync/http11.py:143, in HTTP11Connection.handle_request(self, request)
142 self._response_closed()
--> 143 raise exc
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpcore/_sync/http11.py:113, in HTTP11Connection.handle_request(self, request)
104 with Trace(
105 "receive_response_headers", logger, request, kwargs
106 ) as trace:
107 (
108 http_version,
109 status,
110 reason_phrase,
111 headers,
112 trailing_data,
--> 113 ) = self._receive_response_headers(**kwargs)
114 trace.return_value = (
115 http_version,
116 status,
117 reason_phrase,
118 headers,
119 )
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpcore/_sync/http11.py:186, in HTTP11Connection._receive_response_headers(self, request)
185 while True:
--> 186 event = self._receive_event(timeout=timeout)
187 if isinstance(event, h11.Response):
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpcore/_sync/http11.py:224, in HTTP11Connection._receive_event(self, timeout)
223 if event is h11.NEED_DATA:
--> 224 data = self._network_stream.read(
225 self.READ_NUM_BYTES, timeout=timeout
226 )
228 # If we feed this case through h11 we'll raise an exception like:
229 #
230 # httpcore.RemoteProtocolError: can't handle event type
(...)
234 # perspective. Instead we handle this case distinctly and treat
235 # it as a ConnectError.
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpcore/_backends/sync.py:124, in SyncStream.read(self, max_bytes, timeout)
123 exc_map: ExceptionMapping = {socket.timeout: ReadTimeout, OSError: ReadError}
--> 124 with map_exceptions(exc_map):
125 self._sock.settimeout(timeout)
File /opt/hostedtoolcache/Python/3.11.10/x64/lib/python3.11/contextlib.py:158, in _GeneratorContextManager.__exit__(self, typ, value, traceback)
157 try:
--> 158 self.gen.throw(typ, value, traceback)
159 except StopIteration as exc:
160 # Suppress StopIteration *unless* it's the same exception that
161 # was passed to throw(). This prevents a StopIteration
162 # raised inside the "with" statement from being suppressed.
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpcore/_exceptions.py:14, in map_exceptions(map)
13 if isinstance(exc, from_exc):
---> 14 raise to_exc(exc) from exc
15 raise
ReadTimeout: The read operation timed out
The above exception was the direct cause of the following exception:
ReadTimeout Traceback (most recent call last)
Cell In[2], line 2
1 # Have you seen Santa Claus coming to Toulouse?
----> 2 opensky.api_arrival("LFBO", "2021-12-24 20:00", "2021-12-25 06:00")
File ~/work/traffic/traffic/src/traffic/data/adsb/opensky.py:234, in OpenSky.api_arrival(self, airport, begin, end)
227 @copy_documentation(rest.REST.arrival)
228 def api_arrival(
229 self,
(...)
232 end: None | timelike = None,
233 ) -> pd.DataFrame:
--> 234 return self.rest_client.arrival(
235 airport if isinstance(airport, str) else airport.icao, begin, end
236 )
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/pyopensky/rest.py:410, in REST.arrival(self, airport, begin, end)
407 else:
408 end_ts = to_datetime(end)
--> 410 json = self.get(
411 f"https://opensky-network.org/api/flights/arrival"
412 f"?begin={begin_ts.timestamp():.0f}&airport={airport}&"
413 f"end={end_ts.timestamp():.0f}"
414 )
416 return (
417 pd.DataFrame.from_records(json)
418 .convert_dtypes(dtype_backend="pyarrow")[
(...)
438 .sort_values("lastSeen")
439 )
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/pyopensky/rest.py:61, in REST.get(self, query, retry)
60 def get(self, query: str, retry: int = 5) -> Any:
---> 61 c = self.client.get(query, auth=self.auth)
62 try:
63 if limit := c.headers.get("X-Rate-Limit-Remaining", None):
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_client.py:1066, in Client.get(self, url, params, headers, cookies, auth, follow_redirects, timeout, extensions)
1049 def get(
1050 self,
1051 url: URL | str,
(...)
1059 extensions: RequestExtensions | None = None,
1060 ) -> Response:
1061 """
1062 Send a `GET` request.
1063
1064 **Parameters**: See `httpx.request`.
1065 """
-> 1066 return self.request(
1067 "GET",
1068 url,
1069 params=params,
1070 headers=headers,
1071 cookies=cookies,
1072 auth=auth,
1073 follow_redirects=follow_redirects,
1074 timeout=timeout,
1075 extensions=extensions,
1076 )
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_client.py:837, in Client.request(self, method, url, content, data, files, json, params, headers, cookies, auth, follow_redirects, timeout, extensions)
822 warnings.warn(message, DeprecationWarning)
824 request = self.build_request(
825 method=method,
826 url=url,
(...)
835 extensions=extensions,
836 )
--> 837 return self.send(request, auth=auth, follow_redirects=follow_redirects)
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_client.py:926, in Client.send(self, request, stream, auth, follow_redirects)
922 self._set_timeout(request)
924 auth = self._build_request_auth(request, auth)
--> 926 response = self._send_handling_auth(
927 request,
928 auth=auth,
929 follow_redirects=follow_redirects,
930 history=[],
931 )
932 try:
933 if not stream:
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_client.py:954, in Client._send_handling_auth(self, request, auth, follow_redirects, history)
951 request = next(auth_flow)
953 while True:
--> 954 response = self._send_handling_redirects(
955 request,
956 follow_redirects=follow_redirects,
957 history=history,
958 )
959 try:
960 try:
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_client.py:991, in Client._send_handling_redirects(self, request, follow_redirects, history)
988 for hook in self._event_hooks["request"]:
989 hook(request)
--> 991 response = self._send_single_request(request)
992 try:
993 for hook in self._event_hooks["response"]:
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_client.py:1027, in Client._send_single_request(self, request)
1022 raise RuntimeError(
1023 "Attempted to send an async request with a sync Client instance."
1024 )
1026 with request_context(request=request):
-> 1027 response = transport.handle_request(request)
1029 assert isinstance(response.stream, SyncByteStream)
1031 response.request = request
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_transports/default.py:235, in HTTPTransport.handle_request(self, request)
221 assert isinstance(request.stream, SyncByteStream)
223 req = httpcore.Request(
224 method=request.method,
225 url=httpcore.URL(
(...)
233 extensions=request.extensions,
234 )
--> 235 with map_httpcore_exceptions():
236 resp = self._pool.handle_request(req)
238 assert isinstance(resp.stream, typing.Iterable)
File /opt/hostedtoolcache/Python/3.11.10/x64/lib/python3.11/contextlib.py:158, in _GeneratorContextManager.__exit__(self, typ, value, traceback)
156 value = typ()
157 try:
--> 158 self.gen.throw(typ, value, traceback)
159 except StopIteration as exc:
160 # Suppress StopIteration *unless* it's the same exception that
161 # was passed to throw(). This prevents a StopIteration
162 # raised inside the "with" statement from being suppressed.
163 return exc is not value
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_transports/default.py:89, in map_httpcore_exceptions()
86 raise
88 message = str(exc)
---> 89 raise mapped_exc(message) from exc
ReadTimeout: The read operation timed out
# Or maybe leaving?
opensky.api_departure("LFBO", "2021-12-24 20:00", "2021-12-25 06:00")
firstSeen | lastSeen | icao24 | callsign | estDepartureAirport | estArrivalAirport | |
---|---|---|---|---|---|---|
3 | 2021-12-24 20:15:26+00:00 | 2021-12-24 21:03:57+00:00 | 4b1a1f | EZS32CR | LFBO | LSGG |
2 | 2021-12-24 20:35:00+00:00 | 2021-12-24 21:50:51+00:00 | 3922ed | TAY4947 | LFBO | EBLG |
1 | 2021-12-24 20:41:23+00:00 | 2021-12-24 21:39:38+00:00 | 344498 | IBS36NA | LFBO | LEMD |
0 | 2021-12-25 05:01:41+00:00 | 2021-12-25 06:11:08+00:00 | 394c17 | AFR81DB | LFBO | LFPG |
A basic route database is also accessible through the REST API:
opensky.api_routes("AFR292")
---------------------------------------------------------------------------
HTTPStatusError Traceback (most recent call last)
Cell In[4], line 1
----> 1 opensky.api_routes("AFR292")
File ~/work/traffic/traffic/src/traffic/data/adsb/opensky.py:200, in OpenSky.api_routes(self, callsign)
198 @copy_documentation(rest.REST.routes)
199 def api_routes(self, callsign: str) -> tuple[str, str]:
--> 200 return self.rest_client.routes(callsign)
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/pyopensky/rest.py:280, in REST.routes(self, callsign)
278 def routes(self, callsign: str) -> tuple[str, str]:
279 """Returns the route associated to a callsign."""
--> 280 json = self.get(
281 f"https://opensky-network.org/api/routes?callsign={callsign}"
282 )
284 return tuple(json["route"])
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/pyopensky/rest.py:67, in REST.get(self, query, retry)
65 if limit < 100:
66 _log.warning(f"Rate limiting: Only {limit} calls remaining")
---> 67 c.raise_for_status()
68 return c.json()
69 except httpx.HTTPStatusError:
File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_models.py:763, in Response.raise_for_status(self)
761 error_type = error_types.get(status_class, "Invalid status code")
762 message = message.format(self, error_type=error_type)
--> 763 raise HTTPStatusError(message, request=request, response=self)
HTTPStatusError: Client error '404 ' for url 'https://opensky-network.org/api/routes?callsign=AFR292'
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404