Developer Image API · v1

Authentication

Every catalog request to the Epochal Stock Developer Image API must carry a secret API key in the Authorization header using the Bearer scheme. Keys are shown once at creation, stored as keyed hashes server-side, and must never appear in query strings, browser bundles, or public clients.

Overview

Catalog endpoints under https://epochalstock.com/api/v1 require authentication. Send your key on every request alongside an explicit JSON accept header. Keys are prefixed es_test_ (free trial, issued instantly by POST /trial/signup) or es_live_ (paid) — both use the same header and endpoints:

Authorization: Bearer es_live_…your_secret…
Accept: application/json

Bearer only

Keys must travel in the Authorization header. Query-string keys are rejected with query_key_rejected.

Shown once

The full secret is displayed a single time when the key is created. Epochal Stock stores a keyed hash, not the original secret.

Traceable

Every response includes X-Request-ID. Keep that value when reporting problems or correlating logs.

Up to 20 keys

Create up to 20 named keys per account. Revoke any key immediately through the management API when a project is compromised.

Why Bearer-only

Passing secrets as query parameters is convenient and wrong. URLs leak into browser history, reverse proxies, CDN access logs, analytics beacons, crash reports, and support screenshots. Bearer headers stay out of the request line and are far less likely to be retained by intermediate systems that log full URLs.

The API therefore rejects patterns such as ?api_key=… or ?key=… and returns query_key_rejected (HTTP 400). Always place the secret in the header:

  • Use Authorization: Bearer <key> on every authenticated call.
  • Send Accept: application/json so error bodies stay machine-readable.
  • Load the key from an environment variable or secrets manager—never hard-code it.
  • Call the API only from a trusted server. Public browsers must not hold live keys.

CORS is not supported for browser key use. Requests that look like browser-origin catalog calls with a key are rejected with cors_not_supported (HTTP 403). Your design app’s backend should proxy only the fields the client needs.

Request flow

How an authenticated catalog request is processed from client to handler.

Failed validation never reaches catalog handlers. Invalid or revoked keys return invalid_api_key; inactive billing returns subscription errors; browser/CORS misuse returns cors_not_supported. Successful responses always include X-Request-ID and rate-limit headers.

Header examples

The snippets below show the minimum headers for a simple GET https://epochalstock.com/api/v1/images call. Replace the environment variable with your secret store of choice. Never commit real keys.

cURL
curl -sS "https://epochalstock.com/api/v1/images?limit=5" \
  -H "Authorization: Bearer $EPOCHAL_API_KEY" \
  -H "Accept: application/json" \
  -D -

Tip: Log X-Request-ID on both success and failure paths. Support can locate the exact request without you pasting secrets or full response bodies.

Key lifecycle

Each account may hold up to 20 named keys. Create a separate key per website, backend, or environment so you can revoke one project without rotating everything. All keys on an account share the same rate limit and concurrent-download lease.

  1. Create — After an active subscription, call the management API (POST /keys) with a human-readable name such as design-app-prod. Copy the secret immediately; it is not shown again.
  2. Name & store — Save the secret in a vault or environment variable scoped to that deployment. Document which service owns the key.
  3. Use — Attach the Bearer header from server-side code only. Monitor usage per day via the portal usage endpoint.
  4. Rotate — Create a new named key, deploy it, confirm traffic, then revoke the old key. Do not reuse compromised secrets.
  5. RevokeDELETE /keys/{key_id} invalidates a key immediately. In-flight clients will start receiving invalid_api_key.
Limit

20 named keys

Creating more than the account allows returns key_limit_reached (HTTP 409). Revoke unused keys first.

Hash storage

No secret recovery

Epochal Stock stores a keyed hash of the secret. If you lose a key, create a new one and revoke the old ID if it is still listed.

Shared budget

One rate limit

Extra keys do not multiply the 30 requests/minute account budget. Unexpected multi-key traffic still consumes the shared limit.

Management calls (list/create/revoke keys, billing, usage) live behind the portal session and CSRF protection—not the Bearer catalog key. See Security & billing for portal flows.

Request ID troubleshooting

Every API response includes an X-Request-ID header (and often a matching request_id field inside error JSON). Treat it as the primary correlation token when something fails in production.

When to capture it

  • Any non-2xx catalog or download response.
  • Unexpected empty result sets after a filter change.
  • Intermittent 429, 502, or 503 responses.
  • Support tickets—include the ID, UTC timestamp, endpoint, and HTTP status (never the key).

Successful JSON bodies also carry meta.request_id. Prefer logging both the header and body ID when available so retries and multi-hop proxies stay aligned.

Do not paste API keys into tickets or chat. A Request-ID plus approximate time is enough for server-side lookup. Rotate any key that may have been exposed.

Common authentication errors

Auth-related failures return structured JSON with error.code, error.message, and error.request_id. The table below lists the codes you will see most often while wiring credentials.

HTTP Code Meaning What to do
401 invalid_api_key Missing, malformed, revoked, or unknown Bearer key. Confirm the env var, header spelling, and that the key was not revoked.
402 subscription_required No active paid plan on the account. Complete PayPal subscription activation before catalog calls.
402 subscription_past_due Billing failed or the subscription was suspended. Fix payment method; access returns after the account is active again.
403 cors_not_supported Browser-style cross-origin use of a secret key is blocked. Move the call to your backend and proxy only safe fields to the client.
400 query_key_rejected An API key was supplied in the query string. Remove ?api_key= (and similar) and use the Bearer header only.

For rate limits, validation errors, and the full error catalog, see Rate limits & errors.

Security rules

Never call authenticated endpoints from a public browser with a live key. Frontend bundles, mobile apps, browser extensions, and untrusted clients will leak secrets. Your server should validate user input and proxy only required catalog fields.

  • Store keys in a secrets manager or environment variables—never in git, screenshots, or chat.
  • Issue one named key per project or environment; revoke the smallest blast radius on compromise.
  • Reject putting keys in URLs, form fields, localStorage, or analytics payloads.
  • Set connect and read timeouts on every HTTP client.
  • Retry only 429, 502, and 503 with bounded exponential backoff and jitter.
  • Rotate keys periodically and immediately after any suspected exposure.
  • Treat download URLs as short-lived secrets; do not log them in analytics.