# Obtain archived TLE data

Now I'm achieving the up-to-date TLE from the celestrak.com and tle.info

However, for some analysis in the past, I need old TLE files, to make my calculations more accurate.

Is there a way to obtain the archived TLE data? Is there a way to do it via URL (to be able to load from the script, like celestrak.com/cgi-bin/TLE.pl?CATNR=40052&DATE=...)?

## Celestrak

For some selected groups of satellites, Celestrak has already-built group definitions of satellite archives at https://www.celestrak.com/NORAD/elements/

## Space-Track

Another option is to subscribe to Space-Track.org. You have to register using your name and e-mail address, then verify, but it is a straightforward process. Make sure to read all of the instructions and rules first.

Then at least browse everything else.

One you are registered and logged in, select TLE search from the top menu:

Then enter the satellite number (21639 for example) and click all of the options shown below, and you will get all of the TLEs so far for March, 2018.

If you click the blue API button, it will build a URL for you and then open it in a new tab. You can save that URL and alter it or rebuild it later with Python.

You can also save the data in the new tab directly from File->Save on your browser.

Read the instructions in more depth if you want to try to do this automatically from a script. Visit HowTo from the Help menu: https://www.space-track.org/documentation#/howto

## Finally

Remember you need to use your personal log-in before you can access the site, even using a script, and remember to

Here is a sample Python script I modified from the example at Space-Track.org (requires registration of a free account). It looks for all SpaceLink satellites only (I plan to modify it to accept a list or dictionary with resident space object identifiers instead):

# STTest.py (copyright 2019 by Andrew Stokes, original file named differently)
#
# Simple Python app to extract resident space object history data from www.space-track.org into a spreadsheet
# (prior to executing, register for a free personal account at https://www.space-track.org/auth/createAccount)
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free Software Foundation,
# either version 3 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
#
# For full licencing terms, please refer to the GNU General Public License (gpl-3_0.txt) distributed with this release,

import requests
import json
import xlsxwriter
import time
import datetime
import getpass
import sys

class MyError(Exception):
def __init___(self, args):
Exception.__init__(self, "my exception was raised with arguments {0}".format(args))
self.args = args

# See https://www.space-track.org/documentation for details on REST queries
# "Find Starlinks" query finds all satellites w/ NORAD_CAT_ID > 40000 & OBJECT_NAME matching STARLINK*, 1 line per sat;
# the "OMM Starlink" query gets all Orbital Mean-Elements Messages (OMM) for a specific NORAD_CAT_ID in JSON format.

uriBase = "https://www.space-track.org"
requestCmdAction = "/basicspacedata/query"

# Parameters to derive apoapsis and periapsis from mean motion (see https://en.wikipedia.org/wiki/Mean_motion)
GM = 398600441800000.0
GM13 = GM ** (1.0 / 3.0)
PI = 3.14159265358979
TPI86 = 2.0 * PI / 86400.0

configUsr = input()
configPwd = getpass.getpass(prompt='Securely enter your Space-Track.org password (minimum of 15 characters):  ')
# Excel Output file name - e.g. starlink-track.xlsx (note: make it an .xlsx file)
configOut = 'STText.xslx'
siteCred = {'identity': configUsr, 'password': configPwd}

# User xlsxwriter package to write the .xlsx file
print('Creating Microsoft Excel (.xlsx) file to contain outputs...')
workbook = xlsxwriter.Workbook(configOut)

print('Starting to write outputs to Excel file create...')
now = datetime.datetime.now()
nowStr = now.strftime("%m/%d/%Y %H:%M:%S")
worksheet.write('A1', 'Starlink data from' + uriBase + " on " + nowStr)
worksheet.write('B3', 'SATNAME')
worksheet.write('C3', 'EPOCH')
worksheet.write('D3', 'Orb')
worksheet.write('E3', 'Inc')
worksheet.write('F3', 'Ecc')
worksheet.write('G3', 'MnM')
worksheet.write('H3', 'ApA')
worksheet.write('I3', 'PeA')
worksheet.write('J3', 'AvA')
worksheet.write('K3', 'LAN')
worksheet.write('L3', 'AgP')
worksheet.write('M3', 'MnA')
worksheet.write('N3', 'SMa')
worksheet.write('O3', 'T')
worksheet.write('P3', 'Vel')
wsline = 3

def countdown(t, step=1, msg='Sleeping...'):  # in seconds
pad_str = ' ' * len('%d' % step)
for i in range(t, 0, -step):
sys.stdout.write('{} for the next {} seconds {}\r'.format(msg, i, pad_str))
sys.stdout.flush()
time.sleep(step)
print('Done {} for {} seconds!  {}'.format(msg, t, pad_str))

# use requests package to drive the RESTful session with space-track.org
print('Interfacing with SpaceTrack.org to obtain data...')
with requests.Session() as session:

# Need to log in first. NOTE:  we get a 200 to say the web site got the data, not that we are logged in.
resp = session.post(uriBase + requestLogin, data=siteCred)
if resp.status_code != 200:
raise MyError(resp, "POST fail on login.")

# This query picks up all Starlink satellites from the catalog. NOTE: a 401 failure shows you have bad credentials.
resp = session.get(uriBase + requestCmdAction + requestFindStarlinks)
if resp.status_code != 200:
print(resp)
raise MyError(resp, "GET fail on request for resident space objects.")

# Use json package to break json-formatted response into a Python structure (a list of dictionaries)
satCount = len(retData)
satIds = []
for e in retData:
# each e describes the latest elements for one resident space object. We just need the NORAD_CAT_ID...
satIds.append(catId)

# Using our new list of resident space object NORAD_CAT_IDs, we can now get the OMM message
maxs = 1 # counter for number of sessions we have established without a pause in querying space-track.org
for s in satIds:
if resp.status_code != 200:
# If you are getting error 500's here, its probably the rate throttle on the site (20/min and 200/hr)
# wait a while and retry
print(resp)
raise MyError(resp, "GET fail on request for resident space object number " + s + '.')

# the data here can be quite large, as it's all the elements for every entry for one resident space object
for e in retData:
# each element is one reading of the orbital elements for one resident space object
print("Scanning satellite " + e['OBJECT_NAME'] + " at epoch " + e['EPOCH'] + '...')
mmoti = float(e['MEAN_MOTION'])
ecc = float(e['ECCENTRICITY'])
worksheet.write(wsline, 1, e['OBJECT_NAME'])
worksheet.write(wsline, 2, e['EPOCH'])
worksheet.write(wsline, 3, float(e['REV_AT_EPOCH']))
worksheet.write(wsline, 4, float(e['INCLINATION']), z1_format)
worksheet.write(wsline, 5, ecc, z3_format)
worksheet.write(wsline, 6, mmoti, z1_format)
# do some ninja-fu to flip Mean Motion into Apoapsis and Periapsis, and to get orbital period and velocity
sma = GM13 / ((TPI86 * mmoti) ** (2.0 / 3.0)) / 1000.0
apo = sma * (1.0 + ecc) - MRAD
per = sma * (1.0 - ecc) - MRAD
smak = sma * 1000.0
orbT = 2.0 * PI * ((smak ** 3.0) / GM) ** (0.5)
orbV = (GM / smak) ** (0.5)
worksheet.write(wsline, 7, apo, z1_format)
worksheet.write(wsline, 8, per, z1_format)
worksheet.write(wsline, 9, (apo + per) / 2.0, z1_format)
worksheet.write(wsline, 10, float(e['RA_OF_ASC_NODE']), z1_format)
worksheet.write(wsline, 11, float(e['ARG_OF_PERICENTER']), z1_format)
worksheet.write(wsline, 12, float(e['MEAN_ANOMALY']), z1_format)
worksheet.write(wsline, 13, sma, z1_format)
worksheet.write(wsline, 14, orbT, z0_format)
worksheet.write(wsline, 15, orbV, z0_format)
wsline = wsline + 1
maxs = maxs + 1
print(str(maxs))
if maxs > 18:
print('\nSnoozing for 60 secs for rate limit reasons (max 20/min and 200/hr).')
countdown(60)
maxs = 1
session.close()
workbook.close()
print('\nCompleted session.')


The name of the original script by Andrew Stokes was "SLTrack.py" which I renamed to "STTest.py" because I am still playing around with it. I do not care about any copyright attribution to myself.

• brethvoice, thanks for adding back in the proper attribution. It'd also be best if you included in your answer the location of the original source code (space-track.org/documentation#/howto) and the name of the original script. If you would like, you can add a copyright for the modifications to attribute them to you (see how to do that here). Feb 26 '20 at 19:34
• Hi @called2voyage, thank you for the kind assistance. The name of the original script by A. Stokes was SLTrack.py' which I renamed to STTest.py' because I am still playing around with it. I do not care about any copyright attribution to myself. Feb 27 '20 at 13:12
• Thanks! And welcome to the community! Feb 27 '20 at 13:21