Catalog traffic
Authorization: Bearer es_live_… on search, detail, batch, tags, stats, and
download-token minting. Keys are hashed at rest; the full secret is shown only once at creation.
Developer Image API · v1
Keep secret API keys and management sessions on your server. The catalog is available on a single $50 USD/month plan after PayPal reports the subscription ACTIVE. This page covers the operational security checklist, management endpoint map, billing states that block catalog access, and commercial license expectations.
Authenticated catalog calls and management cookies must never be exposed to public browser JavaScript. Your product UI talks to your backend; your backend holds the Bearer key (and, for portal operations, the management session).
Never put the API key in the browser
Authorization: Bearer es_live_… on search, detail, batch, tags, stats, and
download-token minting. Keys are hashed at rest; the full secret is shown only once at creation.
Portal register/login, keys, billing, and usage use a server-side management session plus CSRF. Do not expose management cookies or PayPal credentials to the browser.
Two-step flow: mint with the Bearer key, then transfer via a short-lived
download_url without the key. Treat that URL as a secret until expiry.
Public browsers are not supported clients for authenticated catalog endpoints
(cors_not_supported). Design for a same-origin or trusted backend proxy.
Follow this list for every production deployment that touches the Developer Image API.
Production security controls
429, 502, and
503 with bounded exponential backoff and jitter. Do not blindly retry other 4xx
(auth, validation, subscription) errors.
Do not embed live keys in frontend builds.
Public env prefixes (NEXT_PUBLIC_, VITE_, Expo public config), browser
extensions, mobile app binaries, and desktop renderer processes are all reverse-engineerable.
If a key may have shipped to clients, revoke it in the portal and create a new named key.
Do not put keys in URLs.
Query-string authentication (?api_key=…) is rejected with
query_key_rejected because URLs leak into browser history, proxies, CDN logs, and
analytics. Use the Authorization: Bearer header only.
Do not expose management sessions to JavaScript.
Portal cookies, CSRF tokens, and PayPal credentials belong on your server (or trusted BFF).
Browser code should never call /portal/*, /keys, or
/billing/* with credentials intended for Epochal management.
Do not log download URLs or Authorization headers into long-lived APM, error
trackers, or third-party analytics. Keep X-Request-ID for support instead.
401/402/422 in a tight loop—fix config or billing first.Developer Image API v1 has a free trial and one commercial plan:
| Plan | Price | Catalog | API limit | Original downloads | Projects / keys |
|---|---|---|---|---|---|
| Free Trial | $0 for 1 month | All image collections | 2 requests/minute · 2,000 total requests | Counted toward the request budget | 1 key (issued at signup) |
| Developer API | $50 USD / month — first month $40 (20% off) | All image collections | 30 requests/minute per account | Unlimited; one concurrent transfer | Up to 20 named keys |
POST /trial/signup: 2,000 requests, 2/minute, one trial per email); no per-image billing, credit balance, bundle pricing, or video access in v1.https://epochalstock.com/api/v1The developer website (or your own admin backend) should proxy these management calls and keep the Epochal management session server-side. Do not expose management cookies or PayPal credentials to browser JavaScript.
| Method | Path | Purpose |
|---|---|---|
GET |
/portal/csrf |
Start management session and obtain CSRF token. |
POST |
/portal/register |
Register name, company, email, and 12+ character password. |
POST |
/portal/login |
Log in with email/password. |
POST |
/portal/logout |
End session. |
GET |
/account |
Current account, plan, status, and CSRF token. |
GET / POST |
/keys |
List keys or create a named key (create only after active subscription). |
DELETE |
/keys/{key_id} |
Revoke a key immediately. |
POST |
/trial/signup |
Public: instant free-trial account plus es_test_ key (no session/CSRF). |
POST |
/billing/subscription |
Create the PayPal subscription and return approval URL. Portal session or Bearer API key (expired trials may still upgrade). First-time subscribers get the $40-first-month plan automatically; body {"plan":"regular"} forces full price. |
GET |
/billing/upgrade?t=… |
Signed-token one-click redirect (302) straight to PayPal checkout. |
POST |
/billing/upgrade-link |
Public: {email} → personalized upgrade link (used by the upgrade page's checkout form). |
GET |
/billing/status |
Current subscription state. |
POST |
/billing/cancel |
Stop renewal; catalog access remains until paid period end. |
GET |
/usage |
Daily per-endpoint request/error/download totals. |
GET /portal/csrf (or login flows), stores them securely, and attaches them on
subsequent portal calls. End users of your product should not receive Epochal management cookies.
X-CSRF-Token. Registration and login first call
/portal/csrf, retain the cookie, and send the token.
POST /api/v1/webhooks/paypal. Catalog access activates only after PayPal reports
the subscription ACTIVE. Webhook event IDs are idempotent.
A typical server-side management client follows this order. Paths below are relative to the API base
(https://epochalstock.com/api/v1) unless your deployment documents a separate portal host.
Management session bootstrap
1. GET /portal/csrf
→ Set-Cookie: management session
→ body/header includes CSRF token
2. POST /portal/register (or /portal/login)
Headers:
Cookie: <session>
X-CSRF-Token: <token>
Content-Type: application/json
Body (register): name, company, email, password (12+ chars)
3. POST /billing/subscription
Headers: Cookie + X-CSRF-Token
→ returns PayPal approval URL for the $50/month plan
4. User approves in PayPal → webhook ACTIVE → account can create keys
5. POST /keys { "name": "design-app-prod" }
→ secret shown once; store in secrets manager as EPOCHAL_API_KEY
6. Catalog traffic uses Bearer key only (no management cookie required)
# Illustrative only — cookie jar kept on your server, never in a browser SPA
BASE="https://epochalstock.com/api/v1"
COOKIE_JAR="$(mktemp)"
curl -sS -c "$COOKIE_JAR" -b "$COOKIE_JAR" "$BASE/portal/csrf"
# Parse CSRF token from response, then:
curl -sS -c "$COOKIE_JAR" -b "$COOKIE_JAR" -X POST "$BASE/portal/login" \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: $CSRF" \
-d '{"email":"you@example.com","password":"your-long-password"}'
curl -sS -c "$COOKIE_JAR" -b "$COOKIE_JAR" "$BASE/account" \
-H "X-CSRF-Token: $CSRF"
rm -f "$COOKIE_JAR"
Invalid CSRF returns HTTP 403 with invalid_csrf. Login abuse can return
429 login_rate_limited. See
Rate limits & errors
for the full code table.
Billing is webhook-driven. Approving PayPal is necessary but not sufficient until the platform records an ACTIVE subscription from a verified webhook event.
| State / signal | Catalog API | What to do |
|---|---|---|
| No subscription / not yet ACTIVE | Blocked — typically HTTP 402 subscription_required |
Complete PayPal approval; wait for ACTIVE webhook; then create keys. |
| ACTIVE | Allowed with a valid Bearer key | Normal operation; monitor usage and rate-limit headers. |
| past_due (payment failure / suspension) | Blocks catalog access — 402 subscription_past_due |
Resolve payment with PayPal/billing; restore ACTIVE before retrying catalog traffic. |
Cancel requested (POST /billing/cancel) |
Access remains until the paid period ends | Stop renewal; plan offboarding; keys stop working after the paid boundary. |
past_due blocks the catalog.
Payment failure or suspension changes the account to past_due and blocks catalog
access until the subscription is healthy again. Design your product UX to surface billing recovery
rather than infinite API retries on 402.
POST /billing/cancel stops renewal; access continues through the already-paid period.409 subscription_exists.409 key_limit_reached.The commercial license at epochalstock.com/license applies to API usage and licensed images.
Every response includes X-Request-ID (and usually meta.request_id or
error.request_id). Log it on success and failure; include it in support tickets—never the API key.
Management endpoint for daily per-endpoint request, error, and download totals. Poll from your backend admin tools, not from public clients.
Unexpected traffic on any key still burns the account’s 30 req/min and the single download lease.
Alert when X-RateLimit-Remaining stays near zero.
Create a new named key, deploy it, then DELETE /keys/{id} for the old key.
Rotate immediately after suspected exposure (git push, screenshot, support paste, client bundle).
Download URL secrecy: mint tokens only on trusted servers; prefer streaming originals through your backend when end users must not see signed URLs. Full flow: Downloads.
Bearer keys, lifecycle, and security rules for catalog traffic.
Two-step originals, token expiry, and concurrency lease.
30/min headers, 402 billing errors, and retry policy.
Multi-language server samples for search through downloads.
Register, subscribe $50/mo, store key, first search.
API capabilities and documentation map.