NASA Images Search
Purpose
Search NASA's Image and Video Library for media (images, videos, audio) matching a free-text query, filters (center, keywords, photographer, year range, location, album), or a known NASA asset ID, and return each match's title, description, NASA ID, date, capture center, media type, and direct URLs to every rendition (thumb/small/medium/large/original for images; mp4/mov/srt for videos). Read-only — never uploads, edits, or comments.
When to Use
- Find photos / videos for a topic (e.g. "Apollo 11", "Mars Perseverance", "Hubble nebula") for use in articles, slide decks, or downstream image-analysis pipelines.
- Resolve a known
nasa_id(e.g.PIA23591,as11-40-5874) to its full set of file renditions and EXIF metadata. - Enumerate an entire NASA album (e.g.
Mars_2020_Perseverance) page by page. - Bulk-build image datasets filtered by capture center (JSC, JPL, KSC, GSFC, HQ, …), photographer, or year range.
- Anywhere you'd otherwise scrape
images.nasa.gov— the public JSON API is faster, cheaper, fully unauthenticated, and structurally more reliable.
Workflow
The NASA Image and Video Library web UI at https://images.nasa.gov is a thin React SPA over a fully public JSON API at https://images-api.nasa.gov. No API key, no auth, no cookies, no Referer header, no User-Agent gating, no Verified, and no residential proxy. A plain HTTPS GET from any IP works. Lead with the API; only fall back to the browser UI if you specifically need to capture an on-page screenshot of the search-results page itself. The browser path is ~100× more expensive for the same data because the SPA hydrates the same JSON endpoint behind a fully JS-rendered page (browse snapshot of /search?q=... returns 0 listing refs).
-
Build the search URL. Base path:
https://images-api.nasa.gov/search. Compose any combination of these query params (at least one filter is required — see gotcha below):Param Meaning Notes qFree-text query Searches title + description + keywords. Space → %20or+.media_typeimage,video,audio, or comma-separated liste.g. media_type=image,video. Omit to search all types.centerNASA capture center Three-letter code: JSC,JPL,KSC,GSFC,HQ,MSFC,LRC,ARC,AFRC,SSC,GRC. Case-sensitive uppercase.keywordsComma-separated keyword list Matches against data[0].keywords.locationCapture location Free text, e.g. Kennedy Space Center. Matches againstdata[0].location.photographerPrimary photographer e.g. NASA/Bill Ingalls. URL-encode the/.secondary_creatorCredit / agency e.g. NASA/JPL-Caltech.titleTitle-only substring Narrower than q.descriptionDescription substring description_508508-compliant alt-text substring nasa_idExact asset ID lookup Single result. Equivalent to /asset/{nasa_id}for the metadata block only.year_start,year_endCapture-year bounds Inclusive, four-digit years (e.g. year_start=2020&year_end=2023). Filters againstdate_created.page1-based page number Default 1.page_sizeResults per page Default 100. Server accepts up to several hundred; throughbrowse cloud fetchthe practical ceiling is ~600 because the 1 MB Fetch response cap kicks in aroundpage_size=700(502 "response body exceeded 1MB"). Use 100 by default; raise only when you need fewer round-trips and have confirmed the response stays under 1 MB. -
Issue the GET:
browse cloud fetch \ "https://images-api.nasa.gov/search?q=apollo%2011&media_type=image&page_size=100"Response is JSON. The top-level shape is the NASA Collection+JSON envelope:
{ "collection": { "version": "1.1", "href": "<echoed request URL>", "items": [ /* see step 3 */ ], "metadata": { "total_hits": 5881 }, "links": [ { "rel": "next", "prompt": "Next", "href": "<next page URL>" } ] } }Read
collection.metadata.total_hitsfor the result count andcollection.links[?rel==next].hrefto drive pagination (see step 5). -
Decode each item.
collection.items[]is the result array. Each element has exactly three keys:href— URL to the per-asset manifest (https://images-assets.nasa.gov/{type}/{nasa_id}/collection.json). This is the same payload/asset/{nasa_id}returns and lists every file rendition (originals + thumbnails + metadata.json + captions.srt).data— single-element array;data[0]is the metadata object:
Only{ "nasa_id": "PIA23591", "title": "Seeing the Mars 2020 Rover Off", "description": "On Feb. 11, 2020, ...", "description_508": "On Feb. 11, 2020, ...", // alt-text variant, may be missing "media_type": "image", // or "video" / "audio" "center": "JPL", "date_created": "2020-02-12T00:00:00Z", "keywords": ["Mars 2020 Rover"], // may be missing "album": ["Mars_2020_Perseverance"], // may be missing "location": "Kennedy Space Center", // may be missing "photographer": "NASA/Aubrey Gemignani", // may be missing "secondary_creator": "NASA/JPL-Caltech" // may be missing }nasa_id,title,media_type,center,date_createdare guaranteed present. Everything else is optional — guard withdata[0].get(key, default).links— array of file-rendition URLs previewable on the search-results card. Formedia_type=imageitems this is the full image set (thumb,small,medium,large,orig); formedia_type=videoitems this contains only thumbnail JPEGs + a.srtcaptions link — to enumerate the actual.mp4/.movvideo files you must call/asset/{nasa_id}(step 4). Each link looks like:
The rendition suffix follows the pattern{ "href": "https://images-assets.nasa.gov/image/PIA23591/PIA23591~medium.jpg", "rel": "alternate", // or "preview" (thumb), "canonical" (orig), "captions" (.srt) "render": "image", // null for captions "width": 1280, "height": 916, "size": 112000 }{nasa_id}~{thumb|small|medium|large|orig}.{jpg|tif|png}. The~origfile may be.jpg,.png,.tif, or.tiffdepending on what was uploaded — read thehref, don't assume.jpg.
-
(Optional) Resolve full asset manifest for video files or EXIF metadata:
browse cloud fetch "https://images-api.nasa.gov/asset/PIA23591"Returns the same envelope but
items[]is now one entry per file (all renditions + ametadata.jsonentry). Use this to:- Get the original-resolution video file URL (
.mp4/.mov) formedia_type=videoitems. - Get the path to the full EXIF + AVAIL metadata JSON (the entry whose
hrefends in/metadata.json). - Get the captions
.srtURL.
Companion lookup endpoints:
https://images-api.nasa.gov/metadata/{nasa_id}— returns{"location": "<S3 URL>"}redirector. Fetch thelocationURL to get the full EXIF block (camera make/model, dimensions, allAVAIL:*fields).https://images-api.nasa.gov/captions/{nasa_id}— same redirector pattern, points to the.srtfile for videos.https://images-api.nasa.gov/album/{album_name}— returns the samecollectionenvelope filtered to one album. Album names are internal collection IDs likeMars_2020_PerseveranceorKSC_50th_Anniversary, not human-readable slugs — discover them by first running a search and readingitems[].data[0].album[].
- Get the original-resolution video file URL (
-
Paginate when
total_hits > page_size. Two equivalent paths:- Follow the server-provided next URL:
collection.links[?rel==next].href(already includes the incrementedpage). - Or increment
pageyourself:&page=2&page_size=100. Pages are 1-indexed.
When the response no longer contains a
links[].rel == "next"entry you've reached the last page. Stop atpage * page_size >= total_hitsas a safety bound. - Follow the server-provided next URL:
-
Build the public web URL for a result (for citing back to a user-facing page):
https://images.nasa.gov/details/{nasa_id}This is the SPA detail page. The API returns no direct field for it — just construct from
nasa_id. The URL renders for any valid NASA ID.
Browser fallback
Use only when the JSON API is unreachable (extremely rare — no documented downtime in our trace, no rate-limit at 10 req/s) or when you specifically need a screenshot of the rendered search page:
- Create a bare Browserbase session — no Verified, no proxies required.
images.nasa.govserves anonymous traffic without any anti-bot challenge.SID=$(browse cloud sessions create --keep-alive | jq -r '.id') - Navigate to
https://images.nasa.gov/search?q={query}&media_type=image(the SPA's search URL accepts the same param shape). - Wait ~3 seconds for hydration, then
browse get html bodyorbrowse snapshot. - The page hits
https://images-api.nasa.gov/search?...under the hood — capture the XHR viabrowser-traceand parse the same JSON shape from step 3 above, or fall back to regex-scraping the rendered cards (data-asset-id="<nasa_id>"plus.image-asset__image img[src]for the thumbnail). Always prefer the captured XHR over the rendered cards — the cards render only the visible page. - Release:
browse cloud sessions update "$SID" --status REQUEST_RELEASE.
Site-Specific Gotchas
- At least one search parameter is required. Bare
GET /searchreturns400 {"reason": "Expected 'q' text search parameter or other keywords."}.media_type=imagealone counts ("228,163 image hits");q=alone counts; any filter combination counts. Do not emit a bare/searchrequest even as a probe. page_sizeserver limit vs. transport limit. The API server itself accepts at leastpage_size=600. Throughbrowse cloud fetch, requests withpage_size >= 700(≈ 1 MB JSON) return502 "response body exceeded the maximum allowed size of 1MB"— that's the Fetch proxy, not NASA. Stick topage_size=100for general use; if you raise it, cap at 500 to stay safely under the Fetch limit, or switch to a browser session whose XHR isn't proxy-capped.- Most metadata fields are optional. Only
nasa_id,title,media_type,center,date_createdare guaranteed.keywords,album,location,photographer,secondary_creator,description,description_508are all sometimes-absent. Always.get(key)with a default. - Video search results don't include the actual video file URLs. For
media_type=videoitems,items[].links[]contains only thumbnail JPEGs (~thumb,~small,~medium,~large) plus a.srtcaptions link. To get the.mp4/.movoriginal, you must call/asset/{nasa_id}in a follow-up request. Images, by contrast, ship all five renditions directly in the search response. - Original-image file extensions vary.
{nasa_id}~origmay be.jpg,.png,.tif, or.tiff(verified:PIA23591~orig.jpg,NHQ201907190146~orig.tif). Read thehreffromlinks[].rel == "canonical"; don't construct the URL from the nasa_id + assume.jpg. - Some
nasa_idvalues contain literal spaces. Video asset IDs likeNDTV000908_Apollo_Digest_Series_Spacecraft for Apollohave unencoded spaces in theirhrefURLs as returned by the API. When following these URLs, URL-encode the space (%20) or your HTTP client will reject the request. centeris case-sensitive uppercase.center=jplreturns 0 hits;center=JPLreturns thousands. The canonical codes are the three- or four-letter NASA center abbreviations (JPL,JSC,KSC,GSFC,HQ,MSFC,LRC,ARC,AFRC,SSC,GRC).media_typeaccepts comma-separated lists.media_type=image,videoreturns mixed results in the same response — branch downstream ondata[0].media_typeper item. There is nomedia_type=alltoggle; omitting the param entirely is the "all types" search.year_start/year_endfilter againstdate_created, not upload date. Some assets have adate_createddecades before they were uploaded (e.g. Apollo-era photos uploaded in 2007). Filteringyear_start=2020excludes them even if NASA published them in 2020.- Album names are internal collection IDs, not slugs.
/album/apollo11returns404 {"reason": "No assets found for album=\"apollo11\" page=1"}. The right name is something likeApollo_11_50th_AnniversaryorKSC_50th_Anniversary. Discover the exact string by running a/search?q=apollo+11&page_size=5first and readingitems[].data[0].album[]. /metadata/{nasa_id}is a two-hop endpoint. It returns{"location": "<S3 URL>"}— the actual EXIF block lives at that S3 URL (animages-assets.nasa.gov/.../metadata.json). Same pattern for/captions/{nasa_id}(returns.srtURL). Fetch thelocationto get the actual payload. There is no direct-content variant.- 404 error bodies are JSON, not HTML.
/asset/<bad-id>→{"reason": "No AssetDB records for nasaid=..."}./album/<bad-name>→{"reason": "No assets found for album=\"...\" page=1"}. Parsereasonfor human-readable failure messages. - No auth, no rate-limit observed, no anti-bot. 10-request bursts from the same IP all returned 200 in our trace. NASA does not publish a documented rate-limit for this API; stay under ~10 req/s as a self-imposed politeness ceiling. No
Referer,User-Agent, or cookies are checked. hrefURLs may usehttp://(nothttps://). Several response fields — notably the/asset/{nasa_id}item hrefs — returnhttp://images-assets.nasa.gov/.... The same URLs work over HTTPS; upgrade the scheme client-side if you care about TLS.- Search SPA at
images.nasa.gov/search?...returns 0 snapshot refs. It's a JS-hydrated React app —browse snapshotafterwait loadshows the chrome but no per-result refs. Don't try to click cards; use the underlying API or capture the XHR.
Expected Output
{
"query": "apollo 11",
"filters": {
"media_type": "image",
"year_start": null,
"year_end": null,
"center": null,
"page": 1,
"page_size": 100
},
"total_hits": 5881,
"page": 1,
"page_size": 100,
"next_page_url": "https://images-api.nasa.gov/search?q=apollo+11&media_type=image&page_size=100&page=2",
"results": [
{
"nasa_id": "jsc2007e034221",
"title": "Apollo 11 spacecraft pre-launch",
"description": "Personnel atop the 402-ft. Mobile Service Structure look back at the Apollo 11 spacecraft as the tower is moved away during a Countdown Demonstration Test. Photo filed 11 July 1969.",
"media_type": "image",
"center": "JSC",
"date_created": "1969-07-11T00:00:00Z",
"keywords": ["Apollo", "Apollo 11", "Launch"],
"album": ["KSC_50th_Anniversary"],
"location": null,
"photographer": null,
"secondary_creator": null,
"asset_manifest_url": "https://images-assets.nasa.gov/image/jsc2007e034221/collection.json",
"details_url": "https://images.nasa.gov/details/jsc2007e034221",
"renditions": {
"thumb": { "url": "https://images-assets.nasa.gov/image/jsc2007e034221/jsc2007e034221~thumb.jpg", "width": 487, "height": 640, "size_bytes": 60000 },
"small": { "url": "https://images-assets.nasa.gov/image/jsc2007e034221/jsc2007e034221~small.jpg", "width": 487, "height": 640, "size_bytes": 60000 },
"medium": { "url": "https://images-assets.nasa.gov/image/jsc2007e034221/jsc2007e034221~medium.jpg", "width": 975, "height": 1280, "size_bytes": 176000 },
"large": { "url": "https://images-assets.nasa.gov/image/jsc2007e034221/jsc2007e034221~large.jpg", "width": 1463, "height": 1920, "size_bytes": 332000 },
"orig": { "url": "https://images-assets.nasa.gov/image/jsc2007e034221/jsc2007e034221~orig.jpg", "width": 2341, "height": 3072, "size_bytes": 1402000 }
}
}
]
}
Video result variant (media_type=video) — note renditions contains only thumbnails + captions; fetch asset_manifest_url to enumerate the .mp4/.mov files:
{
"nasa_id": "NDTV000908_Apollo_Digest_Series_Spacecraft for Apollo",
"title": "Apollo Digest Series — Spacecraft for Apollo",
"media_type": "video",
"center": "HQ",
"date_created": "1967-01-01T00:00:00Z",
"asset_manifest_url": "https://images-assets.nasa.gov/video/NDTV000908_Apollo_Digest_Series_Spacecraft%20for%20Apollo/collection.json",
"renditions": {
"thumb": { "url": "https://images-assets.nasa.gov/video/.../...~thumb.jpg" },
"captions": { "url": "https://images-assets.nasa.gov/video/.../....srt", "rel": "captions" }
},
"note": "Video file URLs (.mp4/.mov) require a follow-up GET to asset_manifest_url."
}
Not-found variant (when resolving a specific nasa_id via /asset/{nasa_id} or /album/{album_name}):
{
"success": false,
"reason": "asset_not_found",
"nasa_id": "THIS_DOES_NOT_EXIST_zzz",
"api_message": "No AssetDB records for nasaid=THIS_DOES_NOT_EXIST_zzz"
}