Developers
Work with Outworld Open Assets from code, scripts and AI agents. Everything here is free, needs no key, and returns the same data as the site. Assets are licensed CC BY 4.0: use them for anything, including commercial work, and credit the author.
REST API · Examples · Python client · MCP server · Claude skill · Rate limits · Licence and attribution · How to cite
REST API
Base URL: https://outworld-open-assets.pages.dev/api/v1. JSON over HTTPS, CORS open, no authentication for reads.
The full contract is the OpenAPI 3.1 document at /api/v1/openapi.json
(load it into Swagger UI, Postman or a code generator).
| Method and path | What it returns |
|---|---|
GET /search |
Search with q, semantic=true, filters and paging (below). GET /assets is the same without a query. |
GET /assets/{id} |
The asset's full metadata.json (dimensions, mass, friction, joints, validation, provenance, licence) plus links. |
GET /assets/{id}/downloads |
Absolute URLs for the zip, USD, metadata.json, LICENSE.txt and previews; every file's size and sha256; the attribution text. |
GET /categories |
Categories and subcategories with counts. |
GET /stats |
Totals by physics tier, category and contributor. |
POST /submissions |
Start a contribution: returns a presigned upload URL (then PUT the zip, then POST /submissions/{id}/complete). |
GET /submissions/{id} |
Status and validation report of a contribution. |
Search parameters:
| Parameter | Meaning |
|---|---|
q |
Free text. Keyword match on name, label, category, id and description. |
semantic |
true embeds q (Workers AI, bge-base-en-v1.5) and ranks by similarity; the response says "semantic": true when the vector index was used. |
category, subcategory |
Exact match, see /categories. |
mechanism |
rigid, articulated (real joints with limits) or deformable (cloth and soft-body tier). |
articulated |
true or false. |
size |
Longest side: small < 0.1 m, medium 0.1 to 0.3 m, large 0.3 to 1 m, xlarge 1 m or more. Or size_min / size_max in metres. |
mass_min, mass_max |
Kilograms. |
contributor |
Contributor name, for example Outworld Labs. |
sort |
category (default), name, newest, mass, size; semantic results default to relevance. |
page, per_page |
1-based page; up to 100 per page (default 20). |
Each result has id, name, description, category, mechanism, dimensions_m (x, y, z in metres, Z up),
mass_kg, thumbnail_url, zip_url, page_url and api_url. Errors come back as
{"error": {"status": 404, "message": "Asset not found"}}.
Files are served from /files/assets/{id}/... with Range and ETag support, and the whole published catalogue
is at /files/catalog/catalog.json (and .csv) for bulk use.
The API is versioned in the path. Within v1 we only add fields; clients should ignore keys they do not know.
Examples
Every example below was run against the live API on (not yet run).
curl
BASE=https://outworld-open-assets.pages.dev/api/v1
# Keyword search with filters
curl -s "$BASE/search?q=mug&mechanism=rigid&per_page=5"
# Semantic search (describe what you need)
curl -s "$BASE/search?q=something+to+pour+hot+water+from&semantic=true&per_page=5"
# One asset's metadata, then its download links
curl -s "$BASE/assets/kitchenware_mug_a01"
curl -s "$BASE/assets/kitchenware_mug_a01/downloads"
# Download and unpack the zip
curl -sLO "https://outworld-open-assets.pages.dev/files/assets/kitchenware_mug_a01/kitchenware_mug_a01.zip"
unzip -q kitchenware_mug_a01.zip
# Categories and totals
curl -s "$BASE/categories"
curl -s "$BASE/stats"
Python (requests)
import requests
BASE = "https://outworld-open-assets.pages.dev/api/v1"
r = requests.get(f"{BASE}/search", params={"q": "cabinet with doors", "semantic": "true",
"mechanism": "articulated", "per_page": 5}, timeout=30)
r.raise_for_status()
for a in r.json()["items"]:
print(a["id"], a["name"], a["dimensions_m"], a["mass_kg"])
asset_id = r.json()["items"][0]["id"]
meta = requests.get(f"{BASE}/assets/{asset_id}", timeout=30).json()
print([(j["name"], j["type"], j["range"]) for j in meta.get("joints", [])])
links = requests.get(f"{BASE}/assets/{asset_id}/downloads", timeout=30).json()
print(links["zip"]["url"], links["zip"]["bytes"])
print(links["attribution_text"])
JavaScript (fetch)
// Node 18+ or any browser (CORS is open for the API).
const BASE = "https://outworld-open-assets.pages.dev/api/v1";
const params = new URLSearchParams({ q: "kitchen knife", semantic: "true", per_page: "5" });
const res = await fetch(`${BASE}/search?${params}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const { total, items } = await res.json();
console.log(`${total} matches`);
for (const a of items) console.log(a.id, a.name, a.mechanism, a.dimensions_m);
const links = await (await fetch(`${BASE}/assets/${items[0].id}/downloads`)).json();
console.log(links.zip.url, links.attribution_text);
Python client
outworld_assets.py is a single file with no dependencies beyond the standard library
(Python 3.8+). It retries on rate limits and server errors, honours Retry-After, verifies checksums and unpacks
each asset to the standard layout.
curl -sO https://outworld-open-assets.pages.dev/developers/outworld_assets.py
python outworld_assets.py search "cabinet with doors" --semantic --mechanism articulated
python outworld_assets.py download kitchenware_mug_a01 --dest assets/
import outworld_assets as oa # https://outworld-open-assets.pages.dev/developers/outworld_assets.py
hits = oa.search("ceramic mug", semantic=True, mechanism="rigid", per_page=3)
for a in hits["items"]:
print(a["id"], a["name"], a["mass_kg"], "kg")
folder = oa.download(hits["items"][0]["id"], "assets/")
print(folder) # assets/<id>/ with simready_usd/sm_<id>_01.usd, metadata.json, LICENSE.txt
print(oa.stats()["assets"], "assets in the archive")
Layout after download:
assets/<id>/
simready_usd/sm_<id>_01.usd OpenUSD, Z up, metres, kilograms
simready_usd/textures/...
simready_usd/.thumbs/256x256/...
metadata.json
LICENSE.txt keep this file with the asset
MCP server
A remote Model Context Protocol server lets AI assistants search the archive and fetch download links. Endpoint (Streamable HTTP, read-only, no sign-in):
https://outworld-open-assets.pages.dev/mcp
Tools: search_assets, get_asset, get_download_urls, list_categories, get_submission_status.
Resources: outworld-assets://assets/{id} (an asset's metadata) and outworld-assets://stats.
Claude Code
claude mcp add --transport http outworld-assets https://outworld-open-assets.pages.dev/mcp
Then ask, for example: "Find three small rigid kitchen objects in Outworld Open Assets and give me their download links."
Claude Desktop (and claude.ai): Settings, Connectors, Add custom connector, name Outworld Open Assets,
URL https://outworld-open-assets.pages.dev/mcp. On plans without custom connectors, add it through the mcp-remote bridge in
claude_desktop_config.json (needs Node.js), then restart Claude Desktop:
{
"mcpServers": {
"outworld-assets": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://outworld-open-assets.pages.dev/mcp"]
}
}
}
Cursor: add to ~/.cursor/mcp.json (or .cursor/mcp.json in a project):
{
"mcpServers": {
"outworld-assets": { "url": "https://outworld-open-assets.pages.dev/mcp" }
}
}
Any other MCP client that speaks Streamable HTTP works with the same URL. The server is stateless and answers with JSON; it allows 60 requests per minute per IP.
Claude skill
outworld-open-assets-skill.zip is a skill for Claude Code (and other
agents that read SKILL.md). It teaches the agent to find assets by description, download and unpack them, and load
them into Isaac Sim (USD reference), MuJoCo (a USD to MJCF converter, CPU) and Newton, with the physics notes:
Z up, metres, mass and inertia, articulations and the deformable tier. It also has a CPU quick-look renderer and
a MuJoCo drop and joint-sweep check.
Install:
mkdir -p ~/.claude/skills
curl -sLO https://outworld-open-assets.pages.dev/developers/outworld-open-assets-skill.zip
unzip -o outworld-open-assets-skill.zip -d ~/.claude/skills/
pip install usd-core mujoco numpy pillow # for the converter, checks and renders
Claude Code picks it up in the next session (~/.claude/skills/outworld-open-assets/SKILL.md); for one project only,
unzip into .claude/skills/ in the project instead. Scripts in the skill:
| Script | Does |
|---|---|
outworld_assets.py |
search, inspect and download (the Python client above) |
usd_to_mjcf.py |
SimReady USD to MuJoCo MJCF: bodies, mass and inertia, primitive and convex collision, hinge and slide joints with limits, textured visuals |
mujoco_check.py |
drop-and-settle test and joint sweep in MuJoCo on CPU, JSON report |
isaacsim_load.py |
reference the asset into Isaac Sim on a ground plane and simulate |
newton_load.py |
load the USD into Newton (ModelBuilder.add_usd) and drop it, CPU or GPU |
quicklook.py |
four-view contact sheet with size and mass, rendered on CPU |
Rate limits
Per IP address, in fixed one-minute windows:
| Endpoint | Limit |
|---|---|
API reads (GET /api/v1/...) |
120 requests per minute |
Semantic search (semantic=true) |
30 requests per minute |
API writes (POST) |
10 per minute; new submissions 5 per hour and 20 per day |
MCP server (/mcp) |
60 requests per minute |
File downloads (/files/...) |
no fixed limit; please cache and use the catalogue for bulk work |
Responses carry x-ratelimit-limit and x-ratelimit-remaining. Over the limit you get HTTP 429 with a
Retry-After header in seconds. Counters are kept per Cloudflare edge location, so treat the numbers as approximate.
For a bulk mirror, download catalog.json once and fetch the zips at a
steady pace instead of querying the API for every asset.
Licence and attribution
Every asset is licensed Creative Commons Attribution 4.0 International (CC BY 4.0). You may copy, modify, redistribute and use the assets for any purpose, including commercial products, training data and papers, as long as you give credit:
- keep the
LICENSE.txtthat ships with each asset when you redistribute the files (modified or not), and say if you changed them; - in a paper, video, dataset card or product credits, name the asset, the author and the licence, for example:
"Ceramic mug, matte sage" by Outworld Labs (outworldlabs.com), from Outworld Open Assets, CC BY 4.0.Theattribution_textfield of/api/v1/assets/{id}/downloadsgives the exact line for each asset; contributed assets name their contributor; - for many assets at once, one line is enough: "Contains N assets from Outworld Open Assets (https://outworld-open-assets.pages.dev), CC BY 4.0, by Outworld Labs and contributors; see the per-asset LICENSE.txt files."
Do not imply that Outworld Labs endorses your work. The API itself is free to use; please respect the rate limits.
How to cite
@misc{outworld_open_assets_2026,
title = {Outworld Open Assets: free SimReady 3D objects for robot simulation},
author = {{Outworld Labs}},
year = {2026},
howpublished = {\url{ https://outworld-open-assets.pages.dev }},
note = {OpenUSD assets validated against NVIDIA SimReady Robotics-Prop 4.0.0; CC BY 4.0. Accessed YYYY-MM-DD}
}
Plain text: Outworld Labs (2026). Outworld Open Assets: free SimReady 3D objects for robot simulation. https://outworld-open-assets.pages.dev. CC BY 4.0.
When you use specific assets, list their ids (for example in an appendix) so others can fetch the same files;
each asset's version field tells you which revision you used.
Questions or a bug in the API: write to Outworld Labs via outworldlabs.com.