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
      )
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[1], line 9
      5 from cartes.utils.features import countries
      7 from traffic.data import opensky
----> 9 sv = opensky.api_states()
     11 with plt.style.context('traffic'):
     12     fig, ax = plt.subplots(subplot_kw=dict(projection=EuroPP()))

File ~/work/traffic/traffic/src/traffic/data/adsb/opensky.py:190, in OpenSky.api_states(self, own, bounds)
    181 @copy_documentation(rest.REST.states)
    182 def api_states(
    183     self,
   (...)
    188     | tuple[float, float, float, float] = None,
    189 ) -> StateVectors:
--> 190     df = self.rest_client.states(own, bounds).pipe(format_history)
    191     return StateVectors(df)

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/pyopensky/rest.py:193, in REST.states(self, own, bounds, retry)
    189         west, south, east, north = bounds
    191     what += f"?lamin={south}&lamax={north}&lomin={west}&lomax={east}"
--> 193 json = self.get(f"https://opensky-network.org/api/states/{what}")
    194 columns: list[str] = list(self._json_columns)
    196 # For some reason, OpenSky may return 18 fields instead of 17

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:1053, in Client.get(self, url, params, headers, cookies, auth, follow_redirects, timeout, extensions)
   1036 def get(
   1037     self,
   1038     url: URL | str,
   (...)
   1046     extensions: RequestExtensions | None = None,
   1047 ) -> Response:
   1048     """
   1049     Send a `GET` request.
   1050 
   1051     **Parameters**: See `httpx.request`.
   1052     """
-> 1053     return self.request(
   1054         "GET",
   1055         url,
   1056         params=params,
   1057         headers=headers,
   1058         cookies=cookies,
   1059         auth=auth,
   1060         follow_redirects=follow_redirects,
   1061         timeout=timeout,
   1062         extensions=extensions,
   1063     )

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_client.py:825, in Client.request(self, method, url, content, data, files, json, params, headers, cookies, auth, follow_redirects, timeout, extensions)
    810     warnings.warn(message, DeprecationWarning, stacklevel=2)
    812 request = self.build_request(
    813     method=method,
    814     url=url,
   (...)
    823     extensions=extensions,
    824 )
--> 825 return self.send(request, auth=auth, follow_redirects=follow_redirects)

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_client.py:912, in Client.send(self, request, stream, auth, follow_redirects)
    904 follow_redirects = (
    905     self.follow_redirects
    906     if isinstance(follow_redirects, UseClientDefault)
    907     else follow_redirects
    908 )
    910 self._set_timeout(request)
--> 912 auth = self._build_request_auth(request, auth)
    914 response = self._send_handling_auth(
    915     request,
    916     auth=auth,
    917     follow_redirects=follow_redirects,
    918     history=[],
    919 )
    920 try:

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_client.py:463, in BaseClient._build_request_auth(self, request, auth)
    457 def _build_request_auth(
    458     self,
    459     request: Request,
    460     auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT,
    461 ) -> Auth:
    462     auth = (
--> 463         self._auth if isinstance(auth, UseClientDefault) else self._build_auth(auth)
    464     )
    466     if auth is not None:
    467         return auth

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_client.py:449, in BaseClient._build_auth(self, auth)
    447     return None
    448 elif isinstance(auth, tuple):
--> 449     return BasicAuth(username=auth[0], password=auth[1])
    450 elif isinstance(auth, Auth):
    451     return auth

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_auth.py:133, in BasicAuth.__init__(self, username, password)
    132 def __init__(self, username: str | bytes, password: str | bytes) -> None:
--> 133     self._auth_header = self._build_auth_header(username, password)

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_auth.py:140, in BasicAuth._build_auth_header(self, username, password)
    139 def _build_auth_header(self, username: str | bytes, password: str | bytes) -> str:
--> 140     userpass = b":".join((to_bytes(username), to_bytes(password)))
    141     token = b64encode(userpass).decode()
    142     return f"Basic {token}"

TypeError: sequence item 0: expected a bytes-like object, NoneType found

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")
---------------------------------------------------------------------------
TypeError                                 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:1053, in Client.get(self, url, params, headers, cookies, auth, follow_redirects, timeout, extensions)
   1036 def get(
   1037     self,
   1038     url: URL | str,
   (...)
   1046     extensions: RequestExtensions | None = None,
   1047 ) -> Response:
   1048     """
   1049     Send a `GET` request.
   1050 
   1051     **Parameters**: See `httpx.request`.
   1052     """
-> 1053     return self.request(
   1054         "GET",
   1055         url,
   1056         params=params,
   1057         headers=headers,
   1058         cookies=cookies,
   1059         auth=auth,
   1060         follow_redirects=follow_redirects,
   1061         timeout=timeout,
   1062         extensions=extensions,
   1063     )

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_client.py:825, in Client.request(self, method, url, content, data, files, json, params, headers, cookies, auth, follow_redirects, timeout, extensions)
    810     warnings.warn(message, DeprecationWarning, stacklevel=2)
    812 request = self.build_request(
    813     method=method,
    814     url=url,
   (...)
    823     extensions=extensions,
    824 )
--> 825 return self.send(request, auth=auth, follow_redirects=follow_redirects)

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_client.py:912, in Client.send(self, request, stream, auth, follow_redirects)
    904 follow_redirects = (
    905     self.follow_redirects
    906     if isinstance(follow_redirects, UseClientDefault)
    907     else follow_redirects
    908 )
    910 self._set_timeout(request)
--> 912 auth = self._build_request_auth(request, auth)
    914 response = self._send_handling_auth(
    915     request,
    916     auth=auth,
    917     follow_redirects=follow_redirects,
    918     history=[],
    919 )
    920 try:

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_client.py:463, in BaseClient._build_request_auth(self, request, auth)
    457 def _build_request_auth(
    458     self,
    459     request: Request,
    460     auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT,
    461 ) -> Auth:
    462     auth = (
--> 463         self._auth if isinstance(auth, UseClientDefault) else self._build_auth(auth)
    464     )
    466     if auth is not None:
    467         return auth

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_client.py:449, in BaseClient._build_auth(self, auth)
    447     return None
    448 elif isinstance(auth, tuple):
--> 449     return BasicAuth(username=auth[0], password=auth[1])
    450 elif isinstance(auth, Auth):
    451     return auth

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_auth.py:133, in BasicAuth.__init__(self, username, password)
    132 def __init__(self, username: str | bytes, password: str | bytes) -> None:
--> 133     self._auth_header = self._build_auth_header(username, password)

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_auth.py:140, in BasicAuth._build_auth_header(self, username, password)
    139 def _build_auth_header(self, username: str | bytes, password: str | bytes) -> str:
--> 140     userpass = b":".join((to_bytes(username), to_bytes(password)))
    141     token = b64encode(userpass).decode()
    142     return f"Basic {token}"

TypeError: sequence item 0: expected a bytes-like object, NoneType found
# Or maybe leaving?
opensky.api_departure("LFBO", "2021-12-24 20:00", "2021-12-25 06:00")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[3], line 2
      1 # Or maybe leaving?
----> 2 opensky.api_departure("LFBO", "2021-12-24 20:00", "2021-12-25 06:00")

File ~/work/traffic/traffic/src/traffic/data/adsb/opensky.py:245, in OpenSky.api_departure(self, airport, begin, end)
    238 @copy_documentation(rest.REST.departure)
    239 def api_departure(
    240     self,
   (...)
    243     end: None | timelike = None,
    244 ) -> pd.DataFrame:
--> 245     return self.rest_client.departure(
    246         airport if isinstance(airport, str) else airport.icao, begin, end
    247     )

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/pyopensky/rest.py:470, in REST.departure(self, airport, begin, end)
    467 else:
    468     end_ts = to_datetime(end)
--> 470 json = self.get(
    471     f"https://opensky-network.org/api/flights/departure"
    472     f"?begin={begin_ts.timestamp():.0f}&airport={airport}&"
    473     f"end={end_ts.timestamp():.0f}"
    474 )
    476 return (
    477     pd.DataFrame.from_records(json)
    478     .convert_dtypes(dtype_backend="pyarrow")[
   (...)
    498     .sort_values("firstSeen")
    499 )

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:1053, in Client.get(self, url, params, headers, cookies, auth, follow_redirects, timeout, extensions)
   1036 def get(
   1037     self,
   1038     url: URL | str,
   (...)
   1046     extensions: RequestExtensions | None = None,
   1047 ) -> Response:
   1048     """
   1049     Send a `GET` request.
   1050 
   1051     **Parameters**: See `httpx.request`.
   1052     """
-> 1053     return self.request(
   1054         "GET",
   1055         url,
   1056         params=params,
   1057         headers=headers,
   1058         cookies=cookies,
   1059         auth=auth,
   1060         follow_redirects=follow_redirects,
   1061         timeout=timeout,
   1062         extensions=extensions,
   1063     )

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_client.py:825, in Client.request(self, method, url, content, data, files, json, params, headers, cookies, auth, follow_redirects, timeout, extensions)
    810     warnings.warn(message, DeprecationWarning, stacklevel=2)
    812 request = self.build_request(
    813     method=method,
    814     url=url,
   (...)
    823     extensions=extensions,
    824 )
--> 825 return self.send(request, auth=auth, follow_redirects=follow_redirects)

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_client.py:912, in Client.send(self, request, stream, auth, follow_redirects)
    904 follow_redirects = (
    905     self.follow_redirects
    906     if isinstance(follow_redirects, UseClientDefault)
    907     else follow_redirects
    908 )
    910 self._set_timeout(request)
--> 912 auth = self._build_request_auth(request, auth)
    914 response = self._send_handling_auth(
    915     request,
    916     auth=auth,
    917     follow_redirects=follow_redirects,
    918     history=[],
    919 )
    920 try:

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_client.py:463, in BaseClient._build_request_auth(self, request, auth)
    457 def _build_request_auth(
    458     self,
    459     request: Request,
    460     auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT,
    461 ) -> Auth:
    462     auth = (
--> 463         self._auth if isinstance(auth, UseClientDefault) else self._build_auth(auth)
    464     )
    466     if auth is not None:
    467         return auth

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_client.py:449, in BaseClient._build_auth(self, auth)
    447     return None
    448 elif isinstance(auth, tuple):
--> 449     return BasicAuth(username=auth[0], password=auth[1])
    450 elif isinstance(auth, Auth):
    451     return auth

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_auth.py:133, in BasicAuth.__init__(self, username, password)
    132 def __init__(self, username: str | bytes, password: str | bytes) -> None:
--> 133     self._auth_header = self._build_auth_header(username, password)

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_auth.py:140, in BasicAuth._build_auth_header(self, username, password)
    139 def _build_auth_header(self, username: str | bytes, password: str | bytes) -> str:
--> 140     userpass = b":".join((to_bytes(username), to_bytes(password)))
    141     token = b64encode(userpass).decode()
    142     return f"Basic {token}"

TypeError: sequence item 0: expected a bytes-like object, NoneType found

A basic route database is also accessible through the REST API:

opensky.api_routes("AFR292")
---------------------------------------------------------------------------
TypeError                                 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: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:1053, in Client.get(self, url, params, headers, cookies, auth, follow_redirects, timeout, extensions)
   1036 def get(
   1037     self,
   1038     url: URL | str,
   (...)
   1046     extensions: RequestExtensions | None = None,
   1047 ) -> Response:
   1048     """
   1049     Send a `GET` request.
   1050 
   1051     **Parameters**: See `httpx.request`.
   1052     """
-> 1053     return self.request(
   1054         "GET",
   1055         url,
   1056         params=params,
   1057         headers=headers,
   1058         cookies=cookies,
   1059         auth=auth,
   1060         follow_redirects=follow_redirects,
   1061         timeout=timeout,
   1062         extensions=extensions,
   1063     )

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_client.py:825, in Client.request(self, method, url, content, data, files, json, params, headers, cookies, auth, follow_redirects, timeout, extensions)
    810     warnings.warn(message, DeprecationWarning, stacklevel=2)
    812 request = self.build_request(
    813     method=method,
    814     url=url,
   (...)
    823     extensions=extensions,
    824 )
--> 825 return self.send(request, auth=auth, follow_redirects=follow_redirects)

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_client.py:912, in Client.send(self, request, stream, auth, follow_redirects)
    904 follow_redirects = (
    905     self.follow_redirects
    906     if isinstance(follow_redirects, UseClientDefault)
    907     else follow_redirects
    908 )
    910 self._set_timeout(request)
--> 912 auth = self._build_request_auth(request, auth)
    914 response = self._send_handling_auth(
    915     request,
    916     auth=auth,
    917     follow_redirects=follow_redirects,
    918     history=[],
    919 )
    920 try:

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_client.py:463, in BaseClient._build_request_auth(self, request, auth)
    457 def _build_request_auth(
    458     self,
    459     request: Request,
    460     auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT,
    461 ) -> Auth:
    462     auth = (
--> 463         self._auth if isinstance(auth, UseClientDefault) else self._build_auth(auth)
    464     )
    466     if auth is not None:
    467         return auth

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_client.py:449, in BaseClient._build_auth(self, auth)
    447     return None
    448 elif isinstance(auth, tuple):
--> 449     return BasicAuth(username=auth[0], password=auth[1])
    450 elif isinstance(auth, Auth):
    451     return auth

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_auth.py:133, in BasicAuth.__init__(self, username, password)
    132 def __init__(self, username: str | bytes, password: str | bytes) -> None:
--> 133     self._auth_header = self._build_auth_header(username, password)

File ~/work/traffic/traffic/.venv/lib/python3.11/site-packages/httpx/_auth.py:140, in BasicAuth._build_auth_header(self, username, password)
    139 def _build_auth_header(self, username: str | bytes, password: str | bytes) -> str:
--> 140     userpass = b":".join((to_bytes(username), to_bytes(password)))
    141     token = b64encode(userpass).decode()
    142     return f"Basic {token}"

TypeError: sequence item 0: expected a bytes-like object, NoneType found