WDC data and services

how-to guides

Build download links

Choose data criteria and generate a link to fetch a zip file of data. Open it and your browser downloads a zip file. This is the quickest route for most people, and everything else on this page is a variation on it.

Your download link
  1. Type the three-letter code of an observatory, for example esk for Eskdalemuir, ler for Lerwick or had for Hartland. Separate several codes with commas.
  2. Choose the dates. The “up to” date is the boundary of the period, so use 1 January 2021 to get all of 2020.
  3. Open the link, then unzip the file. It contains your data files and a short summary of your request.

Other ways to get data

If a downloaded zip isn't what you need, other services are also available:

Browse and plot first The Observatory Data Portal lets you look at the data before you download anything. Automate it with curl or Python Repeat downloads for many dates or observatories, or feed the results into your own analysis. Read data straight into your code The text endpoints return CSV or JSON directly, with no zip to unpack. Connect HAPI-aware software Use our HAPI server if your tools already speak the Heliophysics API standard. Try requests in the interactive docs The full reference for every endpoint and option, where you can fill in a form and run a request.

Query parameters

Observatory code
A three-letter IAGA code, such as esk. Lower case is safest in links.
Cadence
How the data are sampled or averaged: second, minute, hour, day, month or year, plus k for the three-hourly local K-index.
Dates
Written year-month-day, such as 2020-01-01, optionally with a time (2020-01-01T00:00:00). All times are UTC. start_dt and end_dt mark the boundaries of the period, so 2020-01-01 to 2021-01-01 requests the year 2020.
File format
CSV opens in spreadsheets and most analysis tools. JSON suits software. IAGA-2002 and WDC are the established geomagnetic text formats. COV-JSON is CoverageJSON, used by the EPOS portal.
Components
XYZF gives north (X), east (Y), vertical (Z) and total field (F). HDZF gives horizontal intensity (H), declination (D), vertical (Z) and total field (F). original returns the data as they are stored. Yearly data use XYZ and HDZ.

Zipped downloads: full details

The link generator above writes links for zipped downloads, with the template for any download shown below. One request returns one zip file, and you can ask for several observatories at once. Zipped downloads are also the only route that offers the WDC format (minute and hourly means) and, for most cadences, IAGA-2002.

https://wdcapi.bgs.ac.uk/data/zipped-data/{cadence}/{observatories}?start_dt={start}&end_dt={end}&file_format={format}

Example - three observatories in WDC format

Hourly means for Eskdalemuir, Lerwick and Hartland for 2020:

https://wdcapi.bgs.ac.uk/data/zipped-data/hour/esk,ler,had?start_dt=2020-01-01&end_dt=2021-01-01&file_format=wdc&orientation=original
Download this example

Your browser may show the commas as %2C. They mean the same thing.

Particular months instead of dates

Minute, hourly and daily data can also be selected with years and months. Use one method or the other in a request, not both.

https://wdcapi.bgs.ac.uk/data/zipped-data/hour/esk?years=2020&months=1,2,3&file_format=csv

This requests January, February and March 2020. Leave months out to get whole years. For several years, separate them with dashes and give one group of months per year, for example years=2020-2021&months=1,2,3-1,10,11,12. Monthly and yearly cadences accept years only.

Which formats does each cadence offer?

Minute, hourly
CSVJSONIAGA-2002WDCXMLCOV-JSON
Daily, monthly, K-index
CSVJSONIAGA-2002XMLCOV-JSON
Yearly
CSVJSONXMLCOV-JSON
One-second
JSONIAGA-2002
Show all parameters
cadence
Part of the path: second, minute, hour, day, month, year or k.
observatories
Part of the path. One or more codes separated by commas. For month and year you can enter ALL for every observatory.
start_dt, end_dt
Boundaries of the period. One-second data take plain dates, and IAGA-2002 always returns whole days.
years, months
Alternative to dates, as described above. Not available for one-second data.
file_format
Required. See the formats above.
orientation
original (default), XYZF or HDZF; XYZ or HDZ for yearly data. Not offered for K-index or one-second data.
group_by
Minute means only. day (default, as in IAGA-2002) or month for one file per month.
exclude
Monthly and yearly means only. Codes to leave out, handy together with ALL.

Large requests

The size of a single download is limited. If a request is too large the server replies with 413 Request Entity Too Large. The fix is to split the period into smaller pieces and download them one after another. For ten years of minute data from multiple observatories, one year at a time works well.

https://wdcapi.bgs.ac.uk/data/zipped-data/minute/esk,ler,had?start_dt=2010-01-01&end_dt=2011-01-01&file_format=wdc

https://wdcapi.bgs.ac.uk/data/zipped-data/minute/esk,ler,had?start_dt=2011-01-01&end_dt=2012-01-01&file_format=wdc

https://wdcapi.bgs.ac.uk/data/zipped-data/minute/esk,ler,had?start_dt=2012-01-01&end_dt=2013-01-01&file_format=wdc

Keep a note of what you requested and check that each request finished. A successful reply doesn't guarantee that every date holds observations, because coverage varies between observatories and periods. The API documentation has more details on limits; for example, minute means are limited to 10 total years of data per download. The scripts below show how to loop over years.

Check what data exist

Before a big download, you can see which months have data. The availability endpoint lists, for each observatory and year, the months we hold for a chosen cadence.

https://wdcapi.bgs.ac.uk/data/data-availability/hour?obs_code=esk&start_dt=2020-01-01&end_dt=2021-01-01&formatting=simple-json
Check Eskdalemuir for 2020

Swap hour for second, minute, day, month, year or k. obs_code takes one or more codes; leave it out, along with the dates, and you get every observatory for all time, The formatting option is simple-json (default) or nested-json. The response describes availability only and contains no magnetic data.

A trimmed example of a response:

{
    "bsl": {
        "2021": { "months": [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12] },
        "2025": { "months": [] }
    }
}

An empty list means no data for that year. Here September 2021 is missing.

Text endpoints

These return data directly in the response as CSV, JSON or COV-JSON instead of a zip, so a program can read them immediately. They cover one observatory at a time, and split large requests into pages, and are not designed for bulk downloads across many observatories, but are useful for scraping large volumes of targetted data. They don't offer WDC, IAGA-2002 or XML; use zipped downloads for those.

https://wdcapi.bgs.ac.uk/data/text-data/cadence/observatory?start_dt=start&end_dt=end&file_format=format&months_per_page=n&page=p

Try one month of minute data

https://wdcapi.bgs.ac.uk/data/text-data/minute/esk?start_dt=2010-01-01&end_dt=2010-02-01&file_format=json&orientation=original&page=1&months_per_page=1
View in browser

How paging works

Every response includes page and total pages, plus a data list. Ask for ten years (2010-01-01 to 2020-01-01) with months_per_page=1 and total pages is 120, so you collect everything by requesting page=1, page=2, and so on up to page=120. Always read total pages from the response rather than assuming it.

For day, hour, minute and K-index requests you must include months_per_page. The most each cadence allows:

2minute
12hour
36K-index
240day

Monthly and yearly requests don't use months_per_page. If there's nothing for your dates you get a 202 reply with an empty data list.

Python: collect every page into a table
import requests
import pandas as pd

url = "https://wdcapi.bgs.ac.uk/data/text-data/minute/esk"
params = {
    "start_dt": "2010-01-01",
    "end_dt": "2020-01-01",
    "file_format": "json",
    "orientation": "original",
    "months_per_page": 1,
    "page": 1,
}

rows = []
while True:
    r = requests.get(url, params=params, timeout=120)
    r.raise_for_status()
    body = r.json()
    rows.extend(body["data"])
    if params["page"] >= body["total pages"]:
        break
    params["page"] += 1

df = pd.DataFrame(rows)
print(df.head())
What a response looks like
{
    "iaga-code": "...",
    "latitude": ...,
    "longitude": ...,
    "data-quality": "...",
    "converted from": "...",
    "page": 1,
    "total pages": 120,
    "data": [
        { "X": ..., "Y": ..., "Z": ..., "datetime": "..." }
    ]
}

The names of the data columns depend on the orientation you ask for.

One-second data

One-second data are available as JSON for a single observatory, with pages measured in days (days_per_page, 1 or 2, default 2):

https://wdcapi.bgs.ac.uk/data/text-data/second/kir?start_dt=2024-01-01&end_dt=2024-01-03&page=1

The “Larger requests via API” section of the FAQs on the World Data Centre data portal also has a ready-made scraping script.

Download with curl or Python

Scripts are the way to repeat downloads, loop over years, or fetch many observatories. Each example below saves a zip file.

curl is a command-line tool for web requests, available on most Linux and macOS systems and installable on Windows. Put the link in quotes so your shell treats it as one argument.

curl -fL -o esk-hourly-2020.zip "https://wdcapi.bgs.ac.uk/data/zipped-data/hour/esk?start_dt=2020-01-01&end_dt=2021-01-01&file_format=wdc&orientation=original"

-f reports HTTP errors as failures, -L follows redirects and -o names the file to save. A 202 “no data” reply is not an error to curl, so if the saved file is empty, this is why.

This uses only Python's standard library.

import zipfile
from urllib.request import urlretrieve

url = (
    "https://wdcapi.bgs.ac.uk/data/zipped-data/hour/esk"
    "?start_dt=2020-01-01"
    "&end_dt=2021-01-01"
    "&file_format=wdc"
    "&orientation=original"
)
filename = "esk-hourly-2020.zip"

urlretrieve(url, filename)

if zipfile.is_zipfile(filename):
    print("Downloaded", filename)
else:
    # No data for these criteria: the server sent a short message instead of a zip.
    print(open(filename).read())

This needs the requests package (pip install requests) and fetches two years of minute data, one year per request.

import requests
from pathlib import Path
from zipfile import is_zipfile
from io import BytesIO

url = "https://wdcapi.bgs.ac.uk/data/zipped-data/minute/esk"

# Save files in a dedicated folder
output_dir = Path.cwd() / "esk_data"
output_dir.mkdir(parents=True, exist_ok=True)

print("Saving files to:", output_dir.resolve())

for year in range(2010, 2012):
    try:
        r = requests.get(
            url,
            params={"years": year, "file_format": "csv"},
            timeout=600
        )

        print(f"\nYear: {year}")
        print("Status:", r.status_code)
        print("Content type:", r.headers.get("Content-Type"))
        print("Downloaded bytes:", len(r.content))

        if r.status_code == 200:
            if is_zipfile(BytesIO(r.content)):
                file_path = output_dir / f"esk_minute_{year}.zip"
                file_path.write_bytes(r.content)
                print("Saved:", file_path.resolve())
            else:
                print("Response is not a valid ZIP file.")
                print(r.text)

        elif r.status_code == 202:
            print("Request accepted, but data is not yet available in this response.")
            print(r.text)

        else:
            print("Request failed:", r.text)

    except requests.RequestException as e:
        print(f"Error downloading {year}: {e}")

For scripts you rely on, add retries and check that each zip opens before you use it.

HAPI server

We also have a HAPI server. Our server follows version 3.2 of the Heliophysics API (HAPI) specification, a standard way for software to discover time series, inspect their parameters and fetch data for a time range. Tools that read HAPI servers, such as Autoplot or the hapiclient packages for Python, IDL and MATLAB, can be pointed at https://wdcapi.bgs.ac.uk/hapi.

/hapi/capabilities
The HAPI version and output formats (csv, json).
/hapi/about
Server ID, title and contact information.
/hapi/catalog
Every available dataset. Open the catalogue.
/hapi/info
The parameters and details of one dataset.
/hapi/data
Streams the data for a dataset and time range.

Dataset names

Each dataset is named observatory/cadence/orientation, so this is Eskdalemuir minute means in XYZ:

esk/PT1M/xyz

Cadence

PT1M minute · PT1H hourly · P1D daily · P1M monthly · P1Y yearly · PT3H K-index

Orientation

xyz · hdz · original (as stored) · k (three-hourly K-index only)

Inspect a dataset

https://wdcapi.bgs.ac.uk/hapi/info?dataset=esk/PT1M/original

Other examples: esk/PT1M/xyz, esk/PT1M/hdz and esk/PT3H/k. Check the catalogue for the datasets that exist.

Fetch data

One day of Eskdalemuir minute data as CSV, then the same as JSON:

https://wdcapi.bgs.ac.uk/hapi/data?dataset=esk/PT1M/original&format=csv&start=2020-01-01T00:00:00Z&stop=2020-01-02T00:00:00Z
https://wdcapi.bgs.ac.uk/hapi/data?dataset=esk/PT1M/original&format=json&start=2020-01-01T00:00:00Z&stop=2020-01-02T00:00:00Z
Open the CSV example

format is csv (default) or json. Add include=header to receive the dataset description with the data, or parameters= to choose columns. Times are UTC and the stop time marks the end of the period.

Request limits

A single HAPI data request can cover at most:

2 yearsminute means
20 yearshourly means
50 yearsK-index
No limitdaily, monthly, yearly

The maxRequestDuration value in a dataset's /hapi/info response is the definitive limit for that dataset. For longer periods, make several requests.

Use it from Python

import pandas as pd

url = ("https://wdcapi.bgs.ac.uk/hapi/data"
    "?dataset=esk/PT1M/xyz&format=csv"
    "&start=2020-01-01T00:00:00Z&stop=2020-01-02T00:00:00Z")

df = pd.read_csv(url, header=None,
                names=["time", "x", "y", "z", "original_format"],
                parse_dates=["time"])
print(df.head())
# pip install hapiclient
from hapiclient import hapi

server = "https://wdcapi.bgs.ac.uk/hapi"
dataset = "esk/PT1M/xyz"
start, stop = "2020-01-01T00:00:00Z", "2020-01-02T00:00:00Z"

data, meta = hapi(server, dataset, "", start, stop)  # "" means all parameters

Other data and metadata

The same API serves several other datasets. Each of these is a link you can paste into your browser.

Monthly means in BGS format

Our database of observatory monthly means, compiled from definitive hourly data and near-definitive minute data, joined into one series per observatory in a format designed after IAGA-2002.

https://wdcapi.bgs.ac.uk/monthly-means?obs_code=esk

Leave out obs_code to download every observatory (this may take a few minutes), or add include_plot=true for PNG plots comparing each observatory with field models.

curl -X 'GET' \
    'https://wdcapi.bgs.ac.uk/monthly-means' \
    -H 'accept: application/json'

There's also a browsable version on the Observatory Monthly Means page.

Annual means and jump information
https://wdcapi.bgs.ac.uk/data/annual-means-catalogue
https://wdcapi.bgs.ac.uk/data/annual-means-discontinuities?obs_code=esk

Jumps come from changes of site or instrument. Values are old minus new, and no corrections have been applied. To make earlier annual means consistent with later ones, for example when calculating secular variation, subtract the jump from the earlier values.

Global geomagnetic survey data
https://wdcapi.bgs.ac.uk/global-geomagnetic-survey?start_dt=2020-01-01&end_dt=2021-01-01&file_format=csv

Formats are json, csv and geo-json. Without dates you get the last five years. Limit the area with north-latitude, south-latitude, west-longitude and east-longitude, and choose measurement types with include-one-off, include-repeat-station, include-observatory, include-aeromag, include-marine-vector, include-marine-scalar and include-satellite. By default only one-off and repeat-station results are included.

Observatory metadata and locations
https://wdcapi.bgs.ac.uk/metadata/observatory-metadata/esk

Instruments, locations, institutes and contacts for one observatory. Leave off the code to get every observatory, which is a much larger response.

https://wdcapi.bgs.ac.uk/metadata/location?observatory=esk

Latitude, longitude, elevation and opening and closing dates, including past moves.

GeoJSON for maps
https://wdcapi.bgs.ac.uk/metadata/wdc-geojson/

Observatory locations for mapping software. Note that this version lists coordinates as [latitude, longitude], whereas the EPOS version (/metadata/epos-geojson/) uses [longitude, latitude].

Versions that add IGRF-14 field values (/metadata/wdc-geojson-igrf) or quasi-dipole coordinates (/metadata/wdc-geojson-qd-coords) need a datetime parameter.

Yearbooks
https://wdcapi.bgs.ac.uk/metadata/yearbooks?observatory=esk

Links to digitised yearbooks. INTERMAGNET only version is also available at /metadata/intermagnet-yearbooks.

If something goes wrong

The server tells you what happened with a status code.

200
Success.
202
The request was fine but there are no data for those criteria. Check the dates and codes, and use the availability endpoint.
400422
Something in the link isn't valid, such as a misspelt code, a format the cadence doesn't offer, or an end date before the start. The reply says which part to fix.
413
The request is too large. Split it into smaller periods.
500
A problem on our side. Try again, and tell us if it persists.

Common questions

Which format should I choose?
CSV is convenient for spreadsheets and general analysis. JSON suits code. Choose WDC or IAGA-2002 if your workflow needs those established geomagnetic formats. The choices depend on the cadence, as shown in the formats list above. The Python package GeoMagpy is designed to read WDC and IAGA-2002 files, and can convert them to CSV or JSON. It also reads zipped downloads directly, so you can fetch a zip and pass it straight to GeoMagpy without unzipping.
What's the difference between zipped/text endpoints and the HAPI server?
Zipped downloads package files for you to save and support the most formats, including WDC and IAGA-2002. Text endpoints return CSV, JSON or COV-JSON directly, one observatory at a time, in pages. HAPI is a standard interface that compatible software can use to discover datasets and fetch time series.
Can I download several observatories at once?
Yes. With zipped downloads, list the codes separated by commas, such as esk,ler,had. For monthly and yearly means you can enter ALL. Text endpoints and HAPI are one at a time.
Where can I find every parameter?
The interactive API documentation lists every endpoint with its parameters, formats and example responses. The machine-readable OpenAPI specification is also available. For HAPI, use the catalogue and each dataset's info response.

Still stuck? Email wdcgeomag@bgs.ac.uk and include the link you used.

Acknowledging the data

Observatory data are the work of the teams that run each observatory. If you use them in a publication, please acknowledge them. Ready-made acknowledgement text and contact details for any observatory are available at:

https://wdcapi.bgs.ac.uk/acknowledgements/esk
See Eskdalemuir example

« Back to all data and data products