Use case · Original export

Safe original export pipeline

Design tools, print pipelines, and agency libraries often need full-resolution files—not only thumbnails and watermarked previews. The Developer Image API solves that with a two-step download flow: mint a five-minute, single-use download_url on your server, then fetch the binary without the Bearer API key. This page is the product-oriented playbook for building a reliable, key-safe export queue.

The problem

Shipping originals is easy to get wrong. A naive client that calls the catalog with a browser-held key, or that appends secrets to download URLs, leaks credentials into places you cannot scrub: browser history, extension stores, CDN and reverse-proxy access logs, mobile crash dumps, and third-party analytics beacons.

Key in the browser

SPA or plugin code that embeds es_live_… can be extracted from bundles. Authenticated catalog CORS is not a supported public-browser pattern (cors_not_supported).

Key on the download URL

Query-string secrets on catalog traffic are rejected (query_key_rejected). Signed download URLs already authorize a single transfer—do not bolt a key onto them.

Parallel stampede

Users often export multi-select galleries at once. The account allows one concurrent original transfer. Parallel mints return 409 download_already_active.

Token waste

Tokens last about five minutes and are single-use. Minting far ahead of the binary GET, or logging the URL for “later,” burns tokens and expands the leak surface.

Goal of this use case: Your product accepts a list of catalog image_id values from a trusted user session, enqueues them on a backend worker, mints and redeems download tokens serially, and either stores files in your private storage or streams them to the user through your own origin—never through a browser-held API key.

Safe architecture

The rule is constant across every use case on this site: the application backend holds the API key. UIs, plugins, and mobile clients talk only to your API. Your server talks to https://epochalstock.com/api/v1.

  • Store EPOCHAL_API_KEY in a secrets manager or environment variable on the worker host only.
  • Accept export requests only from authenticated product users; validate each image_id.
  • Prefer resolving metadata first (detail or batch) so you know extension, dimensions, and title before minting.
  • Stream or store originals server-side when end users must not see the signed download_url.
  • Never put keys in plugin source, browser localStorage, mobile apps, or public issue trackers.

Two-step flow (product view)

Catalog endpoints return metadata, thumbnails, and previews only. Full originals use the documented download pair described on Original downloads.

  1. MintPOST https://epochalstock.com/api/v1/downloads with Authorization: Bearer …, Content-Type: application/json, and {"image_id":"…"}. Success is HTTP 201 with a signed download_url (typically under a data envelope).
  2. Transfer — Issue a plain GET to that URL immediately. Do not send the API key as a header or query parameter. Write the body to private storage or stream it through your origin.
POST https://epochalstock.com/api/v1/downloads
Authorization: Bearer $EPOCHAL_API_KEY
Content-Type: application/json
Accept: application/json

{"image_id":"es_001_example"}

# Then, without the key:
GET $DOWNLOAD_URL
→ original image bytes

Never put API keys on download URLs. The signature in the URL is the only credential required for the binary transfer. Adding ?api_key=… or a Bearer header is unnecessary and can leak secrets into CDN and proxy logs.

Serial export queue

Accounts may run one concurrent original transfer. All named API keys on the account share that lease. Serial downloads are otherwise unlimited—there is no per-download credit balance in v1. Design your export feature as a single worker (or a mutex per account) that processes one image at a time.

Recommended

One export worker

Enqueue job rows (image_id, destination path, user id). A single consumer mints, downloads, marks complete, then takes the next row.

Allowed

Unlimited serial

After transfer N finishes and the lease is free, start N+1. Throughput is limited by file size and network—not a hard download count cap.

Blocked

Parallel originals

Fan-out across threads or lambdas against the same account will hit 409 download_already_active. Extra keys do not buy parallel leases.

Tip

Mint late

Create the token only when the worker is ready to download. A five-minute window is plenty for an immediate GET, not for overnight queues of pre-minted URLs.

  • Use a durable queue (database row, SQS, Redis list) so retries survive process restarts.
  • Set client timeouts high enough for large originals; a hung transfer may hold the concurrency lease.
  • On successful body receipt, do not retry the same download_url—it is single-use.
  • On failed mint or expired token, mint again when the worker re-attempts that job.
  • Isolate heavy export windows from interactive search traffic when possible so both share the 30 req/min budget cleanly.

Handle 409 download_already_active

If any process on the account is already transferring an original, further attempts to start another return HTTP 409 with error code download_already_active. This is expected under concurrency—not a permanent failure.

Situation What to do
Your own worker is mid-transfer Do not mint again for a second job. Wait until the first GET completes, then continue serially.
Another service/key on the same account is exporting Backoff and retry. Keys share one lease—coordinate or schedule exports.
Stale client assumed parallel was fine Catch 409, re-queue with delay/jitter, surface “export in progress” in the UI.
Hung transfer held the lease Ensure timeouts and connection cleanup so the lease is released; then retry mint.

Integration tip: Prefer preventing 409s with a local mutex over relying on remote conflicts as your primary scheduler. Still handle 409 with exponential backoff in case a second deployment or staging key competes for the same account lease.

// Pseudocode for a resilient export step
function exportOne(imageId):
  for attempt in 1..N:
    response = POST /downloads { image_id: imageId }  // Bearer key
    if response.status == 201:
      GET response.data.download_url → save bytes   // no API key
      return ok
    if response.error.code == "download_already_active":  // HTTP 409
      sleep(backoff(attempt) + jitter)
      continue
    if response.status == 429:
      sleep(Retry-After or until X-RateLimit-Reset)
      continue
    fail permanently (401, 402, 404 original_unavailable, …)
  fail: max retries exceeded

How exports interact with the rate budget

Authenticated catalog traffic shares 30 requests per minute per account. Creating more keys does not multiply the budget or the download lease.

  • POST /downloads counts toward the 30 req/min budget.
  • The binary GET on download_url does not consume an extra catalog rate-limit request, but it holds the concurrency lease until the transfer finishes.
  • On 429 / rate_limit_exceeded, honor Retry-After (or wait until X-RateLimit-Reset) with jitter—do not tight-loop mint calls.
  • Resolve many IDs with POST /images/batch (up to 100 ids) so metadata work burns fewer of the 30 requests before export starts.

Full header reference and retry strategy: Rate limits & errors.

Code examples

Serial export of several image IDs: mint a five-minute token, download without the API key, handle 409 download_already_active, then proceed to the next id. Load the key from the environment only. Base URL: https://epochalstock.com/api/v1.

cURL
# Mint (Bearer required) then download (no key). Repeat serially per image.
API_BASE="${EPOCHAL_API_BASE:-https://epochalstock.com/api/v1}"

mint="$(curl -sS -X POST "$API_BASE/downloads" \
  -H "Authorization: Bearer $EPOCHAL_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"image_id":"es_001_example"}')"

# Parse data.download_url from the 201 JSON, then:
curl -fL "$DOWNLOAD_URL" -o "es_001_example.jpg"

# On HTTP 409 download_already_active: wait and retry the mint step.
# Do not run two mints+GETs in parallel on the same account.

Endpoint details, error table, and security notes for downloads alone: Original downloads. Broader multi-language samples: Code examples.

Security callouts

Treat download_url as a secret until it expires or is used. Anyone with the URL can retrieve the full original within the five-minute, single-use window. Do not paste it into chat, tickets, public issue trackers, or client-side analytics.

Never put the Bearer key in a browser, plugin bundle, or download URL. Architecture is always: app backend holds the key → mints tokens → fetches or proxies binaries. Public browsers are not supported clients for authenticated catalog endpoints.

  • Mint tokens only on trusted servers that already hold EPOCHAL_API_KEY.
  • Do not log full download URLs to long-lived analytics or third-party APM without redaction.
  • Do not store signed URLs in browser localStorage or mobile secure storage as a “cache.”
  • Prefer streaming through your backend when end users must not observe the signed URL.
  • If a URL may have leaked before use, wait for expiry or consume it server-side and discard unwanted bytes.
  • Keep X-Request-ID when mint fails so support can investigate without secrets.
  • Follow the Epochal Stock commercial license—do not redistribute originals as a competing stock marketplace.

Key lifecycle, portal CSRF, subscription states, and the management surface: Security & billing.

Export checklist

Before you ship multi-image export

  • Backend-only Bearer key; no client-side catalog auth.
  • Validate image_id list from your product session, not anonymous callers.
  • Optional: batch-hydrate metadata for filenames, extensions, and dimensions.
  • Single-flight export worker (or per-account mutex) for original transfers.
  • Mint immediately before GET; honor five-minute and single-use rules.
  • Handle 409 download_already_active with backoff; handle 429 with Retry-After.
  • Handle permanent failures: image_not_found, original_unavailable, subscription errors.
  • Redact download URLs in logs; store files in private object storage when possible.
  • UI shows progress for a serial queue (“3 of 12”) instead of implying full parallel export.
HTTP Code Export meaning
201 Token created; download immediately without the API key.
401 invalid_api_key / invalid_download_token Fix credentials or mint a fresh token.
402 subscription_required / subscription_past_due Resolve billing before exporting.
404 image_not_found / original_unavailable Skip or surface to the user; do not infinite-retry.
409 download_already_active Wait for the active transfer; retry with backoff.
429 rate_limit_exceeded Honor Retry-After; space mint calls.

Next steps

Original downloads

Canonical two-step reference, concurrency rules, and download error table.

Security & billing

Key storage, safe proxy architecture, portal session, and plan states.