Image Asset Server REST API guide
A small JSON API over HTTPS. No key is needed on https://image.shep.bot. Every endpoint is described in the OpenAPI 3.1 spec, which you can explore and try in the interactive reference.
Base URL: https://image.shep.bot
The flow
GET /v1/search: find candidates across every source.GET /v1/images/{id}: read the licence verdict, credit line, pixel size, every file and thedisplayURL of one result.GET /v1/images/{id}/download?width=1920: a302to the file on the source.
Plus GET /v1/images/{id}/credits for ATTRIBUTION.md, and POST /v1/sets to pick every image a page, deck or post needs in one call.
No image passes through this server: thumbnails, previews, display URLs and downloads all point at the source’s own servers.
Search
curl "https://image.shep.bot/v1/search?q=mountain+lake&orientation=landscape&min_resolution=1080&commercial=true&limit=10"
| Parameter | Description |
|---|---|
q |
Free text, written like a designer would: mountain lake at sunrise, van gogh sunflowers, rocket icon. Size (4k, high resolution, 1080p), orientation (vertical, 16:9, square), format (svg, png, jpg), transparency (transparent, no background) and kind (photo, watercolor, icon, black and white) words become filters. May be empty when category is set (browse). |
category |
Comma-separated dot-path categories. A parent matches its children: art matches art.painting and art.drawing. See the Category schema for the full list (nature.ocean, nature.plants, animals, city.skyline, architecture, people.work, food, objects, space.planets, science, art.painting, archive.map, background, icons…). |
style |
Comma-separated kind and look: photo, illustration, painting, drawing, vector, icon, transparent, black-white. vector and icon filter strictly; the others are ranked first and filtered only with strict=true, because sources rarely tag them. |
orientation |
landscape, portrait or square (comma-separated for several). |
min_resolution |
Minimum short side in pixels: 720, 1080, 1440, 2160 (or hd, fhd, large, 4k). A 2160×3840 phone wallpaper counts as 2160. Vectors always pass. |
transparent |
true: only images with a transparent background (icons, SVGs, PNG cut-outs). |
format |
Comma-separated: jpg, png, svg, webp, tif. |
commercial |
true: only images whose licence allows commercial use (verdict yes or yes-with-credit). Editorial-only images can still appear: check license.editorial. |
free |
true: exclude paid images. |
downloadable |
true: only images with direct file links. |
strict |
true: drop images whose orientation, size or transparency is unknown when you filter on it (by default they are kept, ranked lower). |
providers |
Comma-separated source ids from /v1/providers, e.g. nasa,met,wikimediacommons. |
limit |
Max results, 1–100 (default 24). |
offset |
Per-source offset for the next page (e.g. 24). |
The response (shortened):
{
"query": "crab nebula",
"filters": { "minResolution": 1080, "commercialOnly": true },
"results": [
{
"id": "nasa:PIA03606",
"provider": "nasa",
"title": "Crab Nebula Mosaic",
"url": "https://images.nasa.gov/details/PIA03606",
"category": "space",
"styles": ["photo"],
"orientation": "square",
"width": 3864, "height": 3864, "year": 2005,
"thumbnailUrl": "https://images-assets.nasa.gov/image/PIA03606/PIA03606~small.jpg",
"previewUrl": "https://images-assets.nasa.gov/image/PIA03606/PIA03606~medium.jpg",
"license": {
"id": "LicenseRef-NASA-Media", "name": "NASA media (public domain in the US)",
"attributionRequired": true, "editorial": true, "assertedBy": "source",
"verdict": "yes-with-credit",
"note": "Generally not copyrighted in the US; credit NASA. Do not imply NASA endorses you…"
},
"attribution": "“Crab Nebula Mosaic” by NASA/JPL (NASA Image and Video Library), https://images.nasa.gov/details/PIA03606, …",
"downloadable": true
}
],
"providers": [
{ "provider": "nasa", "name": "NASA Image and Video Library", "status": "ok", "count": 6, "tookMs": 412 },
{ "provider": "unsplash", "name": "Unsplash", "status": "link", "count": 0, "searchUrl": "https://unsplash.com/s/photos/crab-nebula" }
]
}
thumbnailUrl and previewUrl are meant for <img> tags in a browser: they load straight from the source. Icons are SVGs (vector: true, transparent: true).
Licence verdicts
Every license has a verdict that answers one question: can this go into something you publish commercially (an ad, a product page, a client website, a printed book)?
verdict |
Meaning |
|---|---|
yes |
Commercial use OK, no credit required (CC0, public domain, museum open access). |
yes-with-credit |
Commercial use OK with a credit line: use attribution (CC BY, NASA media, MIT/Apache icon sets). |
restricted |
Conditions that usually rule out a normal project (share-alike). |
clearance-required |
Paid stock, or images that need releases per use. |
check-terms |
Unclear: no rights statement, not evaluated, or possibly a third party’s image. |
no |
Non-commercial or no-derivatives only. |
license.editorial: true means people, insignia or brands appear without releases: fine for articles, books and education, not for advertising. license.assertedBy says who stated the licence: source (the site records it per item), site (one licence for the whole site) or uploader (an unverified claim; such licences are never better than check-terms). More in Image licences explained.
Image details
curl "https://image.shep.bot/v1/images/nasa%3APIA03606"
Ids contain : and sometimes spaces or slashes (Iconify ids look like iconify:mdi:rocket): URL-encode them. Returns the image plus:
files[]: every file the source offers, each withurl,filename,format(jpg,png,svg,tif…),role(original,rendition,thumbnail),width,heightandsizeByteswhen known.display:{ url, type, width, height }, the file a browser should show in a detail view (a displayable format close to 1280 px wide). It loads from the source.shareUrl: a link that opens the image on the website (/search?image=<id>), which unfurls with its own preview card.
Choose files
curl "https://image.shep.bot/v1/images/<id>/files?width=1920&format=jpg"
The file a download would pick: with width, the smallest file at least that wide (a 1920 px copy rather than a 60 MB TIFF), else the biggest below it; an SVG original always fits. format prefers a format, role a kind of file. Returns totalBytes, the licence and the credit line. all=true returns every file.
Download
curl -L -o hero.jpg "https://image.shep.bot/v1/images/<id>/download?width=1920"
Always a 302 redirect to the chosen file on the source (so use -L), with X-License and X-Attribution headers. The same width, format and role parameters as /files choose the file. Images without direct files answer 409 with the image’s url.
Credits
curl "https://image.shep.bot/v1/images/<id>/credits" # ATTRIBUTION.md
curl "https://image.shep.bot/v1/images/<id>/credits?format=json" # credits.json
curl -o credits.zip "https://image.shep.bot/v1/images/<id>/credits?format=zip"
The zip holds ATTRIBUTION.md and credits.json only, never the image.
Plan an image set
curl -X POST "https://image.shep.bot/v1/sets" -H "content-type: application/json" \
-d '{"slots":["hero: mountain lake at sunrise (1920px)","3 icons: rocket, shield, chart","botanical illustration of a rose"],"width":1600,"name":"landing"}'
slots is a list, or text with one slot per line (“(1200px)” at the end sets a slot’s width; “3 icons: rocket, shield, chart” makes one slot per item). Optional: width (default 1600), orientation, styles (e.g. ["photo"]), commercialOnly (default true), providers, alternatives (0–5, default 3), name. Up to 24 slots.
The response is { plan, exports }. plan.slots[] has, per slot, index, description, width, image, file (the file the exports use: the smallest at least as wide as the slot), alt (suggested alt text), alternatives and a note when the pick is smaller, a different shape or editorial. plan.commercialSafe is true when every pick is yes or yes-with-credit. exports holds the text of images.json, credits.html, download.sh, ATTRIBUTION.md and credits.json. The image set builder is a page on top of this endpoint.
List sources
curl "https://image.shep.bot/v1/providers"
Usage statistics and source health
curl "https://image.shep.bot/v1/stats"
Aggregate usage (searches by surface, image views, downloads, MCP tool calls, top clients and categories) for the last 24 hours and 7 days, plus each source’s success rate and p50/p95 latency. The same data is on the statistics page. GET /v1/catalog returns the daily count of what each source holds.
Examples
JavaScript / TypeScript
const base = "https://image.shep.bot";
const res = await fetch(`${base}/v1/search?` + new URLSearchParams({ q: "misty forest", orientation: "landscape", min_resolution: "1080", commercial: "true" }));
const { results } = await res.json();
const pick = results.find((c) => c.downloadable && !c.license?.editorial);
console.log(pick.title, pick.license.verdict, pick.attribution ?? "no credit needed");
// Show it in the page, straight from the source, with its credit as the caption
const details = await (await fetch(`${base}/v1/images/${encodeURIComponent(pick.id)}`)).json();
const figure = document.createElement("figure");
figure.append(Object.assign(document.createElement("img"), { src: details.display.url, alt: pick.title }));
figure.append(Object.assign(document.createElement("figcaption"), { textContent: pick.attribution ?? pick.title }));
document.body.append(figure);
Python
import requests
base = "https://image.shep.bot"
r = requests.get(f"{base}/v1/search", params={"q": "saturn", "min_resolution": "1080", "commercial": "true"})
image = next(c for c in r.json()["results"] if c["downloadable"])
# The download endpoint redirects to the file on the source; requests follows it.
with requests.get(f"{base}/v1/images/{requests.utils.quote(image['id'], safe='')}/download", params={"width": 1920}, stream=True) as dl:
dl.raise_for_status()
with open("saturn.jpg", "wb") as f:
for chunk in dl.iter_content(1 << 20):
f.write(chunk)
print(image.get("attribution", "no credit needed"))
A static-site build
# Fetch a 1600 px hero at build time; keep the credit next to it
curl -L -o public/hero.jpg "https://image.shep.bot/v1/images/<id>/download?width=1600"
curl -o public/CREDITS.md "https://image.shep.bot/v1/images/<id>/credits"
Errors
Errors are JSON: { "error": "message" }.
| Status | Meaning |
|---|---|
400 |
Bad parameter (e.g. an unknown category or an unreadable min_resolution) or unknown source id. |
401 |
API key required (self-hosted servers with IMAGE_SERVER_API_KEY only). |
404 |
Unknown image, or no file matches the width/format/role. |
409 |
The source has no direct downloads for this image; use the returned url. |
429 |
Rate limit exceeded; wait Retry-After seconds. |
502 |
The source site failed. |
Authentication (self-hosted)
The public server is open. If you run your own with IMAGE_SERVER_API_KEY, send the key as Authorization: Bearer <key> or x-api-key: <key>. Keys are accepted in headers only, never as a URL parameter, so they stay out of logs, browser history and Referer headers.
Rate limits and versioning
The public server allows 120 requests per minute per client. Every response carries RateLimit-Policy and RateLimit headers (plus RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset); going over returns 429 with Retry-After. The API is versioned in the path (/v1), only grows additively within a version, and announces deprecations with Deprecation and Sunset headers at least 90 days ahead. Details: Versioning, deprecation & rate limits.
Fair use
Cache results on your side for repeated queries, keep requests to a human pace, and respect each image’s licence and each source’s terms. Don’t use the API to mirror a source’s catalogue. The server already caches source responses for a few minutes and deduplicates identical in-flight requests. Image Asset Server is a free, non-commercial service and does not re-host or resell images.
AI agent? This page is also available as markdown.