Developer Image API · v1

Original downloads

Original files are never exposed as permanent filesystem paths. Instead, your server creates a short-lived, single-use download URL with POST /downloads, then fetches that URL without the Bearer API key. Tokens last five minutes, become invalid after the first successful use, and only one original transfer may run at a time per account.

Overview

Catalog endpoints return metadata, thumbnails, and watermarked previews only. When your product needs the full original (for export, print pipeline, or licensed asset delivery), use the two-step download flow. The design keeps secret API keys out of download URLs so they never land in browser history, proxy logs, CDN access logs, or analytics beacons.

Two steps

POST https://epochalstock.com/api/v1/downloads with image_id and Bearer auth, then GET the returned download_url with no API key.

Five minutes

Each signed URL is valid for about five minutes and is single-use. After first use or expiry, create a new token.

One concurrent transfer

Only one original may transfer at a time per account. Parallel attempts return 409 download_already_active. Serial downloads are unlimited.

Lease, not rate hit

The binary GET does not consume an extra catalog rate-limit request, but it holds the concurrency lease until the transfer finishes.

Prerequisite: Resolve a valid image ID first via search, image detail, or batch metadata. Originals for missing or unavailable assets return catalog errors such as image_not_found or original_unavailable.

Two-step download flow

Mint a token on your trusted server, then pull the binary. Never attach the Bearer key to the download URL.

  1. Create a tokenPOST https://epochalstock.com/api/v1/downloads with Authorization: Bearer …, Content-Type: application/json, and body {"image_id":"…"}. A successful response is HTTP 201 with a signed download_url (and related metadata such as request id in meta).
  2. Download the original — Issue a plain GET to that download_url. Do not add the API key as a header or query parameter. Follow redirects if your client requires it, and write the response body to disk as the original image bytes.

Never put API keys on download URLs. The signed URL already authorizes a single transfer. Adding ?api_key=… or a Bearer header is unnecessary and can leak secrets into logs. Query-string keys on catalog endpoints are rejected with query_key_rejected; treat download URLs the same way—no secrets in the path or query.

Step 1 — Create a download token

Authenticated endpoint. Counts against the account’s 30 requests/minute catalog budget.

POST https://epochalstock.com/api/v1/downloads
Authorization: Bearer $EPOCHAL_API_KEY
Content-Type: application/json
Accept: application/json

{"image_id":"es_001_example"}
Field Location Description
image_id JSON body Required. Catalog image identifier (for example from search or detail).
Authorization Header Bearer live API key. Required for token creation only.
download_url Response Signed URL for the binary transfer. Treat as a secret until it expires.

Minimal cURL for token creation:

curl -sS -X POST "https://epochalstock.com/api/v1/downloads" \
  -H "Authorization: Bearer $EPOCHAL_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"image_id":"es_001_example"}'

Expect HTTP 201 on success. Parse the JSON body for download_url (under data depending on response envelope). Capture X-Request-ID on failure.

Step 2 — Fetch the binary original

Unauthenticated relative to your API key: the signature in the URL is the credential. Download immediately after minting so the five-minute window is not wasted on queue delay.

GET $DOWNLOAD_URL
# No Authorization header
# No api_key query parameter

curl -fL "$DOWNLOAD_URL" -o image.jpg
  • Tokens expire after five minutes.
  • Tokens become invalid after first use—do not reuse a URL for retries after a successful body.
  • If the token is expired or already used, mint a new token with POST /downloads.
  • Write the response to a file using the known extension from image metadata when available.
  • Set sensible client timeouts for large originals; a hung transfer may hold the concurrency lease.

Concurrency rules

Each account may run one concurrent original transfer. All named API keys on that account share the same lease. Creating more keys does not allow parallel originals.

Allowed

Unlimited serial downloads

Download A, wait until it completes, then download B, C, … without a hard cap on count. Serial throughput is limited by transfer time and your integration design—not a download credit balance.

Blocked

Parallel originals

Starting a second original while another is active returns HTTP 409 with download_already_active. Finish or abandon the first transfer, then retry.

Shared

Account-wide lease

Staging and production keys on the same account compete for the single lease. Isolate high-volume export jobs or serialize them in a worker queue.

Integration tip: Queue original downloads in a single worker (or a mutex per account) so concurrent product features never stampede POST /downloads. On 409 download_already_active, wait and retry with backoff rather than minting many tokens that cannot be redeemed yet.

How downloads interact with rate limits

  • POST /downloads is an authenticated catalog-style request and counts toward the 30 requests per minute per account limit.
  • The subsequent binary GET on download_url does not consume an additional catalog rate-limit request.
  • The binary transfer does use the concurrency lease until the transfer completes.
  • Prefer POST /images/batch when resolving many IDs so metadata work uses fewer of the 30 requests before you start downloading.

Full headers, 429 behavior, and error codes: Rate limits & errors.

Code examples

Create a five-minute token for es_001_example, then download the original to a local file. Load the API key from the environment only. Never log the signed download_url to analytics platforms.

cURL
# 1) Create single-use download token (Bearer required)
curl -sS -X POST "https://epochalstock.com/api/v1/downloads" \
  -H "Authorization: Bearer $EPOCHAL_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"image_id":"es_001_example"}'

# 2) Download binary WITHOUT the API key
# Replace $DOWNLOAD_URL with data.download_url from the 201 response
curl -fL "$DOWNLOAD_URL" -o image.jpg

Security

Treat download URLs as secrets. A signed download_url can retrieve the full original until it expires or is used. Do not paste it into chat, tickets, public issue trackers, or client-side analytics. Prefer streaming the bytes through your backend when end users must not see the signed URL.

  • Mint tokens only on a trusted server that already holds the Bearer key.
  • Never log full download_url values to long-lived analytics or third-party APM without redaction.
  • Do not append API keys to download URLs or store tokens in browser localStorage.
  • Do not redistribute originals as standalone stock files or power a competing marketplace; follow the Epochal Stock license.
  • If a URL may have leaked before use, wait for expiry or consume it server-side and discard the bytes you do not need.
  • Keep X-Request-ID when token creation fails—support can investigate without secrets.

Broader key handling, portal CSRF, and billing isolation: Security & billing.

Download-related errors

Failures return structured JSON on authenticated calls. Binary URL failures may return status codes without a full catalog envelope—still capture status and any request id you have from the mint step.

HTTP Code When it happens What to do
401 invalid_api_key Bearer key missing, revoked, or wrong on POST /downloads. Fix credentials; do not retry blindly.
401 invalid_download_token Token expired, already used, or malformed. Mint a new token and download once promptly.
404 image_not_found Unknown image_id. Re-search or refresh metadata; confirm the ID.
404 original_unavailable Metadata exists but original cannot be served. Skip asset or report with Request-ID.
409 download_already_active Another original transfer is in progress for the account. Wait for the active transfer; retry with backoff.
402 subscription_required / subscription_past_due Plan not active. Resolve billing before minting tokens.
429 rate_limit_exceeded Too many authenticated requests this minute. Honor Retry-After; space out mint calls.

Complete catalog of codes: Error reference.

Next steps

Code examples

Broader multi-language samples across catalog endpoints.