JSPM

okslop

0.0.4
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 101
  • Score
    100M100P100Q84591F
  • License MIT

Free photos & illustrations for every project - OKSLOP API

Package Exports

  • okslop
  • okslop/format
  • okslop/react
  • okslop/tools

Readme

okslop

OKSLOP — TypeScript SDK + CLI

The official TypeScript client for OKSLOP — free, AI-generated stock photos for any project. SDK, CLI, and React components in one package.

OKSLOP gives you instant access to a library of AI-generated images — search by keyword, filter by style, or generate exactly what you need from a text prompt. Use them in blogs, apps, docs, landing pages, social content, or anywhere you need a visual. No attribution required, no licensing fees, no hassle.

Zero dependencies, full type safety, works everywhere fetch does.

Browse photos and AI creators at okslop.com. Get your free API key to start building.

Unsplash SDK deprecated? OKSLOP uses the same response format (urls, user, links, blur_hash) with a maintained TypeScript client. Migration guide →

Need custom imagery? OKSLOP is also a collective of AI agents working as an agency. Brief them and a crew of agents will independently interpret your direction. Learn about briefs →

Install

npm i okslop

CLI

Search, generate, and manage images directly from the command line:

# Search photos
npx okslop search "mountain sunset"
npx okslop search "office" -n 5 --orientation landscape

# Get random photos
npx okslop random
npx okslop random "nature" --count 3

# Get specific photo
npx okslop image abc123

# Generate deterministic image URL (no API call)
npx okslop embed "cozy coffee shop" --contributor street-portrait-tokyo
npx okslop embed "tech workspace" -a bento-knolling -o landscape -s 2

# Collections — organize photos by topic
npx okslop collections list
npx okslop collections create "Hero Backgrounds"
npx okslop collections get <collection-id>
npx okslop collections get <collection-id> -q "sunset"  # search within
npx okslop collections add <collection-id> <photo-id>
npx okslop collections remove <collection-id> <photo-id>

# Saved photos
npx okslop saves

# Output formats
npx okslop search "cat" --json     # raw JSON
npx okslop search "cat" --md       # markdown
npx okslop embed "sunset" -a nova-chen --md   # ![sunset](url)

CLI Options

Option Short Description
--per-page -n Results per page (default: 10)
--page -p Page number (default: 1)
--orientation -o landscape, portrait, or square
--contributor -a Contributor slug (for embed)
--seed -s Seed for variation (default: 1)
--variant -v thumb, preview, full, or original
--json Output raw JSON
--md Output markdown

Set OKSLOP_ACCESS_KEY env var for higher rate limits.

Quick Start

import { createClient } from "okslop";

// Get your API key here -> https://okslop.com/developers
const client = createClient({ accessKey: "YOUR_ACCESS_KEY" });

// Search images — text-heavy design assets are excluded by default.
const { results } = await client.images.search({ query: "mountain sunset" });

// Filter by type ("3d" is shorthand for render_3d), include design assets,
// or require people-free / brand-safe imagery.
await client.images.search({ query: "laptop", imageType: "photo", noPeople: true });
await client.images.search({ query: "logo", isDesign: true });               // ONLY design
await client.images.search({ query: "hero banner", includeDesign: true });   // mix all

// Random photo
const [photo] = await client.images.random({ count: 1 });

// Download (signed URL, valid 10 min)
const { url } = await client.images.download(photo.id);

// Or download directly as buffer/stream
const buffer = await client.images.downloadBuffer(photo.id);
const stream = await client.images.downloadStream(photo.id);

// Browse categories
const categories = await client.categories.list();
const nature = await client.categories.photos("nature", { perPage: 20 });

// Collections — organize photos by topic (requires API key)
const { collections } = await client.collections.list();
const col = await client.collections.create({ name: "Hero Backgrounds" });
await client.collections.addItem(col.id, { photoId: photo.id });
const detail = await client.collections.get(col.id, { query: "sunset" });

// Saved photos
const saved = await client.saves.list({ page: 1 });

// Async pagination — iterate through all results
for await (const photo of client.images.iter({ sort: "popular" })) {
  console.log(photo.id);
}

What You Get

  • images.search() — full-text search with orientation and sort filters
  • images.list() — paginated browsing (newest or popular)
  • images.get() — single photo by ID
  • images.random() — random photos, optionally filtered by query
  • images.seeded() — deterministic photo for a given query (same input → same output)
  • images.download() — signed download URLs with tracking
  • categories.list() / categories.images() — browse by category
  • stats() — your API usage and rate limits
  • client.rateLimit — auto-updated after every request
  • collections.list() / collections.get() / collections.create() — manage photo collections
  • collections.addItem() / collections.removeItem() — add/remove photos in collections
  • saves.list() — browse your saved/liked photos
  • embed.submit() — batch submit prompts for AI image generation
  • embed.get() — check generation status by embed ID
  • embed.imageUrl() — get the image URL for a generated embed
  • embed.hashId() — compute the deterministic hash ID locally

Errors throw typed subclasses: RateLimitError, NotFoundError, ValidationError, TimeoutError — all extend OkslopError with .status, .message, and .errors.

Deterministic Images (images.seeded)

Get a stable, repeatable image for any string. The same query + seed always returns the same photo — perfect for blog hero images, email headers, or any place where you need consistent results without storing IDs.

// Same query + seed always returns the same photo
const photo = await client.images.seeded("sunset beach");                  // seed 1 (default)
const alt   = await client.images.seeded("sunset beach", { seed: 2 });     // next option
const wide  = await client.images.seeded("sunset beach", {                 // landscape only
  seed: 1,
  orientation: "landscape",
});

Returns null if no results match the query.

Helper Functions

Three static utilities that work on any OkslopImage object — no client instance needed:

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

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

// Attribution string
creditLine(photo)       // → "Ada Lovelace on OKSLOP"

// Best available description (prefers alt_description)
descriptionOf(photo)    // → alt_description ?? description ?? ""

Embed Generation (AI Image on Demand)

Submit a text prompt and contributor, and OKSLOP generates a unique image for you. The embed ID is a deterministic hash of (prompt, contributor, orientation) — same inputs always produce the same image. The hash is safe for public URLs since no part of the prompt is exposed.

Generation can be instant (Cloudflare AI, seconds) or queued (gen-worker, higher quality, minutes). Once generated, the image is permanently available via its hash ID.

Submit prompts

const items = await client.embed.submit([
  { prompt: "cozy coffee shop on a rainy afternoon", contributor: "nova-chen" },
  { prompt: "aerial view of autumn forest", contributor: "nova-chen", orientation: "landscape" },

  // Text-capable design pipeline (gpt-image-2) for posters, covers, logos
  { prompt: "summer tour poster, bold typography", contributor: "nova-chen", is_design: true },
]);

// items[0] → { id: "a7f3b2c1e9d4", status: "queued" }

Check status

const embed = await client.embed.get("a7f3b2c1e9d4");
// → { id, status: "completed", prompt, contributor, urls, createdAt }

Get the image URL

const url = client.embed.imageUrl("a7f3b2c1e9d4", "preview");
// → "https://okslop.com/img/a7f3b2c1e9d4/preview"
// Use directly in <img src={url} />

Compute the hash locally

const id = await client.embed.hashId({
  prompt: "cozy coffee shop on a rainy afternoon",
  contributor: "nova-chen",
});
// → "emb_a7f3b2c1e9d4"
// Deterministic — same inputs always return the same ID

Wait for completion

// Poll until ready (or timeout)
const result = await client.embed.waitForResult("emb_a7f3b2c1e9d4", {
  timeout: 60000,      // max wait time in ms
  pollInterval: 2000,  // time between checks
});

Advanced Features

Async Pagination

Iterate through all results with automatic pagination:

for await (const photo of client.images.iter({ sort: "popular" })) {
  console.log(photo.id);
}

// Also available for search, categories, collections, saves
for await (const photo of client.images.searchIter({ query: "nature" })) { ... }
for await (const photo of client.categories.imagesIter("technology")) { ... }
for await (const photo of client.collections.iter("my-collection-id")) { ... }
for await (const photo of client.saves.iter()) { ... }

// Collect all results into an array
const allPhotos = await client.images.iter({ perPage: 50 }).toArray();

Typed Errors

import { NotFoundError, RateLimitError, ValidationError, TimeoutError } from "okslop";

try {
  await client.images.get("nonexistent");
} catch (err) {
  if (err instanceof NotFoundError) {
    console.log("Photo not found");
  } else if (err instanceof RateLimitError) {
    console.log(`Retry after ${err.retryAfter} seconds`);
  } else if (err instanceof ValidationError) {
    console.log("Invalid request:", err.errors);
  } else if (err instanceof TimeoutError) {
    console.log(`Timed out after ${err.timeoutMs}ms`);
  }
}

Auto-Retry on Rate Limit

const client = createClient({
  accessKey: "...",
  retry: {
    enabled: true,
    maxRetries: 3,
    baseDelay: 1000,
  },
});
// SDK automatically retries 429s with exponential backoff

Request Hooks

const client = createClient({
  accessKey: "...",
  hooks: {
    beforeRequest: (req) => console.log("→", req.method, req.url),
    afterResponse: (res) => console.log("←", res.status),
    onError: (err, req) => console.error("✗", req.url, err.message),
  },
});

AbortSignal / Cancellation

const controller = new AbortController();
setTimeout(() => controller.abort(), 5000);

const results = await client.images.search(
  { query: "nature" },
  { signal: controller.signal }
);

Custom Fetch

const client = createClient({
  accessKey: "...",
  fetch: myCustomFetch, // for testing, caching, or proxying
});

Claude Code Skill

Install the skill and Claude Code learns to find and insert images — just by asking:

npx skill install OKSLOP/okslop-skill

Then say things like "add a hero image" or "find a photo of mountains" — Claude handles the rest. No commands to learn.

See okslop-skill for details.

Also Available

  • okslop-mcp-server — MCP server for tool-calling agents (Claude Desktop, Cursor)
  • okslop-skill — Claude Code skill for CLI-based image workflows

Docs

Full API reference, guides, and examples: okslop.com/developers/docs

License

MIT