Blog/General

The Unsplash API Is Dead. Here's How to Migrate in 10 Minutes.

Published March 27, 2026

The Unsplash API Is Dead. Here's How to Migrate in 10 Minutes.
Beige Pentium
Cover image by OKSLOP contributor Beige Pentium

If you're still importing unsplash-js, you're importing an archived package. (For the bigger picture on why, see what happened to Unsplash.) Unsplash archived the SDK in August 2024. Their official guidance is "just use fetch."

That's not a developer experience strategy. That's an abandonment signal.

Here's what's happened since Getty acquired Unsplash:

  • unsplash-js archived. No maintenance, no updates, no security patches. Still getting 482K npm downloads/month because nobody's migrated yet.
  • source.unsplash.com killed. The simple URL endpoint that thousands of projects relied on? Gone. No migration path documented.
  • Rate limits tightened. Unverified apps get 50 requests/hour. The verification process is slow and opaque.
  • Getty "going concern" warning. The parent company's financial troubles mean Unsplash's API isn't getting investment.

None of this means Unsplash was bad. It was great, and if it still works for your project, keep using it. But if you're hitting rate limits, getting bitten by the archived SDK, or just want a backup plan, it's worth knowing what else is out there.

Try it alongside Unsplash first

Don't rip anything out. Install the OKSLOP SDK next to your existing Unsplash integration and see if the results work for your use case:

npm install okslop
// Your existing Unsplash code stays untouched
import { createApi } from "unsplash-js";
const unsplash = createApi({ accessKey: "YOUR_UNSPLASH_KEY" });

// Add OKSLOP alongside it
import { createClient } from "okslop";
const okslop = createClient({ accessKey: "YOUR_OKSLOP_KEY" });

// Try the same search on both and compare results
const unsplashResults = await unsplash.search.getPhotos({ query: "mountain sunset" });
const okslopResults = await okslop.photos.search({ query: "mountain sunset" });

The response format is deliberately compatible: same urls, user, links, blur_hash structure. So you can test OKSLOP results in your existing UI without changing your rendering code.

If the results work for you, great. Migrate at your own pace. If they don't, you've lost nothing.

Getting an API key

  1. Go to okslop.com/developers
  2. Create an app
  3. Copy your access key

No opaque verification process. No weeks-long review.

Unsplash uses keyword matching. OKSLOP uses semantic vector search (Jina CLIP v2). That means you can search for concepts, not just tags:

// This actually works: no image was manually tagged with these words
const { results } = await client.photos.search({
  query: "lonely person in a vast landscape",
  per_page: 10,
  orientation: "landscape",
});

The response shape matches what you're used to:

results[0].id            // "a7f3b2c1e9d4"
results[0].urls.regular  // "https://okslop.com/img/a7f3b2c1e9d4/preview"
results[0].urls.thumb    // "https://okslop.com/img/a7f3b2c1e9d4/thumb"
results[0].urls.full     // "https://okslop.com/img/a7f3b2c1e9d4/full"
results[0].blur_hash     // "LKO2?U%2Tw=w]~RBVZRi..."
results[0].user.name     // "Nova Chen"
results[0].alt_description // "A solitary figure..."

Random photos

// Get a random photo
const [photo] = await client.photos.random({ count: 1 });

// Random photo matching a query
const [photo] = await client.photos.random({
  count: 1,
  query: "office workspace",
});

Deterministic images (seeded)

This is something Unsplash never had. Same query + seed always returns the same photo:

// Always returns the same photo for this query
const photo = await client.photos.seeded("sunset beach");

// Need a different option? Change the seed
const alt = await client.photos.seeded("sunset beach", 2);

This is useful for blog templates, placeholder systems, or anywhere you need a consistent image without storing IDs.

Helper functions

import { pickUrl, creditLine, descriptionOf } from "okslop";

// Pick the best URL for a target width
pickUrl(photo, 300)   // → photo.urls.thumb   (≤400px)
pickUrl(photo, 700)   // → photo.urls.small   (≤800px)
pickUrl(photo, 1500)  // → photo.urls.full    (≤2000px)

// Attribution
creditLine(photo)     // → "Nova Chen on OKSLOP"

// Alt text
descriptionOf(photo)  // → alt_description ?? description ?? ""

React: adding an OKSLOP picker

The okslop-react package gives you a drop-in image picker. If you're already using an Unsplash picker, you can run both side by side and let users choose:

npm install okslop-react
import { OkslopPicker } from "okslop-react";

// Same callback shape, works with your existing upload logic
<OkslopPicker
  onFinishedUploading={(blob) => {
    uploadToYourBackend(blob);
  }}
/>

The picker includes semantic search, preview, and variant selection. You can offer it as a second image source alongside Unsplash, or switch fully once you're comfortable with the results.

REST API (no SDK)

If you prefer raw fetch (which is apparently what Unsplash recommends now), OKSLOP's REST API is straightforward:

// Search
const res = await fetch(
  "https://okslop.com/api/v1/search/photos?query=mountain+sunset&per_page=10",
  { headers: { Authorization: "Bearer YOUR_KEY" } }
);
const { results } = await res.json();

// Random
const res = await fetch(
  "https://okslop.com/api/v1/photos/random?count=3",
  { headers: { Authorization: "Bearer YOUR_KEY" } }
);

// Single photo
const res = await fetch(
  "https://okslop.com/api/v1/photos/a7f3b2c1e9d4",
  { headers: { Authorization: "Bearer YOUR_KEY" } }
);

Anonymous access works at 30 requests/minute without a key, enough for development and prototyping.

Rate limits

TierRequestsNotes
Anonymous30/min, 500/dayNo key needed, good for prototyping
FreeHigher limitsCreate an app at /developers
Pro ($29/mo)500K/monthFor production apps
Scale ($99/mo)5M/monthHigh-traffic applications

Rate limit headers in every response:

X-Ratelimit-Limit: 1000
X-Ratelimit-Remaining: 997
X-Ratelimit-Reset: 1711526400

What's different from Unsplash

Be upfront with your users about these differences:

The images are AI-generated. Not photographs. OKSLOP discloses this by default. It's in the name. If your app displays image credits, the attribution will say "on OKSLOP," which signals AI origin.

Semantic search instead of keyword search. This is usually better, since you can search for moods and concepts, not just tags. But results will differ from Unsplash for identical queries.

Growing library. OKSLOP's library grows through briefs and the contributor game, not photographer uploads. The catalog is different from Unsplash's, not a mirror of it.

No attribution required. Nice-to-have, not required. But creditLine(photo) makes it easy if you want to include it.

A gradual migration path

You don't have to do this all at once. Here's a sensible order:

Step 1: Try it (5 minutes)

  • npm install okslop
  • Get an API key at okslop.com/developers
  • Run a few test searches, compare results to Unsplash

Step 2: Run both side by side (optional, but recommended)

  • Add OKSLOP as a fallback or secondary source
  • Test your rendering code with OKSLOP responses (same urls shape)
  • Check that your specific search queries return useful results

Step 3: Switch over (when you're ready)

  • Replace createApi with createClient
  • Update search calls (.search.getPhotos.photos.search)
  • Update random calls (.photos.getRandom.photos.random)
  • Update any attribution strings
  • Remove Unsplash API key from your env

No rush on step 3. Unsplash still works. The SDK is archived, not broken. But it won't get security patches, and the rate limits aren't getting more generous. Having a tested alternative ready means you're not scrambling if things get worse.

The OKSLOP SDK is maintained, typed, and actively developed. It's a good backup to have even if you're not ready to switch today.