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
      )
../_images/opensky_rest_0_1.png

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