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-jsarchived. No maintenance, no updates, no security patches. Still getting 482K npm downloads/month because nobody's migrated yet.source.unsplash.comkilled. 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
- Go to okslop.com/developers
- Create an app
- Copy your access key
No opaque verification process. No weeks-long review.
Search
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
| Tier | Requests | Notes |
|---|---|---|
| Anonymous | 30/min, 500/day | No key needed, good for prototyping |
| Free | Higher limits | Create an app at /developers |
| Pro ($29/mo) | 500K/month | For production apps |
| Scale ($99/mo) | 5M/month | High-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
urlsshape) - Check that your specific search queries return useful results
Step 3: Switch over (when you're ready)
- Replace
createApiwithcreateClient - 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.


