GuidesIMAGE APIS

Free image and stock photo APIs for a website

We called 12 free image APIs from Chromium and Firefox on 26 October 2026: all 11 with a JSON API send CORS headers, so call them directly. An img tag needs no CORS at all. CORS only matters when you fetch() the bytes or read canvas pixels, which worked on 8 image hosts and failed on The Met and The Cat API. Unsplash, Pexels and Pixabay need a key you should keep on a server.

Results by API

Each image went through four checks in Chromium 156 and Firefox 157: a plain img tag, a canvas read with getImageData, the same read with crossOrigin = "anonymous", and fetch(). Keyed JSON endpoints got a fake key: a 401 that carries Access-Control-Allow-Origin proves the browser can read the reply.

Free image APIs called from a browser page, checked October 2026
APIKeyCORS on JSONCORS on image filesFree limitLicense and credit
UnsplashAccess Key, Client-ID headerYesYes, canvas works50 an hour in demo, 1,000 in productionCredit photographer and Unsplash, hotlink
PexelsKey in Authorization headerYesYes, canvas works200 an hour, 20,000 a monthCredit photographers when possible
Pixabaykey= in the URLYesYes, canvas works100 per 60 seconds, cache 24 hoursShow the source, no permanent hotlinking
OpenverseNoneYes, about 9 s uncachedThumbnails yes, originals vary20 a minute, 200 a day anonymousCC license or public domain per result
Wikimedia CommonsNone, add origin=*Only with origin=*Yes on thumbnailsNo hard read limit, go serialPer file, in extmetadata
NASA APODapi_key=, DEMO_KEYYesUntested, API returned a logo1,000 an hour with a free keyCredit in the copyright field
Lorem PicsumNoneYesYes, canvas worksNone publishedUnsplash photos, author in /v2/list
placehold.coNoneNo JSON, image URLs onlyYes, SVG by defaultNone publishedGenerated placeholder images
Dog CEONoneYesYes, canvas worksNone publishedOpen source dog photos
The Cat APIx-api-key, search works withoutYesNo header, canvas fails10,000 a month on the free planCommercial license from $99 a month
Art Institute of ChicagoNoneYesYes on IIIF, behind a bot check60 a minute per IPFilter is_public_domain=true
The MetNoneYesNo header, canvas fails80 requests a secondPublic domain when isPublicDomain

The Met and The Cat API image hosts send no CORS header, so their images display but cannot be read. The Art Institute of Chicago image server sent Access-Control-Allow-Origin: * to curl, but its Cloudflare bot check blocked our automated browsers, so test it in your own.

Show images with img, read pixels with crossOrigin

An img tag displays cross-origin images with no CORS headers. CORS matters once you draw the image on a canvas and read it back. Drawing a plain img and calling getImageData threw a SecurityError on every host we tried, even hosts that send Access-Control-Allow-Origin, because the browser only checks that header when the image is requested in CORS mode.

Set crossOrigin = "anonymous" before src and the read works on hosts that send the header: Unsplash, Pexels, Pixabay, Openverse, Wikimedia Commons, Lorem Picsum, placehold.co and Dog CEO. Do not add it to every image by default. On The Met and The Cat API hosts, crossOrigin made the image fail to load at all, and fetch() failed with "Failed to fetch". Leave it off when you only display the image.

Draw a CORS-enabled image to a canvas and read its pixels
const image = new Image();
image.crossOrigin = 'anonymous';
image.src = 'https://picsum.photos/seed/cors/400/300';
await image.decode();

const canvas = document.createElement('canvas');
canvas.width = image.naturalWidth;
canvas.height = image.naturalHeight;
const context = canvas.getContext('2d');
context.drawImage(image, 0, 0);

// Without crossOrigin, or without Access-Control-Allow-Origin on the image, this throws a SecurityError
const [red, green, blue] = context.getImageData(0, 0, 1, 1).data;
console.log({ red, green, blue }, canvas.toDataURL('image/jpeg').length);

The tainted canvas guide covers this error in depth.

Search images without a key

Openverse searches Creative Commons and public-domain images, and each result carries its license and a ready-made attribution line. Its response headers give anonymous callers 20 requests a minute and 200 a day; anonymous page_size stops at 20 and pagination at 240 results.

Wikimedia Commons needs origin=* in the query string. With it, the API sends Access-Control-Allow-Origin: * and treats the request as logged out; without it, there is no CORS header. There is no hard read limit, but Wikimedia asks for requests one after another, not in parallel. Each file's license comes back in extmetadata.

Wikimedia Commons: search images with their license, no key
const params = new URLSearchParams({
  action: 'query',
  generator: 'search',
  gsrsearch: 'lighthouse filetype:bitmap',
  gsrnamespace: '6',
  gsrlimit: '12',
  prop: 'imageinfo',
  iiprop: 'url|extmetadata',
  iiurlwidth: '640',
  iiextmetadatafilter: 'LicenseShortName',
  format: 'json',
  origin: '*',
});

const data = await fetch(`https://commons.wikimedia.org/w/api.php?${params}`).then((response) => response.json());

const images = Object.values(data.query?.pages ?? {}).map((page) => ({
  src: page.imageinfo[0].thumburl,
  page: page.imageinfo[0].descriptionurl,
  license: page.imageinfo[0].extmetadata?.LicenseShortName?.value,
}));
console.table(images);

Wikipedia takes the same parameter; see Wikipedia API from JavaScript. Two museum APIs also search with no key:

  • Art Institute of Chicago: 60 requests a minute per IP. Add query[term][is_public_domain]=true and build image URLs from each result's image_id on its IIIF server.
  • The Met: stay under 80 requests a second. The old /public/collection/v1/search returns 410 Gone since 1 October 2026; /public/collection/v1.1/search pages with offset and limit and returns 100 results by default. Use objects with isPublicDomain: true.

Unsplash, Pexels and Pixabay

All three send CORS headers on JSON and on image files, and all three need a key. Any visitor can copy a key from frontend code and burn your quota. Keep it in a small server function that returns only what the page needs.

  • Unsplash: Authorization: Client-ID KEY, 50 requests an hour in demo mode and 1,000 after production approval. Only api.unsplash.com calls count, not image loads. Hotlink the returned photo.urls, call photo.links.download_location when a user picks a photo, and credit the photographer and Unsplash with links that carry utm_source and utm_medium=referral.
  • Pexels: key in the Authorization header, 200 requests an hour and 20,000 a month, raised for free if you meet its terms. Credit photographers when possible.
  • Pixabay: key= in the URL, 100 requests per 60 seconds. Cache responses for 24 hours and show where images come from. Search results may hotlink for a while, but images you keep must be downloaded to your own server.
Unsplash search in a Cloudflare Worker, so the Access Key stays secret
export default {
  async fetch(request, env) {
    const query = new URL(request.url).searchParams.get('q') ?? 'cat';
    const upstream = await fetch(
      `https://api.unsplash.com/search/photos?per_page=12&query=${encodeURIComponent(query)}`,
      { headers: { Authorization: `Client-ID ${env.UNSPLASH_ACCESS_KEY}`, 'Accept-Version': 'v1' } },
    );
    const data = await upstream.json();

    // We send the browser only what it needs to show and credit each photo
    const body = upstream.ok
      ? data.results.map((photo) => ({
          src: photo.urls.small,
          alt: photo.alt_description,
          author: photo.user.name,
          profile: `${photo.user.links.html}?utm_source=your_app&utm_medium=referral`,
          downloadLocation: photo.links.download_location,
        }))
      : { error: data.errors?.join(', ') ?? `HTTP ${upstream.status}` };

    return Response.json(body, {
      status: upstream.status,
      headers: { 'Access-Control-Allow-Origin': 'https://your-site.example' },
    });
  },
};

No backend yet? API proxy without a server shows where a function like this can live.

Placeholders, pets and NASA APOD

  • Lorem Picsum: Unsplash photos at URLs like picsum.photos/200/300, /id/237/200/300 and /seed/NAME/200/300, with ?grayscale and ?blur=1 to 10. /v2/list gives the author.
  • placehold.co: image URLs only, SVG by default, also PNG, JPEG, GIF, WebP and AVIF, up to 4000 x 4000 pixels.
  • Dog CEO: random dog photos, with CORS on the JSON and on the images.
  • The Cat API: search works without a key. The free plan is 10,000 requests a month with an x-api-key header, and a commercial license starts at $99 a month.

NASA's APOD API is broken today. Without a date it returned HTTP 500. With a date it returned 200, but every date we tried came back titled "NASA Science" with the NASA logo as the image. The docs give DEMO_KEY 30 requests an hour and 50 a day per IP, yet its header said 10, and the 11th call got 429 with a Retry-After of about 20 hours. For a daily photo, the Bing image of the day works through the free proxy.

Where the cors.dev proxy fits

You do not need a proxy for the JSON APIs on this page. Openverse and the Art Institute answered the free proxy with a Cloudflare challenge anyway, so call them directly. The free tier also refuses keys in the URL, such as Pixabay's key= or NASA's api_key=, with 400 and X-Cors-Error: credential_in_url: a private key should not go through a public relay.

The free tier relays text and JSON only. Every raster image we sent returned 502 with X-Cors-Error: unsupported_response; a placehold.co SVG passed because SVG is text. If you must read pixels from The Met or The Cat API images, cors.dev Pro ($5 a month) relays images up to 6 MiB with CORS headers.

Good questions.

Do I need CORS to show images from an API?

No. An img tag displays cross-origin images without CORS headers. You need CORS on the image file only to read its bytes with fetch() or its pixels from a canvas.

Which free image API needs no key?

Openverse, Wikimedia Commons with origin=*, the Art Institute of Chicago and The Met all search images with no key. Lorem Picsum, placehold.co, Dog CEO and the basic search of The Cat API need none either.

Can I put my Unsplash access key in frontend code?

No. Any visitor can copy it and use up your 50 requests an hour in demo mode, or 1,000 in production. Unsplash's guidelines say the Access Key must remain confidential, so call the API from a server function.

Why does getImageData throw a SecurityError?

The canvas is tainted: the image was drawn without a CORS request, even if its server sends Access-Control-Allow-Origin. Set crossOrigin = "anonymous" before src on a host that sends the header; the tainted canvas guide has the details.

Does the NASA APOD API still work?

Not on 26 October 2026. Requests without a date returned HTTP 500, and dated requests returned the NASA logo instead of the photo. DEMO_KEY also allowed only 10 calls before a 429.

Can I hotlink Pixabay images?

Only to show search results for a while. Pixabay does not allow permanent hotlinking, so download the images you keep to your own server and show where they came from.