MPC Observations API¶
This tutorial provides information on how to use the Minor Planet Center's Observations API.¶
The Minor Planet Center's Observations service returns the observational data for specified solar system objects.
This can be useful if you want to:
- Retrieve all observations of an asteroid or comet
- Analyze the observational history of an object
- Perform your own orbit calculations using raw observation data
- Compare observations from different observatories or time periods
The Observations API is a REST endpoint. You can use your language of choice to send GET requests to:
https://data.minorplanetcenter.net/api/get-obs
In the examples below we use Python code to query the API.
Further information and documentation concerning the Observations API can be found at:
Import Packages¶
Here we import the standard Python packages needed to call the API and interpret the returned data.
import requests
import json
import pandas as pd
API Parameters¶
The Observations API accepts the following parameters:
| Parameter | Type | Required | Description | Default |
|---|---|---|---|---|
desigs |
List of strings | Yes | Name, permanent number, or provisional designation(s) of the object(s) | None |
output_format |
List of strings | No | One or more of: XML, ADES_DF, OBS_DF, OBS80 |
XML |
ades_version |
String | No | ADES format version: 2017 or 2022 |
2022 |
Output Formats Explained¶
- XML: Observations in ADES XML format (standard for data exchange)
- OBS80: Observations in the classic MPC 80-column format
- ADES_DF: List of dictionaries with ADES field names (ideal for pandas DataFrames)
- OBS_DF: List of dictionaries with MPC 80-column field names (ideal for pandas DataFrames)
Basic Query: Single Object¶
Here we query for observations of asteroid (101955) Bennu, the target of the OSIRIS-REx mission.
We request the OBS80 format, which returns observations in the classic 80-column format.
# Query observations for Bennu in OBS80 format
response = requests.get(
"https://data.minorplanetcenter.net/api/get-obs",
json={"desigs": ["Bennu"], "output_format": ["OBS80"]}
)
if response.ok:
obs80_string = response.json()[0]['OBS80']
# Print just the first few observations
lines = obs80_string.strip().split('\n')
print(f"Total observations: {len(lines)}")
print("\nFirst 5 observations:")
print('\n'.join(lines[:5]))
else:
print(f"Error: {response.status_code} - {response.content}")
XML Format (ADES Standard)¶
The ADES (Astrometry Data Exchange Standard) XML format is the modern standard for exchanging astrometric observations. It contains rich metadata about each observation.
# Query observations in XML format
response = requests.get(
"https://data.minorplanetcenter.net/api/get-obs",
json={"desigs": ["2023 BU"], "output_format": ["XML"]}
)
if response.ok:
xml_string = response.json()[0]['XML']
# Print the first 2000 characters to see the structure
print(xml_string[:2000])
print("\n... (truncated for display)")
else:
print(f"Error: {response.status_code} - {response.content}")
Working with Pandas DataFrames¶
For data analysis in Python, the ADES_DF and OBS_DF formats are most convenient. These return lists of dictionaries that can be directly converted to pandas DataFrames.
ADES DataFrame Format¶
The ADES format uses standardized field names and includes additional metadata fields.
# Query observations in ADES DataFrame format
response = requests.get(
"https://data.minorplanetcenter.net/api/get-obs",
json={"desigs": ["Bennu"], "output_format": ["ADES_DF"]}
)
if response.ok:
ades_df = pd.DataFrame(response.json()[0]['ADES_DF'])
print(f"Shape: {ades_df.shape[0]} observations x {ades_df.shape[1]} columns")
print(f"\nColumns: {list(ades_df.columns)}")
print("\nFirst 5 rows (selected columns):")
display_cols = ['permID', 'provID', 'obsTime', 'ra', 'dec', 'mag', 'stn']
available_cols = [c for c in display_cols if c in ades_df.columns]
print(ades_df[available_cols].head())
else:
print(f"Error: {response.status_code} - {response.content}")
OBS DataFrame Format¶
The OBS_DF format uses field names corresponding to the classic MPC 80-column format.
# Query observations in OBS DataFrame format
response = requests.get(
"https://data.minorplanetcenter.net/api/get-obs",
json={"desigs": ["Bennu"], "output_format": ["OBS_DF"]}
)
if response.ok:
obs_df = pd.DataFrame(response.json()[0]['OBS_DF'])
print(f"Shape: {obs_df.shape[0]} observations x {obs_df.shape[1]} columns")
print(f"\nColumns: {list(obs_df.columns)}")
print("\nFirst 5 rows:")
print(obs_df.head())
else:
print(f"Error: {response.status_code} - {response.content}")
Requesting Multiple Formats¶
You can request multiple output formats in a single API call. This is efficient when you need data in different formats for different purposes.
# Request both DataFrame formats in one call
response = requests.get(
"https://data.minorplanetcenter.net/api/get-obs",
json={"desigs": ["Bennu"], "output_format": ["ADES_DF", "OBS_DF"]}
)
if response.ok:
result = response.json()[0]
print(f"Keys in response: {list(result.keys())}")
ades_df = pd.DataFrame(result['ADES_DF'])
obs_df = pd.DataFrame(result['OBS_DF'])
print(f"\nADES_DF shape: {ades_df.shape}")
print(f"OBS_DF shape: {obs_df.shape}")
else:
print(f"Error: {response.status_code} - {response.content}")
Multi-Object Query¶
At the time this tutorial is created, the API is restricted to providing information on only a single designation.
Attempts to query multiple designations in a single call will result in an error message.
# Query observations for multiple objects
response = requests.get(
"https://data.minorplanetcenter.net/api/get-obs",
json={
"desigs": ["Ceres", "Pallas", "Vesta"],
"output_format": ["ADES_DF"]
}
)
if response.ok:
results = response.json()
print(f"Number of objects returned: {len(results)}")
for i, result in enumerate(results):
df = pd.DataFrame(result['ADES_DF'])
# Get the object identifier from the first row
obj_id = df['permID'].iloc[0] if 'permID' in df.columns else f"Object {i+1}"
print(f"\n{obj_id}: {len(df)} observations")
else:
print(f"Error: {response.status_code} - {response.content}")
Different Designation Types¶
The API accepts various types of object designations:
- Names:
Bennu,Ceres,Apophis - Permanent numbers:
101955,1,99942 - Provisional designations:
2023 BU,1999 RQ36
All of these will work for the same object.
# Query the same object using different designation types
designations = ["Bennu", "101955", "1999 RQ36"]
for desig in designations:
response = requests.get(
"https://data.minorplanetcenter.net/api/get-obs",
json={"desigs": [desig], "output_format": ["ADES_DF"]}
)
if response.ok:
df = pd.DataFrame(response.json()[0]['ADES_DF'])
print(f"Queried '{desig}': {len(df)} observations returned")
else:
print(f"Queried '{desig}': Error {response.status_code}")
ADES Version Parameter¶
The ADES standard has two versions: 2017 and 2022. The 2022 version is the default and includes additional fields. You can specify which version you want using the ades_version parameter.
# Compare ADES 2017 vs 2022 formats
for version in ["2017", "2022"]:
response = requests.get(
"https://data.minorplanetcenter.net/api/get-obs",
json={
"desigs": ["2023 BU"],
"output_format": ["ADES_DF"],
"ades_version": version
}
)
if response.ok:
df = pd.DataFrame(response.json()[0]['ADES_DF'])
print(f"ADES {version}: {df.shape[1]} columns")
print(f" Columns: {list(df.columns)}\n")
else:
print(f"ADES {version}: Error {response.status_code}")
Example Analysis: Observation History¶
Let's analyze the observational history of an asteroid. We'll look at when observations were made and by which observatories.
# Get observations of near-Earth asteroid Apophis
response = requests.get(
"https://data.minorplanetcenter.net/api/get-obs",
json={"desigs": ["Apophis"], "output_format": ["ADES_DF"]}
)
if response.ok:
df = pd.DataFrame(response.json()[0]['ADES_DF'])
# Convert observation time to datetime
df['obstime'] = pd.to_datetime(df['obstime'],format='mixed')
df['year'] = df['obstime'].dt.year
print(f"Apophis Observation Summary")
print(f"="*40)
print(f"Total observations: {len(df)}")
print(f"Date range: {df['obstime'].min().date()} to {df['obstime'].max().date()}")
print(f"\nObservations by year:")
print(df['year'].value_counts().sort_index())
print(f"\nTop 10 observatories (by observation count):")
print(df['stn'].value_counts().head(10))
else:
print(f"Error: {response.status_code} - {response.content}")
Error Handling¶
The API returns standard HTTP status codes. Here we demonstrate how to handle common error cases.
# Query for a non-existent object
response = requests.get(
"https://data.minorplanetcenter.net/api/get-obs",
json={"desigs": ["NotARealAsteroid12345"], "output_format": ["ADES_DF"]}
)
print(f"Status code: {response.status_code}")
print(f"Response OK: {response.ok}")
if response.ok:
result = response.json()
print(f"Response content: {result}")
else:
print(f"Error content: {response.content}")
Summary¶
The MPC Observations API provides access to the complete observational record of solar system objects. Key points:
- Endpoint:
https://data.minorplanetcenter.net/api/get-obs - Required parameter:
desigs- list of object designations - Output formats:
XML,OBS80,ADES_DF,OBS_DF - Recommended for analysis: Use
ADES_DForOBS_DFwith pandas
For questions or feedback, contact the MPC via the Jira Helpdesk.