Self-hosted service
nasa/apod-api avatar
nasa/apod-api

apod-api: a Flask pass-through that is scheduled to be archived

Astronomy Picture of the Day API service

1,093 stars217 forksPythonApache-2.0

At a glance

What is it?
The NASA apod-api service exposes the Astronomy Picture of the Day as a single versioned JSON endpoint, and as of October 2026 it no longer scrapes the site itself but forwards each call to a new WordPress endpoint. The repository states that it will be archived and pointed at the new endpoint on December 1, 2026, and asks users not to change their code.
Who is it for?
Adopt apod-api if you want the Astronomy Picture of the Day as a JSON endpoint and you can run it yourself, since the project explicitly recommends standing up your own instance when reliability matters. Do not adopt it as a long-term dependency on a hosted service, because the repository announces its own archival on December 1, 2026 and the maintenance burden sits with whoever watches the repository.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 19 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The service became a pass-through in October 2026

The header of the README carries the most important fact about this project, and it is a deadline. It is marked as the final update of 9-10-2026, and it says the backend has been rewired to point to the new Wordpress Endpoint, that this API will pass your call to the new endpoint and return the result, and that the readme has been updated with the new return values and their meaning. The next sentence is the one to plan around: this repo will be archived and will be pointed to the new end point on December 1, 2026, and you should not need to update your code or projects at this time. So the service you would be adopting is a thin Flask layer that forwards a request and returns the upstream response, and the upstream is a WordPress REST endpoint for the Astronomy Picture of the Day. The project is written in Python with the Flask micro framework, is licensed under Apache-2.0, and its package metadata names it the Astronomy Picture of the Day API service at version 1.1.0, requiring Python 3.12 or newer.

One endpoint, and a small set of query parameters

The API surface is deliberately narrow. There is only one endpoint in this service, which takes optional fields as parameters to an HTTP GET request, and a JSON dictionary is returned nominally. The path is versioned, with the documented form being /<version>/apod. The parameters are the ones you would expect from a picture archive. api_key takes the demo value DEMO_KEY or a key signed up for at api.nasa.gov. date is a string in YYYY-MM-DD format for the date of the image, defaulting to today's date, and it must be after 1995-06-16, the first day an APOD picture was posted, with no images for tomorrow available through the API. count is a positive integer no greater than 25, and when given it returns that many randomly chosen images in a JSON array. start_date and end_date define a range, with end_date defaulting to the current date if start_date is given alone. The constraints on combining them are explicit, since count cannot be used in conjunction with date or start_date and end_date, and start_date cannot be used with date. The example in the documentation is a single call for a specific day:

bash
localhost:5000/v1/apod?date=2014-10-01

A point worth noting for a designer of the client is that the response is nominally a JSON dictionary, so a client should not assume the payload is always a single object: with count or a date range it is an array.

The returned fields are WordPress fields now, and the parser is still in the tree

The field list is the clearest evidence of the rewiring. The response carries the APOD date in YYYY-MM-DD format, a WordPress post ID, the APOD post title, the permalink of the post on the site, a normalised media type such as image, video or iframe, the APOD explanation text, the APOD post URL which matches the permalink, a full-size featured image URL when one is available, the APOD Basic HTML document as a JSON string, and a plain-text HTML source URL for easier copy and paste workflows. The last three are new shapes compared with a scraped response, and a client that reads a fixed set of keys needs to be checked against this list. The repository still contains an apod_parser/ directory, and the dependency list still includes beautifulsoup4 and pillow alongside flask, flask-cors, gunicorn, jinja2, requests, urllib3 and werkzeug, so the scraping code and its libraries remain even though the live behaviour is now a forward. The templates/ and static/ directories in the tree suggest there is still a rendered surface behind the JSON, and the documentation links a fuller user guide for the APOD feed and API.

Running it locally takes two commands, and a container takes one

The dependency management was modernised too. The README notes that a code refresh occurred on 1-21-2026, where the code was refreshed to use uv for dependency management, and that the Dockerfile and docker-compose.yml were updated to support containerized deployment. It also says that all open issues and pull requests were closed to start fresh, and that the hosting of the API was still being updated at that point, which may cause downtime. The standard environment is four steps. You clone the repository, change into the directory, install the dependencies with uv sync, and run the application with uv run python application.py. The Docker route is three steps, cloning, changing into the directory, and building and running the image with docker compose up --build. The compose file publishes the service on port 5000 and sets FLASK_ENV=production for the container, while the Dockerfile exposes the same port and runs the application under gunicorn. The tests follow the same pattern as the rest of the toolchain, with uv run pytest once you have followed the standard environment steps.

The production process model is four gunicorn workers on port 5000

The container image is built from python:3.12-slim, and the uv binary is copied in directly from the official image at version 0.9.26 rather than installed with a script. The dependency layer is cached deliberately, with the pyproject.toml and uv.lock copied first so that a change to the code does not invalidate the dependency install, and the install itself runs as uv sync --frozen --no-dev, which means the lock file is honoured exactly and the development group is left out of the image. The runtime command is the part that tells you how the service is meant to be served:

dockerfile
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "--access-logfile", "-", "application:app"]

Four worker processes, bound to all interfaces on port 5000, with access logging to stdout, serving the application object from application.py. That is a small, conventional deployment, and since the service forwards a request to an upstream and returns the answer, the worker count is about concurrency of outbound calls rather than local compute. The dev dependencies give a hint about what the maintainers worry about: locust for load testing and memory-profiler for memory profiling sit alongside pytest, pytest-cov and ruff, which is a sensible set for a service whose README tells people to run their own instance if they need reliability.

The test configuration has a small inconsistency worth noticing

The pytest configuration in the package metadata is short and mostly ordinary. Test paths are the tests/ directory, files are matched with the test_*.py pattern, the root of the repository is added to the import path, and coverage is enabled with the addopts line forcing --cov=apod --cov-report=term-missing. The one oddity is that the coverage target is named apod while the source package directory in the tree is apod_parser/, so the coverage configuration is pointing at a name that does not obviously match the package layout. It is the kind of thing worth checking before you rely on the coverage numbers in continuous integration, and it is visible in the repository rather than hidden. Linting is deliberately narrow, with the ruff configuration selecting only the I rule, which is import sorting, so the lint gate is about consistent import order rather than a broad style opinion. The image and the compose file are in the tree together, and the .python-version file pins the interpreter, which is consistent with a project that has settled on a modern toolchain.

Where apod-api is the wrong tool

Three limits are clear. The first is the archive date, and it is not a soft one. The repository states that it will be archived and pointed at the new endpoint on December 1, 2026, so depending on a hosted instance of this service is depending on something with a stated end date. A team that plans around that needs either its own copy, which the project explicitly invites, or a client that points at the WordPress endpoint directly. The second is the content boundary. The README is explicit that no one watching the repository has anything to do with the Astronomy Photo of the Day website, so the service cannot deal with issues directly related to their content and asks you to contact them. If your problem is a wrong image, a missing date or a bad explanation, this repository is not where it gets fixed, and the boundary between a working API and a correct catalogue is not the same boundary. The third is scope. This is a single endpoint over one data set, and the response is nominally a dictionary that becomes an array depending on the parameters, which is a small amount of special handling for a client. It is not a search interface, not a feed of anything but the picture of the day, and it does not carry a service level that a hosted platform would offer.

Editorial conclusion

Adopt apod-api if you want the Astronomy Picture of the Day as a JSON endpoint and you can run it yourself, since the project explicitly recommends standing up your own instance when reliability matters. Do not adopt it as a long-term dependency on a hosted service, because the repository announces its own archival on December 1, 2026 and the maintenance burden sits with whoever watches the repository. Before you integrate, read the returned field list, because the response shape changed when the backend was rewired, and plan for the archive date by pointing your client at the WordPress endpoint yourself or by running your own copy of this service.

Frequently asked questions

What does the NASA apod-api service provide?

It is a microservice written in Python with the Flask micro framework that exposes the Astronomy Picture of the Day through a single versioned endpoint, returning a JSON dictionary with fields including date, title, explanation, media_type, permalink and hdurl.

When is the apod-api repository being archived?

The README marks a final update of 9-10-2026 and states that the repo will be archived and pointed to the new endpoint on December 1, 2026, and that you should not need to update your code or projects at this time.

How do I run the apod-api service locally?

Clone the repository, change into the directory, install dependencies with `uv sync`, and run `uv run python application.py`. With Docker, clone, change into the directory and run `docker compose up --build`. The tests run with `uv run pytest`.

What query parameters does the APOD endpoint accept?

An api_key, a date in YYYY-MM-DD format that must be after 1995-06-16, a count of up to 25 randomly chosen images, and a start_date and end_date range. count cannot be combined with date or with the range parameters, and start_date cannot be combined with date.

What licence is apod-api released under?

The Apache 2.0 licence, with the LICENSE file at the repository root. The package metadata names the project apod-api at version 1.1.0 and requires Python 3.12 or newer, and the last push to the repository was on 2026-10-06.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. nasa/apod-api on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/nasa-apod-api.svg)](https://hysenlabs.com/projects/nasa-apod-api)

Was this useful?