Profile Image

Andrew Elsasser

SaaS Solutions Engineer

NASA Astronomy Picture of the Day (APOD) Tutorial

NASA IMAGE OF THE DAY

Learn how I created my NASA Image of the Day project

NASA’s Open API:
Open and Free

NASA features a fully-function RESTful open web API for public use. The objective of NASA’s API is to make NASA data, including imagery, eminently accessible to application developers. Their catalog focuses on broadly useful and user friendly APIs and does not hold every NASA API.

Astronomy Picture of the Day

The Astronomy Picture of the Day(APOD) is one of the most popular websites at NASA, which you can visit here. The objective of the website is to display an Astronomy Picture of the Day, an image title, copyright info and description. The picture of the day is large and ideal for wallpaper or background use, and it just so happens their APOD is available for free and public use via RESTful API.

So why not have some fun creating code to get NASA’s APOD endpoint?

Getting Started

Getting started can be overwhelming, due to having so many ideas regarding the outcome of a project. However, start simple, like a snowball fitting in the palm of your hand, and build upon that. After all, all snowmen start as snowballs.

Want to checkout the NASA APOD Github? Go here.

A note from the NASA Github:

The deployed version of this API is based on the eb branch. The version that was deployed before that is in the eb_previous branch. The master branch is used as development as that’s where most of the pull requests will come into anyways.

This API is deployed on AWS using elastic beanstalk due to large number of people who use the service. However, if you’re planning on using it just yourself, it is small enough to be stood up on a single micro EC2 or any other small size cloud compute machine.

Start with a Basic Outcome

What do we hope to achieve? First, we must understand what parameters are available to use. NASA will only provide so much data in response to our request, so what’s available to work with?

Request Parameters

  • api_key – demo: DEMO_KEhttps://api.nasa.gov/#signUp
  • date – A string in YYYY-MM-DD format indicating the date of the APOD image (example: 2014-11-03). Defaults to today’s date. Must be after 1995-06-16, the first day an APOD picture was posted. There are no images for tomorrow available through this API.
  • concept_tags – A boolean True|False indicating whether concept tags should be returned with the rest of the response. The concept tags are not necessarily included in the explanation, but rather derived from common search tags that are associated with the description text. (Better than just pure text search.) Defaults to False.
  • hd – A boolean True|False parameter indicating whether or not high-resolution images should be returned. This is present for legacy purposes, it is always ignored by the service and high-resolution urls are returned regardless.
  • count – A positive integer, no greater than 100. If this is specified then count randomly chosen images will be returned in a JSON array. Cannot be used in conjunction with date or start_date and end_date.
  • start_date – A string in YYYY-MM-DD format indicating the start of a date range. All images in the range from start_date to end_date will be returned in a JSON array. Cannot be used with date.
  • end_date – A string in YYYY-MM-DD format indicating that end of a date range. If start_date is specified without an end_date then end_date defaults to the current date.
  • thumbs – A boolean parameter True|False inidcating whether the API should return a thumbnail image URL for video files. If set to True, the API returns URL of video thumbnail. If an APOD is not a video, this parameter is ignored.

Like ordering from a menu, the above parameters are what’s available to request from NASA. Let’s now take a look at what kind of response fields we’ll receive.

Response Fields

  • resource –  A dictionary describing the image_set or planet that the response illustrates, completely determined by the structured endpoint.
  • concept_tags – A boolean reflection of the supplied option. Included in response because of default values.
  • title – The title of the image.
  • date – Date of image. Included in response because of default values.
  • url – The URL of the APOD image or video of the day.
  • hdurl – The URL for any high-resolution image for that day. Returned regardless of ‘hd’ param setting but will be omitted in the response IF it does not exist originally at APOD.
  • media_type – The type of media (data) returned. May either be ‘image’ or ‘video’ depending on content.
  • explanation – The supplied text explanation of the image.
  • concepts – The most relevant concepts within the text explanation. Only supplied if concept_tags is set to True.
  • thumbnail_url – The URL of thumbnail of the video.
  • copyright – The name of the copyright holder.
  • service_version – The service version used.

Putting the Idea Together

From the above request and response parameters, we know what’s available to request and what we’ll get in return. I am interested in keeping things simple. We know that the date and end_date parameters default to today, so there’s no need to explicitly define those parameters, since we just want the picture of the day. However, the request parameters do indicate we have the ability to perform historical lookups by date or date range, having an optional number of records returned, which is perhaps a great idea for a future enhancement to this mini project.

The rest of the request parameters are either irrelevant or redundant, and thus all we need to do is make a basic request. The API endpoint URL will look like this:

https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY

If we submit a request using the above API endpoint URL and demo key, we’ll receive a JSON response like so:

"User Data:", {
  copyright: "
Nicolas Martino, Adrien Soto, Louis Leroux & 
Yann Sainty
",
  date: "2025-02-18",
  explanation: "Seen as a seagull and a duck, these nebulae are not the only cosmic clouds to evoke images of flight. But both are winging their way across this broad celestial landscape, spanning almost 7 degrees across planet Earth's night sky toward the constellation of the Big Dog (Canis Major). The expansive Seagull (top center) is itself composed of two major cataloged emission nebulas. Brighter NGC 2327 forms the head with the more diffuse IC 2177 as the wings and body. Impressively, the Seagull's wingspan would correspond to about 250 light-years at the nebula's estimated distance of 3,800 light-years. At the lower right, the Duck appears much more compact and would span only about 50 light-years given its 15,000 light-year distance estimate. Blown by energetic winds from an extremely massive, hot star near its center, the Duck nebula is cataloged as NGC 2359. Of course, the Duck's thick body and winged appendages also lend it the slightly more dramatic popular moniker, Thor's Helmet.   Portal Universe: Random APOD Generator",
  hdurl: "https://apod.nasa.gov/apod/image/2502/SeagullThor_Martino_5149.jpg",
  media_type: "image",
  service_version: "v1",
  title: "Thor's Helmet versus the Seagull",
  url: "https://apod.nasa.gov/apod/image/2502/SeagullThor_Martino_960.jpg"
}

Now, let’s look at making sense out of this JSON response. We want to again keep things simple, so we won’t need to parse every field.

We’re interested in:

  • copyright
  • date
  • explanation
  • hdurl
  • title

Where to begin?

For me, I prefer to tackle the meat of the matter head-on. However, one could start with the HTML and CSS design of the page, knowing what response parameters we’ll be working with. Design can be simple or it can be elegant, however at the end of the day, the same data is displayed regardless. Therefore, we can design simple HTML and CSS to start and improve on that over time in iteration. Using a car analogy, I prefer to create the engine and drivetrain first because it requires the most work, and there will always be a body wrapped around these parts when finished, simple or elegant, though for me, I am all about simplicity and efficient functionality.

Javascript Request Body

Let’s take a look at what the request will look like in Javascript. We’re going to use the Fetch( ) function, which is the best practice. Fetch is promise-based and is integrated with features of the modern web such as service workers and Cross-Origin Resource Sharing (CORS). The Fetch( ) function returns a Promise which is fulfilled with a Response object representing the server’s response.

//Let's set the apiURL as a constant since it won't be changing.
const apiUrl = 'https://api.nasa.gov/planetary/apod?api_key=API_KEY';

//Here, we pass the apiURL as a parameter within Fetch(), telling fetch //exactly what to get.

fetch(apiUrl)
  .then(response => { 

//Here we see the response object representing the server's response
//If, for some reason the response is invalid, we can handle that //gracefully and let the end-user know something was wrong with the response.

    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    return response.json(); //else, we'll return the response JSON
  })

//Since the NASA APOD offers both images and videos, we'll need to check the //media type, whether it is an image or video.

  .then(userData => {
    if (userData.media_type === 'image') {

//Here, we know we'll create an HTML div having id "image", and we'll set its //inner html via Javascript by creating the image element and setting its src //attribute to the hdurl provided to us in the JSON response.     
 
document.getElementById("image").innerHTML = `<img src="${userData.hdurl}" width="100%" height="100%"/>`;
    
    } 
else {

//If, by chance, we receive a YouTube link, for example, we can handle that //using an HTML iframe. Just like how the image element and its src attribute //are created using Javascript, we'll set the iframe src to URL instead of //hdurl, since hdurl will only ever return HD images, and URL returns either //a video or image

      document.getElementById("image").innerHTML = `<iframe width="100%" height="100%" src="${userData.url}" frameborder="0" allowfullscreen></iframe>`;
    }

//Here, we use the Javascript DOM to set the relative DIV content to specific //JSON response field values.
 document.getElementById("title").innerHTML = "<b>Title:&nbsp;</b>" + userData.title;
 document.getElementById("copyright").innerHTML = "<b>Copyright:&nbsp;</b>" + userData.copyright;
 document.getElementById("date").innerHTML = "<b>Date:&nbsp;</b>" + userData.date;
document.getElementById("explanation").innerHTML = "<b><u>Explanation</u>&nbsp;</b><br/>" + userData.explanation;

//Logging the API response is optional as it takes more resources to do so //and is somewhat unnecessary, unless you have a practical purpose for doing //so, which could include checking the API Response JSON for items of //interest.

    console.log('User Data:', userData);
  })

//Last, if there is an error with the response image, we'll gracefully handle //that and log the resulting error to the console.

  .catch(error => {
    document.getElementById("image").textContent = 'Failed to load image';
    console.error('Error:', error);
  });

HTML Body

Now that the hard part is done, we need to create the necessary corresponding HTML responsible for housing the Javascript-driven content. In the left column, we see the HTML code responsible and ready to house the Javascript response data set via the Document Object Model.


<div style="text-align:center;">
  <br/>
  <h2>NASA.gov</h2>
  <h3>Image of the Day</h3>
    
      <div id="image" height="100%"></div>

  <p id="title"></p>
  <p id="copyright"></p>
  <p id="date"></p>
  <p id="explanation"></p>

</div>

The Final Product

After much research and hard work, we can finally put it all together! This is the outcome of our labor.

Let’s add some CSS to improve the styling of the image and its response fields:

<style>
img {border-radius:100px;}
#header { width: 100%; text-align:center; font-family: Verdana, sans-serif; font-size:large; }
#content { width: 100%; text-align:center; font-family: Verdana, sans-serif; font-size: medium; line-height: 2.0;}
</style>

Leave a Reply

Your email address will not be published. Required fields are marked *