3DAssets.dev

REST API v1

Stable JSON over HTTPS. Reads are public and cached; writes need a Bearer API key.

Base URL https://3dassets.dev/api/v1. OpenAPI: /api/v1/openapi.json. Errors always look like { "error": { "code", "message", "details?" } }.

Read

No key, no account, no headers. Every route in this section answers an anonymous request, and does so on the same rate limits an account gets.

bash
# search
curl "https://3dassets.dev/api/v1/assets?q=bench&category=props&theme=fantasy&style=low-poly&sort=popular"

# one asset (includes cdnUrl, downloadUrl, license.attributionText, stats, usage snippets)
curl "https://3dassets.dev/api/v1/assets/park-bench"

# same thing as Markdown
curl "https://3dassets.dev/assets/park-bench.md"

# taxonomy
curl "https://3dassets.dev/api/v1/categories"   # also /tags /licenses /packs

Filters on GET /assets: q, category, tag, theme, style, contributor, featured=true, sort=relevance|newest|popular|title (best match when there is a q, newest otherwise), page, limit (≤100).

Accounts

bash
# 1. create (a 6-digit code is emailed)
curl -X POST https://3dassets.dev/api/v1/accounts -H 'content-type: application/json' \
  -d '{"email":"[email protected]","name":"Ada","username":"ada","source":"api"}'

# 2. verify → API key (shown once)
curl -X POST https://3dassets.dev/api/v1/accounts/verify -H 'content-type: application/json' \
  -d '{"email":"[email protected]","code":"123456"}'

# 3. check
curl https://3dassets.dev/api/v1/me -H 'Authorization: Bearer <apiKey>'

Accounts have no passwords

Signing up needs a username and an email, nothing else. Everything after that is a six-digit code we email, so there is never a credential for a user to paste into an agent session.

/accounts/login-code answers the same 200 whether or not the address has an account, so it cannot be used to discover who is registered.

Submit an asset

Two routes to the same place. Both end in processing (validation, re-encoding) and then a human review. Poll GET /assets/{slug} with your key to watch status move through processing → pending → published (or failed / rejected with a reason).

CC0 1.0 Universal is applied automatically. Set attestation: true only after the contributor accepts the irrevocable CC0 dedication and confirms their publishing rights. No licence or tags are needed in the request. If supplied, license must be cc0-1.0; other values receive a validation error. Missing or false acceptance is rejected. An API key alone does not grant permission to dedicate a model.

From a URL (simplest)

bash
curl -X POST https://3dassets.dev/api/v1/assets/from-url -H 'Authorization: Bearer <apiKey>' -H 'content-type: application/json' -d '{
  "url": "https://example.com/models/bench.glb",
  "title": "Park bench",
  "summary": "Low-poly wooden park bench, 1.8 m wide, PBR textures.",
  "category": "props",
  "attestation": true
}'

Direct upload

bash
# presign
curl -X POST https://3dassets.dev/api/v1/uploads/presign -H 'Authorization: Bearer <apiKey>' -H 'content-type: application/json' \
  -d '{"filename":"bench.glb","contentLength":1234567,"withPreview":false}'
# → { upload: { url, key, method: "PUT", headers } }

# upload the bytes straight to storage
curl -X PUT "<upload.url>" -H 'content-type: model/gltf-binary' --data-binary @bench.glb

# create the asset
curl -X POST https://3dassets.dev/api/v1/assets -H 'Authorization: Bearer <apiKey>' -H 'content-type: application/json' \
  -d '{"quarantineKey":"<upload.key>","title":"Park bench","summary":"…","category":"props","attestation":true}'

Edit or withdraw your own asset

PATCH /assets/{slug} changes the metadata of an asset you submitted: title, summary, description, category, tags, sourceUrl, aiGenerated, aiModel. The uploaded GLB is immutable: to change the model itself, submit a new asset.

GET /me/assets reports pendingEdit for anything you have waiting. DELETE /assets/{slug} withdraws the asset itself, permanently. Someone else’s asset answers 404 to all of these.

bash
curl -X PATCH https://3dassets.dev/api/v1/assets/park-bench -H 'Authorization: Bearer <apiKey>' -H 'content-type: application/json' \
  -d '{"summary":"Low-poly wooden park bench, 1.8 m wide, 2k PBR textures."}'

Rules every file must pass

Rejections come back with a machine-readable code (EXTERNAL_URI, LIMIT_EXCEEDED, VALIDATION_FAILED, …) and a human sentence you can show as-is.

Rate limits

Everything here is free, and the limits are set so that ordinary work never meets them: they exist so one caller cannot overload the site, not to ration it. Requests are counted in fixed windows against your API key, or against your IP address when you send none. The allowance is the same either way: a key authenticates your writes and gives you your own counter, it does not buy extra headroom.

WhatLimit
Search & read
GET /api/v1/*, /assets/{slug}.md, MCP read tools
10,000/min
Downloads
/download/{slug}
2,500/min
Submissions & edits
POST /api/v1/assets, /assets/from-url, /uploads/presign, /packs/submissions, /reports, MCP write tools
60/min
Accounts
POST /api/v1/accounts/verify, /accounts/session, key rotation
10/hour · 30/day
Account emails
POST /api/v1/accounts, /accounts/login-code, /accounts/resend
6/hour · 20/day

Account calls are always counted per IP address. Reads and downloads answer with RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (seconds); every 429 carries those plus Retry-After and the usual error body:

json
{ "error": { "code": "RATE_LIMITED", "message": "Rate limit exceeded for downloads (…). Retry in 42 seconds.",
  "details": { "bucket": "download", "retryAfterSeconds": 42, "limit": 30, "window": "minute" } } }

Other

AI model attribution

Set aiGenerated: true if AI helped create geometry, generating code or textures, and optionally include aiModel (up to 160 characters). Use GET /api/v1/ai-models?q=... or MCP list_ai_models for OpenRouter suggestions refreshed hourly. Custom model and 3D-tool names are accepted. Leave unknown models unspecified; never guess. Using an agent only to upload does not count as AI creation.

The asset info panel, JSON and Markdown show this as contributor-reported attribution. A model catalogue outage does not block uploading. Switching AI use off clears the model name.