I found the python package HorizonJPL in the Python Package Index, but it looks like it's limited to activity in 2013. When I go to the linked documentation page https://docs.google.com/document/d/1g9q3ln9LVAATOZ15986HLTCaqcAj_Jd8e_jOGS3YWrE/pub the info is fairly sparse, and the two links there lead to a dead end in Japanese (below).

Is there a way I can access JPL Horizons for tables of data (Like I am doing here) from within a Python script? In other words, I want to build a url query and receive a json of text with state vectors in return using urllib or urllib2.

Update: This is a typical example of what I want to do:

enter image description here

This is what is displayed on the pypi site - there seems to be names/handles but I don't know how to query them.

API Documentation



Planetary Data System: http://pds.nasa.gov/

Jet Propulsion Labs: http://www.jpl.nasa.gov/

HORIZON User Manual: http://ssd.jpl.nasa.gov/?horizons_doc


Matthew Mihok (@mattmattmatt)

Dexter Jagula (@djagula)

Siddarth Kalra (@SiddarthKalra)

Tiago Moreira (@Kamots)

Here is what happens when I try the links in the google doc:

enter image description here

enter image description here

  • If this doesn't attract any help in say another week, then maybe a good candidate for migration to stackoverflow at that time. The python tag is critical there. – uhoh Jun 4 '16 at 11:33
  • 1
    Have you tried using telnetlib to access the telnet interface? – 2012rcampion Jun 4 '16 at 20:25
  • @2012rcampion OK - I'll look into it today! If I understand correctly, I would have to write a python script that basically emulates a person typing commands and receiving prompts. It's possible someone has done a good job of this and written a nice interface wrapper - my ultimate goal is to see if anyone has made something like that generally available. In the mean time I will definitely try what you have suggested. Thanks!! – uhoh Jun 5 '16 at 2:03
  • 1
    If you're not intending to use the script for a long time, you don't have to process the prompts at all. What I did was to get one dataset manually, then have my script basically replay the same session with one configurable parameter. – 2012rcampion Jun 5 '16 at 4:44
  • @2012rcampion OK got it. The manual is helpful, but I'm searching for example sessions, which are easier for me to digest. I've added an example of what I want to do in the question. If I get it working, I'll post it as an answer - if someone else posts a similar example telnet session or a script first, gosh I'll be forced to accept it! :) – uhoh Jun 5 '16 at 8:44
up vote 4 down vote accepted

callhorizons is depricated now and refers to the python library astroquery which now seems to be the way to go.

astroquery (GitHub, readthedocs) is "an astropy affiliated package that contains a collection of tools to access online Astronomical data. Each web service has its own sub-package.", where making Horizons queries is just one of many options. It's part of the larger Astropy Project which is "a community effort to develop a common core package for Astronomy in Python and foster an ecosystem of interoperable astronomy packages."

I recommend reading documentation page for JPL Horizons queries for astroquery can be found here: https://astroquery.readthedocs.io/en/latest/jplhorizons/jplhorizons.html It lists a few good examples, e.g.:

from astroquery.jplhorizons import Horizons
obj = Horizons(id='Ceres', location='568',
                epochs={'start':'2010-01-01', 'stop':'2010-03-01', 'step':'10d'})
eph = obj.ephemerides()

targetname    datetime_str   datetime_jd ...   GlxLat  RA_3sigma DEC_3sigma
   ---            ---             d      ...    deg      arcsec    arcsec
---------- ----------------- ----------- ... --------- --------- ----------
   1 Ceres 2010-Jan-01 00:00   2455197.5 ... 24.120057       0.0        0.0
   1 Ceres 2010-Jan-11 00:00   2455207.5 ... 20.621496       0.0        0.0
   1 Ceres 2010-Jan-21 00:00   2455217.5 ... 17.229529       0.0        0.0
   1 Ceres 2010-Jan-31 00:00   2455227.5 ...  13.97264       0.0        0.0
   1 Ceres 2010-Feb-10 00:00   2455237.5 ... 10.877201       0.0        0.0
   1 Ceres 2010-Feb-20 00:00   2455247.5 ...  7.976737       0.0        0.0

I also recommend skimming the docs of the HorizonsClass. In particular, I was confused initially that setting target id=399 didn't target Earth, but that was because the class also takes a id-type argument that defaults to smallbody (asteroids and the like), and needed to be changed to majorbody.

It seems very straight forward to use, and is clearly the way to go now.

PS: I will mention I got an error KeyError: 'Obsrv-lon' for my particular query, but the issue is being investigated.

  • This seems like potentially very good news! I've just asked How exactly to use astroquery.jplhorizons to get the state vectors of Mercury and the Parker Solar Probe? – uhoh Sep 13 at 13:14
  • 1
    This is good information, but would benefit from slightly more explanation in the post rather than just a link. Does the astroquery project have a readme or mission statement that you could quote here, perhaps? – Bear Sep 13 at 13:14
  • If you can make a small edit and add a bit more information as described above I can click-accept your answer, thanks! – uhoh Sep 13 at 14:13
  • 1
    Ok, I added a bit more info :) – Gandalf Saxe Sep 14 at 7:23
  • Thanks, looks great! I'll give it a try today. – uhoh Sep 15 at 1:25

There is one now! I've just run across the python package callhorizons in github and Python Package Index.

There is more information in readthedocis.io which helps explain how to do queries if you are looking for asteroids/minor bodies, for which there are zillions and the names can be people's name, or alphanumerics.

I'll try to test it further and update here. It looks like there is a lot of capability here, but I can't say for sure yet. There is a blog post by an astronomer but it's from January 2016.

It was a one-line install, namely pip install callhorizons. I then typed in the simple demo at the top of the PyPI page and got an answer!

>>> import callhorizons
>>> eros = callhorizons.query('Eros')
>>> eros.set_discreteepochs([2457446.177083, 2457446.182343])
>>> eros.get_ephemerides(568)
>>> print(eros['RA'], eros['DEC'])
(array([ 292.46003,  292.46332]), array([-27.44392, -27.44335]))

Yay python!

The current version is a pure python script, and contains the comment:

CALLHORIZONS - a Python interface to access JPL HORIZONS
ephemerides and orbital elements.

This module provides a convenient python interface to the JPL
HORIZONS system by directly accessing and parsing the HORIZONS
website. Ephemerides can be obtained through get_ephemerides,
orbital elements through get_elements. Function
export2pyephem provides an interface to the PyEphem module.

michael.mommert (at) nau.edu, latest version: v1.0.5, 2017-05-05.
This code is inspired by code created by Alex Hagen.

Currently it only generates the query url for an observer table, not state vectors, but it could certainly be easily customized to get state vectors. Here is a small chunk, additional parameters are added to the url string before it's used.

### construct URL for HORIZONS query
url = "http://ssd.jpl.nasa.gov/horizons_batch.cgi?batch=l" \
      + "&TABLE_TYPE='OBSERVER'" \
      + "&QUANTITIES='" + str(quantities) + "'" \
      + "&CSV_FORMAT='YES'" \
      + "&ANG_FORMAT='DEG'" \
      + "&CAL_FORMAT='BOTH'" \
      + "&SOLAR_ELONG='" + str(solar_elongation[0]) + "," \
      + str(solar_elongation[1]) + "'" \
      + "&CENTER='"+str(observatory_code)+"'"

OK try a spacecraft, also viewed from Mauna Kea. From JPL Horizons with the following setup:

enter image description here

gives (scrolls horizontally):

Date_________JDUT, , ,R.A._(ICRF/J2000.0), DEC_(ICRF/J2000.0),  APmag, S-brt,            delta,     deldot,    S-O-T,/r,    S-T-O,
2457754.500000000,*,m,20 01 24.54,-56 59 28.4,   n.a.,  n.a., 1.7047550022E+10, 18.9796569,  36.6514,/T,   0.2976,
2457784.500000000,*,m,20 03 22.69,-56 58 00.2,   n.a.,  n.a., 1.7080283550E+10,  6.5439542,  40.6419,/L,   0.3218,
2457814.500000000,*,m,20 05 08.36,-57 01 24.2,   n.a.,  n.a., 1.7082950019E+10, -3.4910462,  59.2751,/L,   0.4270,
2457844.500000000,*,m,20 06 16.02,-57 09 10.1,   n.a.,  n.a., 1.7065586218E+10, -8.4792450,  82.4692,/L,   0.4975,
2457874.500000000,*,m,20 06 30.25,-57 19 39.5,   n.a.,  n.a., 1.7043227392E+10, -7.3046878, 106.0860,/L,   0.4870,
2457904.500000000,*,m,20 05 49.21,-57 30 32.2,   n.a.,  n.a., 1.7031649793E+10, -0.4854246, 127.5819,/L,   0.4048,
2457934.500000000,*,m,20 04 24.98,-57 39 21.6,   n.a.,  n.a., 1.7043463848E+10, 10.1577911, 141.9907,/L,   0.3144,

Compare to this script:

import callhorizons
import numpy as np

JDs = [2457754.5 + 30*i for i in range(7)] # 01-Jan-2017 00:00 UTC + n*30 days

V2  = callhorizons.query(-32, smallbody=False)     # -32 for Voyager 2


V2.get_ephemerides(568)    # 568 for Mauna Kea 

radecs = np.vstack((V2['RA'], V2['DEC']))

for JD, radec in zip(JDs, radecs.T):
    print JD, radec

A spot check shows that these are the same, great! Fyi the very large apparent motion is due to parallax - the Earth shifting its perspective every month. After one year the position actually only changes by a few tenths of a degree.

2457754.5 [ 300.35226  -56.99121]
2457784.5 [ 300.84454  -56.96671]
2457814.5 [ 301.28482  -57.0234 ]
2457844.5 [ 301.56676  -57.1528 ]
2457874.5 [ 301.62605  -57.32765]
2457904.5 [ 301.45504  -57.50895]
2457934.5 [ 301.10407  -57.65601]
  • I've accepted my own answer because it's acceptable. I'm quite open to further and/or different answers as well, if you have something to add please don't hesitate to post it. – uhoh Jun 17 '17 at 1:38
  • 2
    Seems like a pretty cool library! I would have recommended SpiceyPy which is an interface to SPICE, and SPICE runs JPL Horizons. But then you have to rewrite all the logic. – ChrisR Jun 19 '17 at 19:34
  • @ChrisR I'll take a look. Can you consider leaving a short answer mentioning SpiceyPy? Comments are by definition temporary, and even if it might not be a perfect match to "Python API for JPL Horizons?" it does do something similar enough that future readers might benefit from knowing about. Doesn't have to be a long answer, just a link and an explanation that when one wants to access Horizons automatically, one is really just accessing SPICE and so one should know that the Horizons middle-man is not always necessary. – uhoh Jun 20 '17 at 3:56

Your Answer


By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.