Quickstart

The Skyfora API gives you Global Navigation Satellite System (GNSS) measurement data, reference station metadata, and network information. Use it in your own applications and workflows.

This guide shows how to create an API key, find your stations, and read measurement data.

1. Create an API key

Sign in to the Skyfora Portal and create an API key under your organization settings. The key is scoped to your organization and grants access to the stations and networks you are entitled to.

Include the key in every API request:

X-API-Key: your-api-key-here

2. List your stations

Find your stations before you query measurements. The stations endpoint returns metadata for every station your organization is entitled to. This includes the id that measurement queries need, and the display_name used across Skyfora products.

curl https://api.skyfora.com/api/v1/stations \
  -H "X-API-Key: your-api-key-here"

Response:

{
  "stations": [
    {
      "id": "4f9b1c2e-6a70-4a3e-9a4f-2d8f5b7c0e11",
      "display_name": "BERG001",
      "source_type": "cors",
      "resolution_seconds": 30,
      "country_code": "DE",
      "latitude": 50.94,
      "longitude": 6.96
    },
    {
      "id": "7c2d9e40-1b5f-4c8a-8d3e-6f0a9b4c2d55",
      "display_name": "SEED001",
      "source_type": "cors",
      "resolution_seconds": 30,
      "country_code": "DE",
      "latitude": 52.52,
      "longitude": 13.41
    }
  ],
  "pagination": { "page": 1, "page_size": 20, "total_items": 2, "total_pages": 1 }
}

Use the id values when querying measurements in the next step.

You can filter stations by country, network, bounding box, or search term - see the Stations API reference for details.

3. Query measurements

Use the measurements endpoint to retrieve observation data for your stations. Specify a time range, one or more station ids, and the variables you want.

Available variables

The variables are sourced or derived from GNSS observations. The observation rate may differ for each station. Read resolution_seconds from GET /stations for the epoch interval of a given station.

Variable Description Unit
ztd Zenith Total Delay mm
ztd_sigma Zenith Total Delay uncertainty (standard deviation of the estimate) mm
pwv Precipitable Water Vapour mm
snr Carrier-to-noise density ratio (C/N0), per satellite and signal band dB-Hz
el Satellite elevation (per satellite) degrees
az Satellite azimuth (per satellite) degrees
prn Satellite pseudo-random noise (PRN) identifiers -

How Precipitable Water Vapour is derived: Skyfora estimates Precipitable Water Vapour (PWV) from Zenith Total Delay (ZTD). The estimate also uses surface pressure, air temperature at 2 m, and relative humidity. These meteorological inputs come from the nearest suitable observation, or from a numerical weather prediction (NWP) model when no observation is available. Skyfora adjusts them for the height of the GNSS site. The API response does not return these inputs. They are not necessarily measured at the GNSS station itself.

Data-quality filtering

Measurement responses are quality-filtered by default. The count in a JSON response counts only the filtered rows it returns.

Set "raw": true in the measurements request body to include observations excluded by the default filter. Use this mode for diagnostics.

Response formats

The API supports two response formats via content negotiation:

  • NetCDF-4 (default) - binary scientific data format with CF-1.8 conventions. Returned when no Accept header is set, or with Accept: application/x-netcdf.
  • JSON - returned when Accept: application/json is set.

NetCDF-4 is the recommended format for data analysis workflows. It can be read directly by Python xarray, MATLAB, R ncdf4, and other NetCDF-compatible tools.

Download a NetCDF file

curl -X POST https://api.skyfora.com/api/v1/measurements/query \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key-here" \
  -o measurements.nc \
  -d '{
    "start_time": "2026-03-20T00:00:00Z",
    "end_time": "2026-03-21T00:00:00Z",
    "station_ids": ["4f9b1c2e-6a70-4a3e-9a4f-2d8f5b7c0e11"],
    "data_vars": ["ztd", "ztd_sigma", "pwv"]
  }'

Fetch measurements as JSON

curl -X POST https://api.skyfora.com/api/v1/measurements/query \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "X-API-Key: your-api-key-here" \
  -d '{
    "start_time": "2026-03-20T00:00:00Z",
    "end_time": "2026-03-20T01:00:00Z",
    "station_ids": ["4f9b1c2e-6a70-4a3e-9a4f-2d8f5b7c0e11"],
    "data_vars": ["ztd", "ztd_sigma", "pwv"]
  }'

To retrieve unfiltered observations for diagnostics, add "raw": true to the JSON body:

curl -X POST https://api.skyfora.com/api/v1/measurements/query \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "X-API-Key: your-api-key-here" \
  -d '{
    "start_time": "2026-03-20T00:00:00Z",
    "end_time": "2026-03-20T01:00:00Z",
    "station_ids": ["4f9b1c2e-6a70-4a3e-9a4f-2d8f5b7c0e11"],
    "data_vars": ["ztd", "ztd_sigma"],
    "raw": true
  }'

Response:

{
  "data": [
    {
      "timestamp": "2026-03-20T00:00:00.000Z",
      "station_id": "4f9b1c2e-6a70-4a3e-9a4f-2d8f5b7c0e11",
      "display_name": "BERG001",
      "ztd": 2353.5,
      "ztd_sigma": 4.0,
      "pwv": 12.3
    },
    {
      "timestamp": "2026-03-20T00:15:00.000Z",
      "station_id": "4f9b1c2e-6a70-4a3e-9a4f-2d8f5b7c0e11",
      "display_name": "BERG001",
      "ztd": 2354.0,
      "ztd_sigma": 4.0,
      "pwv": 12.4
    }
  ],
  "count": 2
}

4. Analyze the data

Read NetCDF with Python

import xarray as xr

ds = xr.open_dataset("measurements.nc")

# Inspect the dataset. Stations are identified by station_id and display_name
print(ds)

# Convert to a pandas DataFrame and select one station
df = ds.to_dataframe().reset_index()
station = df[df.station_id == "4f9b1c2e-6a70-4a3e-9a4f-2d8f5b7c0e11"]
print(station.head())

Query multiple stations

curl -X POST https://api.skyfora.com/api/v1/measurements/query \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key-here" \
  -o measurements.nc \
  -d '{
    "start_time": "2026-03-20T00:00:00Z",
    "end_time": "2026-03-21T00:00:00Z",
    "station_ids": ["4f9b1c2e-6a70-4a3e-9a4f-2d8f5b7c0e11", "7c2d9e40-1b5f-4c8a-8d3e-6f0a9b4c2d55"],
    "data_vars": ["ztd", "pwv"]
  }'

Access control

Your API key is scoped to your organization. You can only access stations and measurement data for networks your organization is entitled to. Requests for stations outside your entitlements will return a 403 error.

Limits

The API returns all observations matching your query in a single response. If the estimated result exceeds 150 MB, the request returns 413. If a query runs past the processing deadline, it returns 408.

As a rough guide, querying all 7 variables for 50 stations over 30 days produces approximately 60 MB. At 100 stations with the same parameters and time range, you would approach the limit. Numeric-only queries (ztd, ztd_sigma, pwv) are much smaller and can cover more stations and longer time ranges in a single request.

If your query exceeds a limit, split it into several requests. Divide by time range, or by groups of stations, then combine the results on your side.

Insight dashboard

The Skyfora Insight dashboard also plots every measurement variable the API offers: Zenith Total Delay, Zenith Total Delay uncertainty, Precipitable Water Vapour, satellite elevation, satellite azimuth, and carrier-to-noise density ratio. Your organization needs access to the matching parameters. Insight applies the same default data-quality filter as the API.