Quick Nav API Intro JSON Intro Get a NASA API Key Today's APOD Archived APOD Having Fun

Brief Introduction to APIs

What is an API?

API is an acronym for Application Programming Interface. APIs provides a simple interface for a complicated system we may not need to know all the details about. We can perform complex tasks and request resources from the API service through simple commands and supplying it with our parameters. As an analogy, imagine the scenario where a customer (user) visits a coffee shop. All the customer (user) needs to know are the menu items (methods and parameters) needed to place an order (request). If all the parameters are correctly followed, e.g. the user doesn't try to order off-menu, the barista, who acts as the API, can correctly take in the order (request) and deliver it to the kitchen, which acts as the application in this scenario. The kitchen processes the request and produces a response, in this case, it may be a cappuccino. The barista (API) receives this cappuccino (response) and delivers it back to the user. The customer (user) successfully made a request and received a response without having to know all the details of what went on in the kitchen, i.e. what the receipe was, what equipment was used, how the coffee beans were sourced, etc. All the user needed to know were the methods offered by the API, the parameters these methods can take, and what to expect back in response to their request.

Photo: https://www.handsonconnect.org/blog/2016/8/17/what-is-an-api-and-why-should-i-care

In this guide, we'll discuss the NASA Astronomy Picture of the Day (abbreviated APOD) API. It is a web API. For this particular web API, we can interact with it through the URL. Making a request through the URL involves including a query string to the end. The query string is how we encode the parameters we want to send in the request. An example of this is as follow,

http://www.example.com/index.html?field1=value1&field2=value2

The hostname is http://www.example.com/, the resource we want to access is index.html, and the query string is field1=value1&field2=value2. A ? separates the resource location and query string. Each key-value pair inside the query string is separated by a &. In this example, you see two key-value pairs. Within each pair, the key and value are separated by a =. This kind of request is known as a GET request - we use the URL to encode the parameters and make the request. In contrast, there is something known as a POST request. In a POST request, the body of the request encodes the parameters of the request. The URL of a POST request can contain a query string as well. What signifies a request to be a POST is the use of the request body. After we send the request to the NASA APOD API using a URL query string, we'll get back what is know as JSON formatted data. We'll discuss this in the next section.

Below are some links I found helpful in understanding the basics of APIs.

Article: "Web APIs for non-programmers" by School of Data
Video: "What is an API?"" by MuleSoft
Video: "REST API concepts and examples" by WebConcepts


What APIs do NASA offer?

Besides the popular Astronomy Picture of the Day API, NASA offers a number of other APIs. Below is a list briefly summarizing the APIs available through NASA. If you wish to learn more, click on the button following the list to visit NASA API portal.

Brief Introduction to JSON

What is JSON?

JSON is an acronym for JavaScript Object Notation. It's an simple format used to organize and structure readable data. It's primarily used between servers and web applications. A JSON object contains key-value pairs separated by commas and completely enclosed in parentheses. Both the key and value need to be strings and must be written using double quotes "". Below is a example of a JSON object assigned to a variable philip. It contains six key-value pairs.

var philip =
{
  "first_name" : "Philip",
  "middle_initial" : "J",
  "last_name" : "Fry",
  "date_of_birth" : "August 9, 1975",
  "employer" : "Planet Express",
  "occupation" : "Delivery Boy"
}

How to use JSON?

We can extract data from it simply by referring to the variable we assigned the JSON object to and using the dot or bracket notation. For example, philip.employer and philip["employer"] will both output Planet Express.

Now that we know what is JSON and how to work with it, there's just a few extra steps we need to take in order to send and receive JSON data. In order to send JSON data to a web server, we must first convert it to a single string. We can use the browser built-in JSON.stringify() function to do so. We can assign the results of the stringify function to a variable and then send this variable. For example, var philipString = JSON.stringify(philip) passes philip into the stringify function and assigns the results to a new variable philipString. The output of philipString is a long string:

"{"first_name":"Philip","middle_initial":"J","last_name":"Fry","date_of_birth":"August 9, 1975","employer":"Planet Express","occupation":"Delivery Boy"}"
Likewise, we will receive JSON data in a single string format from the web server. Fortunately, there's another built-in function, JSON.parse(), that can convert this JSON string back to a JSON object so that we can easily extract data from it. For example, var returnedPhilip = JSON.parse(philipString) will pass philipString into the parse function and assign the results to a new variable returnedPhilip. The output of returnedPhilip is identical to philip, the original JSON object.

API Key

What is an API key?

An API key is a unique passcode the API service provides to you. It's primary purpose is for the service behind the API to identify the origin of the request. This enables the API to keep track of the number of requests made to their service by any particular key and helps to prevent abuse from high-volume requests. Some APIs are paid services. An API key enables the user to identify themselves so they are able to use the service and allows the API service to keep track of the usage for billing purposes. So it's important to not publicly share your API keys.


Getting a NASA API Key

Getting an API key from NASA is very simple! The button below will take you to the page where you can apply for an API key. Just follow the instructions and an alphanumeric key will be emailed to you.

NASA provides a trial key that allows users to quickly explore their APIs prior to applying for an API key of their own. This special API key is DEMO_KEY. However, there are some strict rate-limits imposed on this trial key, specifically 30 requests per IP address per hour (maximum 50 requests per IP address per day). If you sign up for a NASA API key, the rate-limit is increased significantly, to 1,000 requests per hour. Keep in mind it is not at all common for APIs to have a kind of trial key. In this guide, you might hit the rate-limit restriction using NASA's trial key, so we will use a key I generated instead.

How to use NASA's APOD API to get Today's Media

API Documentation

Now that you have some understanding of APIs and a NASA API key, you're ready to use any of the APIs offered by NASA. We'll focus on the Astronomy Picture of the Day API. Fortunately, NASA's APOD API has some built-in defaults which makes it very simple and convenient to access today's media. You may notice I said media. NASA's APOD can actually return a URL to a photo or video. Unfortunately, NASA's APOD API documenation isn't very detailed, and as it turns out, isn't up-to-date. Don't worry though! We'll discuss it thoroughly in the following sections.


Using the URL to Make a Query

Without doing anything fancy, we can make a request to the NASA APOD API by just using the URL to submit the query or request (specifically a GET request). At the time of writing this guide, entering https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY into the browser yields the following chunk of text returned:

{
  "copyright": "Yuri BeletskyCarnegieLas Campanas ObservatoryTWAN",
  "date": "2017-05-09",
  "explanation": "Do you see it?  This common question frequently precedes the rediscovery of one of the most
    commonly recognized configurations of stars on the northern sky: the Big Dipper.  This grouping of stars is
    one of the few things that has likely been seen, and will be seen, by every generation.   The Big Dipper is
    not by itself a constellation.  Although part of the constellation of the Great Bear (Ursa Major), the Big
    Dipper is an asterism that has been known by different names to different societies. Five of the Big Dipper
    stars are actually near each other in space and were likely formed at nearly the same time.  Connecting two
    stars in the far part of the Big Dipper will lead one to Polaris, the North Star, which is part of the Little
    Dipper.   Relative stellar motions will cause the Big Dipper to slowly change its configuration over the next
    100,000 years. Pictured in late April, the Big Dipper was actually imaged twice -- above and below distant
    Chilean volcanoes, the later reflected from an unusually calm lagoon.    Follow APOD on: Facebook,  Google
    Plus,  Instagram, or Twitter",
  "hdurl": "https://apod.nasa.gov/apod/image/1705/DoubleDipper_Beletsky_1199.jpg",
  "media_type": "image",
  "service_version": "v1",
  "title": "Big Dipper Above and Below Chilean Volcanoes",
  "url": "https://apod.nasa.gov/apod/image/1705/DoubleDipper_Beletsky_960.jpg"
}

Thankfully we know some JSON, so we know that what we are getting back is an object containing a series of key-value pairs separated by commas. If you take the URL assigned to the "hdurl" or "url" property and enter it into the browser, you should get a cool-looking astronomy photo displayed.


Displaying the APOD Automatically

Now that you know know how to make a request using the a URL query string, you might be wondering how to automate the process of getting the APOD media and displaying it to the browser. Below is an example of code that can do just that. Click on the Result tab to view the finished page.



Let's go through the process of setting this up. Under the HTML tab, you'll see the page is set up with a few placeholder - namely a <div> for the image or video and a few <figcaption>to hold the date, explanation, title, and copyright values. If the image or media is in the public domain, the returned JSON object may not include a copyright property. Thus we will have in our JavaScript code a conditional statement to evaluate the existence of this property and display it if it does. The CSS tab contains some styling code that provides the HTML with some aesthetics. It's not critically important in this context so we will not discuss any CSS aspect in this guide.

Let's start at the very beginning of the JavaScript tab. The API key is saved as in the variable apiKey at the top of the code for easy reference. And should you want to share your code, having a single location for your key makes it easy to remove your key prior to sharing. After that you'll see the XMLHttpRequest constructor is called and the result of that call returns a XMLHttpRequest object which is assigned to the variable req. The XMLHttpRequest object is what you use to make a HTTP request. It has a variety of methods that you can call, including loading the object with data (i.e. a URL query string for a GET request) or extracting data from the object once a response has been received. We call the XMLHttpRequest.open() method through our req variable. The syntax for this method is XMLHttpRequest.open(method, url, async). In this case, the method we want to use is "GET", the URL we want to send the GET request to is "https://api.nasa.gov/planetary/apod?api_key=" + apiKey, where apiKey will be substituted with our actual API key, and async is set to true because we want to make the call asynchronously. This allows the page to continue running its program without having to stop and wait for a response from the XMLHttpRequest object once we send it off.

So in order for us to know that we received a reponse, we need to add an Event Listener to the object using the appropriately named addEventListener() method. Its basic syntax is target.addEventListener(type, listener[, options]). The target in this case is our XMLHttpRequest object in req. The type refers to its event type, in this case we set it to "load" - as in we want to trigger this Event Listener once the resource we requested has finished loading. The second argument we provide is the callback function we want to execute once the resource has been loaded. This callback function makes up the bulk of the program needed to render the page. Before we get to that, I want to point out that if you scroll most of the way down, you'll see the command req.send(null). This is how we send off the XMLHttpRequest object. The XMLHttpRequest.send() method has an optional argument that takes in data to set as the request body. This is typically done for a POST request, where we usually provide a stringified JSON object, like this, in the request body. However, for our purposes, we don't need to send anything in the request body since we use the URL query string to send our request data. Thus we set the optional argument to null so we explicitly know nothing is being sent in the request body.

Now back to the callback function. The syntax may look a bit odd and it's because the callback function is defined on the spot as it is being supplied as an argument to the function. The first thing that is done in the callback function is to have an if/else statement to evaluate the status of the req object. The XMLHttpRequest.status property holds the numerical HTTP status code of the response. The general rule of thumb is that anything in the 200's and 300's mean the response is OK, while anything in the 400's and 500's meant some error occurred. You can read more about it on Mozilla's HTTP response status codes page. So if the status of the response is good, we'll execute the commands necessary to extract the information from the response and render our page, else, the only thing that is done is this line of code towards the bottom of the page, console.log("Error in network request: " + req.statusText), which logs the error status to console.log.

The response we get back from the API call is stored in the XMLHttpRequest.responseText property. The value that is stored here is a stringified JSON object. As we learned in the "How to Use JSON?" section above, in order to access the key-value pairs we must first parse the string using the browser built-in function JSON.parse(). We do this in the line var response = JSON.parse(req.responseText), which assigns the results of the parsing to the variable response. Now response holds a JSON object which we can access the values of using either the dot or bracket notation. We will use the dot notation in this guide since it provides a cleaner look.

Before we process response, let's clean up anything on the page and give it a blank canvas to work with. This is primarily needed to prevent two media from coexisting on the same page. Also, as briefly mentioned, the copyright property may not be present in all returned JSON responses, so we want to erase that caption so it doesn't carry over. The following block of code clears the text content from all the <figcaption> we have in our HTML page by targeting the element's ID through the DOM:

document.getElementById("todayCaption1").textContent = "";
document.getElementById("todayCaption2").textContent = "";
document.getElementById("todayCaption3").textContent = "";
document.getElementById("todayCaption4").textContent = "";

And the following section of code removes anything that is in the <div> which holds the APOD media - either an <img> for an image or <iframe> for a video. This removal is done again through the DOM, this time using removeChild() method if hasChildNodes() returns true on that <div>.

var todayPhotoDivNode = document.getElementById("todayPhotoDiv");
while(todayPhotoDivNode.hasChildNodes() == true) {
  todayPhotoDivNode.removeChild(todayPhotoDivNode.lastChild);
}

Now we deal with the reponse. First we look at the media_type property of the response object. We have if / else if blocks to deal with whether it's an image or video. Thus, far I've only encountered these two categories of media_type thus far. At the time of writing this, the API documentation doesn't mention much about this. Through the DOM, we create an <img> for an image or an <iframe> for a video, we give it an identifier to make it easy to target, and then add these as child nodes to the media <div> on the main page.

if(response.media_type == "image") {
  var img = document.createElement("img");
  img.id = "todayPhoto";
  document.getElementById("todayPhotoDiv").appendChild(img);

  if(response.hasOwnProperty("hdurl")) {
  	document.getElementById("todayPhoto").src = response.hdurl;
  } else {
  	document.getElementById("todayPhoto").src = response.url;
  }
} else if(response.media_type == "video") {
  var vid = document.createElement("iframe");
  vid.id = "todayVideo";
  document.getElementById("todayPhotoDiv").appendChild(vid);

  document.getElementById("todayVideo").src = response.url;
}

The API documentation states that the query string can take a parameter hd and a bool value. It returns a high resolution image if the bool value is true. This info seems to be outdated. Regardless of whether or not you include the hd query parameter and supply it with true or false, the response will include the hdurl property if it exist. Typically if the media_type is an image, both hdurl and url exists. If it's a video, only the url property exists. Thus, in our code, we have to assign the URL in the src property of our <img> or <iframe> accordingly. As a precaution for future APOD media, if the media_type is an image, the code will evaluate the presense of a hdurl property using the hasOwnProperty() function, if it exists, it uses that URL as the source for the image, if not, then url is used instead.

Lastly, the following block of code deals with extracting the strings stored with the title, date, explanation, and copyright properties of the response object. As previously mentioned, the copyright property may not always be returned, thus we use hasOwnProperty() again to test for it before we set the caption. If we didn't not do this, then the caption would hold undefined if the property didn't exist.

document.getElementById("todayCaption1").textContent = "Title: " + response.title;
document.getElementById("todayCaption2").textContent = "Date: " + response.date;
document.getElementById("todayCaption3").textContent = "Explanation: " + response.explanation;

if(response.hasOwnProperty("copyright")) {
  document.getElementById("todayCaption4").textContent = "Copyright: " + response.copyright;
}

Now with all this put together, you'll get a page that nicely displays today's APOD media, whether it's an image or video, and any available captions to go along with it. Below is a screenshot of the resulting page at the time of writing this guide.

Screenshot of APOD page on May 9, 2017.

How to use NASA's APOD API to get Archived Media

Using the URL to Make an Archived APOD Query

NASA's APOD API also allows you to search for past APOD media. It takes a date as a query parameter, in the value format YYYY-MM-DD, and return that day's APOD media. For example, let's say you want to view the very first available APOD media offered by NASA, which happened on June 16, 1995. The URL query string would look like https://api.nasa.gov/planetary/apod?date=1995-06-16&api_key=DEMO_KEY. As you expected, a JSON object is returned (shown below). You can browse the APOD archive website for more info.

{
  "date": "1995-06-16",
  "explanation": "Today's Picture:    Explanation:  If the Earth could somehow be transformed to the ultra-high
     density of a neutron star , it might appear as it does in the above computer generated figure. Due to the very
     strong gravitational field, the neutron star distorts light from the background sky greatly. If you look
     closely, two images of the constellation Orion are visible. The gravity of this particular neutron star is so
     great that no part of the neutron star is blocked from view - light is pulled around by gravity even from the
     back of the neutron star.   We keep an  archive file.  Astronomy Picture of the Day is brought to you by
     Robert Nemiroff and  Jerry Bonnell . Original material on this page is copyrighted to Robert Nemiroff and
     Jerry Bonnell.",
  "hdurl": "https://apod.nasa.gov/apod/image/e_lens.gif",
  "media_type": "image",
  "service_version": "v1",
  "title": "Neutron Star Earth",
  "url": "https://apod.nasa.gov/apod/image/e_lens.gif"
}

Building a Simple GUI for Searching Archived APOD Media

Now that we know how to search for archived APOD media by date. Let's build a simple graphical user interface (GUI) that will allow users to easily select a date, whether they want to display the photo in HD or Standard definition, and then display that media in the browser. Below is an example of code that can do this. The page is best viewed using Chrome. For other browsers, ensure you enter the date in the format YYYY-MM-DD, including the hyphens. More on this in a bit.



You can click on the Result tab and play around with the interface to get a feel of what it does. Please note that the functionality of the Date field is browser specific. For Chrome, Opera, and Microsoft Edge, the date is displayed with the format MM/DD/YYYY and has an optional pull-down calendar. Firefox and Safari treats this simply as a text input without any special formatting. In this case, in order to get the page to work correctly, the format of the date has to be YYYY-MM-DD.

To get the date input field, radio buttons, and submit button, these input elements were added to the HTML code. The rest of the HTML are placeholders for the APOD media and captions as you may be familiar with from the previous section. In the Javascript code, what's new here is a function that binds the submit button so the page knows when to grab and process the user's inputs. This is done by adding an event listener on the button. If you remember from the previous section, we can doing this by calling the addEventListener() function through the submit button. This time we set the type of event to listen for to be a "click", and similarly the callback function is defined on the spot and passed as the second argument.

The next thing we do is have the command event.preventDefault(). This prevents the page from refreshing when the submit button is clicked. This allows the page to retain the user's inputs for processing. Then you'll see the familiar code of clearing the captions and removing the media element. Below that you'll see the code to verify the user entered a valid date, as reproduced below.

var inputDate = document.getElementById("date").value;
var inputDateObj = new Date(inputDate);
var currentDateObj = new Date();

if (inputDate == "" || inputDateObj > currentDateObj) {
  document.getElementById("dateAPOD").style.border = "solid lightgrey";
  document.getElementById("dateCaption1").textContent = "Please select a valid date.";
  return;
}

A JavaScript Date object is created from the user's date input through passing the input string date to the Date constructor and assigning the result of the call to the variable inputDateObj. If we don't pass any any argument into the Date constructor, by default it creates a Date object using the current date and time. This is what was done to create variable currentDateObj. In our if block, we evaluate two things: 1) whether the user selected a date and 2) whether the user entered a future date. We can use the equality operator (==) to test the user's input against an empty string to achieve the first and a relational operator (>) to compare the Date objects to achieve the second. If it's the case that the user did not select a date or selected a future date, we display the message "Please select a valid date." to the page using one of the available text fields. The function immediately returns and does not process the input any further.

After this, you'll see the familiar code of creating the XMLHttpRequest object by calling its constructor, assigning the result to the variable req, and calling the open() method on the object. The only thing different here is the URL query string being passed. We added a date query parameter and assigned the value to be the date the user input, inputDate. The API only recognizes the date in the specific format YYYY-MM-DD. This format is a default for Chrome, Opera, and Microsoft edge. But for Firefox and Safari, it's important that the user enters this same date format as well for the call to work.

The remaining code inside the callback function is very similar to what you've seen in the previous section. The difference here is that the radio buttons are evaluated. If HD is checked, then the page will display the photo held by the hdurl property, if not, then the page will use the url property. Additionally, if the request returns a bad status code, the page will display an error message letting the user know an error has occurred. If the user entered their date manually, then the date may have been entered incorrectly and the user is warned of such as well.

Screenshot of the Archived APOD page with the date June 16, 1995 as input.

Having Fun

Using APOD media as a Background Splash for a Web Page

As you may have seen, NASA has some pretty awesome APOD photos in their library. Let's get the photo to show up as a background splash on a web page. The thing we have to keep in mind here is that occassionally a video may be the APOD media. In that case, we want to step back chronologically and grab the most recent image, and use that instead. Below is a code example that do.



In order to get today's date, we use JavaScript's Date constructor to generate a Date object which is assigned to the variable dateObj. Since the dateparameter in the URL query string takes a very specific format for the date, we use the dateObj.toISOString() function chained with the split()function to extract the portion of the string that is the date in the format YYYY-MM-DD. The full command to do this is dateObj.toISOString().split("T")[0], which first takes dateObj, convert it to an ISO string, and splits the string at the character T. The substrings are stored in an array. We take the first element of that array (hence the [0]) which is the date in the correct format. We can then use this date string in a URL query string to request the APOD media. Depending on your location, the time-offset can cause tomorrow's date to be displayed by toISOString(). A quick workaround I found was to cast dateObj to a string using toDateString() and re-encode it into a Date object by having the line dateObj = new Date(dateObj.toDateString()).

You'll notice that the async parameter of the XMLHttpRequest.open() function is set to false. This is so that we wait for a response from NASA's server before continuing on. Otherwise what'll happen is that a request will be sent, then since the foundImage bool flag is still false, the while loop will iterate again and another request will be fired before we received back our first one. A multitude of these will happen in quick succession and eventually we'll hit our rate-limit imposed by the API key and end up without an APOD photo. Additionally we'll crash our page because we would have used up a lot of memory from creating and sending all those requests.

Inside the callback function, we test if the media_type we received in the response is an image. If so, we set the backgroundImage to be the value held by hdurl if that property exists, otherwise we use url property. The foundImage bool flag is set to true so the while loop doesn't re-iterate. If the media_type is something else, we take dateObj , decrement the date, and assign it back to dateObj. A correctly formatted date string is then extracted from dateObj and the while loop re-iterates. The section of code that does this reproduced below.

dateObj.setDate(dateObj.getDate() - 1);
dateString = dateObj.toISOString().split("T")[0];

See the page working in action by clicking on the button below.

Good Luck!

Now that you've learned the basics of APIs and how to interact with NASA's Astronomy Picture of the Day API, the learning doesn't stop there! There's many, many more APIs out there for you to explore.
Good luck and happy coding!