Use case · Design asset search

Fast licensed asset search for design tools

Design plugins, web editors, and agency tools need search that is fast, filterable, and legally clear. Epochal Stock’s GET https://epochalstock.com/api/v1/images endpoint is the primary discovery surface: free-text q, tag filters, stable sort, and opaque cursor pagination—backed by the full catalog on the single $50/month plan.

The problem

Product teams building design software repeatedly hit the same wall: users expect a stock browser inside the canvas, but wiring “search” is harder than it looks.

Unclear licensing

Scraped or mixed sources create legal risk for agencies and SaaS vendors shipping production art.

Slow or incomplete search

Keyword-only UIs without tags, sort control, or reliable paging feel broken after the first page.

Secret leakage

Browser-side API keys in plugins get extracted immediately—and Epochal rejects CORS catalog access for secret keys.

Rate-budget waste

Naïve “load everything” loops burn the shared 30 req/min account limit before designers finish browsing.

Who it hurts

  • Plugin authors for Figma-style, Adobe, or browser design tools who need an in-panel stock picker
  • SaaS design editors that must suggest licensed images without exposing vendor credentials
  • Agencies building internal libraries where designers expect Google-like speed and predictable filters
  • CMS teams embedding “find a hero image” into editorial workflows

Out of scope for this use case: putting a secret Bearer key in client-side JavaScript, mobile app binaries, or public plugin packages. Catalog calls must run on a server (or trusted plugin backend) that holds EPOCHAL_API_KEY. See Authentication.

Solution: GET /images

Implement search as a thin backend proxy that accepts user query parameters, validates them, and forwards a Bearer-authenticated request to the catalog API. Return only the fields your UI needs (IDs, titles, tags, dimensions, thumbnail_url / preview_url).

Capability API surface
Text search (title, description, tags) q (max 200 characters)
Theme / filter chips tags (up to 20 comma-separated) + tag_mode all|any
Result order sort: relevance, newest, oldest
Page size limit 1–100 (default 30)
Next page cursor = previous next_cursor (opaque; never invent)
Auth Authorization: Bearer … only (no query-string keys)

Full parameter reference, response shape, and live gallery: Search & list images.

Search UX → backend → Epochal

Keep secrets off the canvas. The design client talks to your API; your API talks to Epochal Stock.

Recommended integration path

1. Design UI Search box, tag chips, sort control, “load more”
2. Your backend Validate q/tags/limit · attach Bearer key · log X-Request-ID
3. Epochal GET /images · 30 req/min per account
4. Render grid Thumbnails + titles · store IDs for detail/batch/download
  • Proxy only whitelist query params; cap q length and tag count before calling Epochal
  • Map HTTP 401/402/429 to clear product errors (key, subscription, slow down)
  • Pass next_cursor back to the client as an opaque token for “Load more”
  • Never return the raw API key or full management session to the plugin/browser

Step-by-step implementation

  1. Subscribe and create a named key for this product (for example design-plugin-prod). Store it as EPOCHAL_API_KEY on the server only.
  2. Expose an internal search route such as GET /api/stock/search that accepts q, tags, tag_mode, sort, limit, and cursor.
  3. Forward to GET https://epochalstock.com/api/v1/images with Authorization: Bearer … and Accept: application/json.
  4. Render results from data.images; show data.total when useful; if data.next_cursor is present, enable “Load more” with the same filters + cursor.
  5. On selection, keep the image id. Use GET /images/{id} or POST /images/batch for richer panels, and downloads for originals.

Key parameters for design search

q

User free text. Default sort becomes relevance when q is set.

tags + tag_mode

all (default) requires every tag; any matches at least one—ideal for multi-select chips.

sort

relevance for text search; newest / oldest for browse and “fresh stock” modes.

limit + cursor

Prefer 24–48 for grids. Always reuse the server’s next_cursor unchanged.

Tip: Seed filter chips from GET /tags (tag + image_count), then search with GET /images?tags=…. See Tag-driven curation.

Code examples

Search for “golden forest”, require both nature and trees, sort by newest, return up to 30 results. Keys come from the environment—never from the design client.

cURL
curl -sS "https://epochalstock.com/api/v1/images?q=golden+forest&tags=nature,trees&tag_mode=all&sort=newest&limit=30" \
  -H "Authorization: Bearer $EPOCHAL_API_KEY" \
  -H "Accept: application/json"

Pagination pattern for “Load more”

Design grids should request a fixed filter set and page with the opaque cursor. Replay the same q, tags, tag_mode, sort, and limit on every page.

cURL
# First page
curl -sS "https://epochalstock.com/api/v1/images?q=forest&sort=newest&limit=24" \
  -H "Authorization: Bearer $EPOCHAL_API_KEY"

# Next page — paste next_cursor exactly (URL-encode if needed)
curl -sS "https://epochalstock.com/api/v1/images?q=forest&sort=newest&limit=24&cursor=CURSOR_FROM_PREVIOUS" \
  -H "Authorization: Bearer $EPOCHAL_API_KEY"
  • Do not decode, parse, or invent cursors—they are opaque server tokens
  • Invalid or expired cursors return invalid_cursor (HTTP 400)—restart from the first page
  • Each page request counts as one authenticated call toward the account limit

Rate-limit notes

The account budget is 30 authenticated requests per minute, shared across all named keys. Creating more keys does not multiply the limit. Debounce search-as-you-type on the client, and coalesce identical in-flight queries on the server.

Header / signal Meaning
X-RateLimit-Limit Always 30 for catalog API traffic
X-RateLimit-Remaining Requests left in the current window
X-RateLimit-Reset Unix time when the window resets
HTTP 429 · rate_limit_exceeded Honor Retry-After; back off with jitter

Full error matrix and retry guidance: Rate limits & errors.

Out of scope

  • Client-side secret keys — browser CORS is not a supported catalog client; keys in plugins will leak
  • Query-string API keys — rejected as query_key_rejected
  • Video or per-image credits — v1 is images only on the $50/month plan (free 1-month trial: 2,000 requests at 2/min via POST /trial/signup)
  • Invented endpoints — no GraphQL, /collections, or /upload in documented v1 catalog API

Next steps

Authentication

Bearer header format, key lifecycle, and auth error codes.

Bulk metadata

Rehydrate large saved ID lists with POST /images/batch.

Tag curation

Discover themes via GET /tags and filter search by tags.