Authentication
All API requests require authentication via a Bearer token in theAuthorization header. The one exception is the thumbnail of a public scene (GET /v1/scenes/{id}/thumbnail), which needs no key.
API keys
API keys use thes3d_ prefix and are passed as Bearer tokens:
Creating a key
- Sign in to the Splat3D dashboard
- Go to Settings > API Keys
- Click Create Key and give it a name
- Copy the key immediately — it’s only shown once
Scopes
A key carries the scopes picked when it was created, All by default. The API checks them on every request (requireScope in api/src/middleware/scope.ts):
GET /v1/usage takes any key. A key without the scope an endpoint needs gets 403 insufficient_scope, before anything is read, changed or charged:
Key format
The web dashboard generates keys as
s3d_<32 hex chars>
(POST in web/src/app/api/internal/keys/route.ts) and stores only the
SHA-256 hash. The API authenticator validates the s3d_ prefix and
looks up the SHA-256 hash; there is no length or entropy check at the
edge beyond the prefix (requireApiKeyAuth in api/src/middleware/auth.ts).
Key management
API keys are managed exclusively from the web dashboard at https://splat-3d.com/dashboard/settings. There is no public API for listing, creating, or revoking keys — this is by design to keep the auth perimeter simple. You can have up to 5 active API keys per account (MAX_ACTIVE_KEYS in web/src/app/api/internal/keys/route.ts).
Rate limits
A request over a per-minute limit answers
429 rate_limited with Retry-After: 60. It is refused before it reads or changes any scene, quota or credit balance: nothing is charged and no scene is made. Wait Retry-After seconds, then send the same request again. Polling at the recommended 5 to 10 seconds stays well under the limit: 5 scenes polled every 5 seconds is 60 requests a minute.
/v1 response carries X-RateLimit-Limit, the requests a minute its route allows: 30 on create, /process and /retrain, 300 on the rest. A 429 rate_limited names the limit that refused it, there and in meta.limit. There is no X-RateLimit-Remaining: the counter behind the limits only says whether a request is over.
The limits are counted per Cloudflare location and approximately: a fast
burst can pass one several times over before its first
429, and a
refusal can clear in under a minute. Don’t plan against the exact
numbers. They stop runaway loops and stolen keys; the monthly quotas and
credits below do the metering.POST /v1/scenes/{id}/process (and /retrain) returns 429 queue_full with Retry-After: 120. Nothing is charged and the scene stays uploading: call /process again once one finishes. A refused /retrain keeps no new version, unless the response carries no Retry-After: then check GET /v1/scenes before sending it again. Creating and uploading a scene is never refused for this.
402, 413, 422 or 429) keeps no new scene, so it counts nothing. One whose launch is charged and then fails (502) keeps its new scene, which counts.
When you hit a monthly quota, the API returns 429 quota_exceeded:
Error responses
All authentication errors follow the standard error envelope:Best practices
- One key per environment — separate keys for development, staging, and production
- Rotate regularly — revoke and replace keys periodically
- Use descriptive names — name keys after their purpose (e.g., “production-backend”, “ci-pipeline”)
- Monitor usage — the dashboard shows the
last_used_attimestamp for each key so you can spot unused ones and revoke them