Build 3D rendering into your product with the DX.GL pipeline
Your users need product videos, turntable animations, or multi-view training datasets — but building a GPU rendering pipeline is a multi-year engineering effort. DX.GL handles the hard parts: model processing, GPU scheduling, shadow accumulation, video encoding, and asset delivery. You focus on your product.
What this means in practice:
Whether you're building an e-commerce platform, a 3D marketplace, a research data pipeline, or a content management system — the same API powers all of these.
You use a single API key (dxgl_sk_...) created in the Portal → API Keys. All renders, uploads, and downloads are billed to your account. You handle user mapping and billing on your side however you want.
| Path | Best for | How it works |
|---|---|---|
| REST API | Production pipelines, backend services | Direct HTTP calls from your server. Deterministic, testable, fully scriptable. |
| MCP (Model Context Protocol) | Prototyping, ad-hoc tasks, AI-powered workflows | AI agents call DX.GL tools via natural language. Great for internal tooling and rapid iteration. |
Both paths use the same underlying API and the same credit system. Many integrators start with MCP for prototyping and add direct REST calls for production.
Credits are deducted per render based on quality tier:
| Quality | Output | Credits |
|---|---|---|
standard |
1080p H.264 MP4 | 1 |
4k |
4K H.264 MP4 | 4 |
pro |
4K ProRes 4444 with alpha | 16 |
dataset 100×800 |
100-view training ZIP | 1 |
dataset 196×1024 |
196-view training ZIP | 4 |
dataset 400×2048 |
400-view training ZIP | 16 |
Use the quote endpoint to check costs before committing. Batch renders deduct atomically — if you don't have enough credits, nothing is charged.
Base URL: https://api.dx.gl/v1
All requests require Authorization: Bearer dxgl_sk_... in the header.
curl -X POST https://api.dx.gl/v1/models \
-H "Authorization: Bearer dxgl_sk_..." \
-F "[email protected]" \
-F 'renderSettings={"aspect":"16:9","bgColor":"#ffffff","length":6}'
Response:
{ "data": { "modelId": "Ab3kF9x2qL1m", "renderId": "Xz7pQ4w8nR2k" } }
The upload triggers a free system preview render automatically. If you pass renderSettings, a paid render is also queued.
curl -X POST https://api.dx.gl/v1/models/ingest \
-H "Authorization: Bearer dxgl_sk_..." \
-H "Content-Type: application/json" \
-d '{"url": "https://your-cdn.com/model.glb", "renderSettings": {"aspect":"16:9"}}'
Same response format. The server downloads the file directly (max 100 MB, 2-minute timeout). SHA-256 deduplication prevents re-uploading identical files.
curl -X POST https://api.dx.gl/v1/renders \
-H "Authorization: Bearer dxgl_sk_..." \
-H "Content-Type: application/json" \
-d '{
"modelId": "Ab3kF9x2qL1m",
"renderSettings": {
"quality": "standard",
"aspect": "1:1",
"bgColor": "#000000",
"length": 9,
"shadows": true,
"easing": true
}
}'
curl -X POST https://api.dx.gl/v1/renders/batch \
-H "Authorization: Bearer dxgl_sk_..." \
-H "Content-Type: application/json" \
-d '{
"renders": [
{ "modelId": "Ab3kF9x2qL1m", "renderSettings": { "aspect": "16:9", "bgColor": "#ffffff" } },
{ "modelId": "Ab3kF9x2qL1m", "renderSettings": { "aspect": "1:1", "bgColor": "#000000" } },
{ "modelId": "Ab3kF9x2qL1m", "renderSettings": { "aspect": "9:16", "bgColor": "#ffffff" } }
]
}'
Maximum 100 renders per batch. Credits deducted atomically. Renders for the same model share GPU frames — encoding multiple variants from one render pass.
curl https://api.dx.gl/v1/renders/Xz7pQ4w8nR2k \
-H "Authorization: Bearer dxgl_sk_..."
Status progression: pending → poster-processing → poster-done → video-processing → done
The poster is available at poster-done — you can show it to users immediately while the video finishes encoding.
# Full video
curl -o video.mp4 https://api.dx.gl/v1/renders/Xz7pQ4w8nR2k/video \
-H "Authorization: Bearer dxgl_sk_..."
# Web-optimized variant (lower bitrate, faster loading)
curl -o web.mp4 "https://api.dx.gl/v1/renders/Xz7pQ4w8nR2k/video?quality=web" \
-H "Authorization: Bearer dxgl_sk_..."
# Poster image (full resolution)
curl -o poster.png "https://api.dx.gl/v1/renders/Xz7pQ4w8nR2k/poster?quality=full" \
-H "Authorization: Bearer dxgl_sk_..."
# All assets as ZIP
curl -o assets.zip https://api.dx.gl/v1/renders/Xz7pQ4w8nR2k/bundle \
-H "Authorization: Bearer dxgl_sk_..."
All asset endpoints return Cache-Control: immutable — renders never change after completion. Safe to cache aggressively.
import requests, time
API = "https://api.dx.gl/v1"
HEADERS = {"Authorization": "Bearer dxgl_sk_..."}
def render_model(glb_path, settings):
"""Upload a model, render it, return the video URL."""
with open(glb_path, "rb") as f:
r = requests.post(f"{API}/models", headers=HEADERS,
files={"file": f},
data={"renderSettings": json.dumps(settings)})
data = r.json()["data"]
render_id = data["renderId"]
# Poll until done
while True:
r = requests.get(f"{API}/renders/{render_id}", headers=HEADERS)
status = r.json()["data"]["status"]
if status == "done":
return f"{API}/renders/{render_id}/video"
if status == "error":
raise Exception(f"Render failed: {r.json()['data'].get('errorMessage')}")
time.sleep(3)
# Usage
url = render_model("chair.glb", {"aspect": "16:9", "bgColor": "#ffffff", "length": 6})
const API = 'https://api.dx.gl/v1';
const headers = { 'Authorization': 'Bearer dxgl_sk_...' };
async function renderModel(filePath, settings) {
const form = new FormData();
form.append('file', fs.createReadStream(filePath));
form.append('renderSettings', JSON.stringify(settings));
const upload = await fetch(`${API}/models`, { method: 'POST', headers, body: form });
const { renderId } = (await upload.json()).data;
// Poll until done
while (true) {
await new Promise(r => setTimeout(r, 3000));
const res = await fetch(`${API}/renders/${renderId}`, { headers });
const { status, errorMessage } = (await res.json()).data;
if (status === 'done') return `${API}/renders/${renderId}/video`;
if (status === 'error') throw new Error(`Render failed: ${errorMessage}`);
}
}
The MCP server lets AI agents interact with DX.GL through tool calls. Add this to your MCP client configuration (Windsurf, Claude Desktop, Cursor, or any compatible host):
{
"mcpServers": {
"dxgl": {
"serverUrl": "https://mcp.dx.gl/",
"headers": {
"Authorization": "Bearer dxgl_sk_..."
}
}
}
}
| Tool | Description |
|---|---|
ingest_model |
Import a 3D model from a URL |
list_models |
List models with tag and pagination filters |
get_model |
Get model details and all its renders |
create_render |
Create a single render (video or dataset) |
create_batch_renders |
Create multiple renders atomically (max 100) |
get_render |
Check render status (poll until done) |
download_render |
Get download URL for a completed render |
get_account |
Check credit balance |
quote |
Estimate credit cost before committing |
Catalog onboarding:
Import all GLB files from these URLs, tag them "client-acme", then render each in 16:9 white and 1:1 black with shadows. Quote the total first.
Variant expansion:
For all models tagged "client-acme", create 9:16 renders with the branded gradient background (overlay ID abc123DEF456). Skip any that already have a 9:16 render.
Dataset generation:
Generate 196×1024 hemisphere datasets for all models tagged "training-batch-7". Quote first, then proceed.
Status check:
How many credits do I have left? List all pending renders and their status.
| Use MCP when... | Use REST when... |
|---|---|
| Prototyping a new integration | Running in production |
| Ad-hoc batch operations | Automated backend pipeline |
| Internal tooling for ops | User-facing render triggers |
| Exploring the API interactively | Deterministic, testable code |
Many teams use both: MCP for operations and internal tooling, REST for the production path.
Upload new SKUs nightly from your product database. Tag each upload with a client or campaign identifier for easy filtering.
for sku in new_skus:
r = requests.post(f"{API}/models/ingest", headers=HEADERS, json={
"url": sku["glb_url"],
"renderSettings": {"aspect": "16:9", "bgColor": "#ffffff", "length": 6}
})
model_id = r.json()["data"]["modelId"]
# Tag the model
requests.patch(f"{API}/models/{model_id}", headers=HEADERS, json={
"title": sku["name"],
"sku": sku["sku_code"],
"tags": ["client-acme", "fall-2026"]
})
User uploads a model through your UI → your backend forwards it to DX.GL → polls for completion → delivers the video.
User → Your Frontend → Your Backend → DX.GL API → Poll → Deliver video URL
The poster is available at poster-done status — show it immediately as a preview while the video finishes.
Generate every combination of aspect ratio, background, and quality for each model:
variants = [
{"aspect": "16:9", "bgColor": "#ffffff", "shadows": True},
{"aspect": "1:1", "bgColor": "#000000", "shadows": True},
{"aspect": "9:16", "bgColor": "#ffffff", "reflector": True},
]
renders = []
for model_id in model_ids:
for v in variants:
renders.append({"modelId": model_id, "renderSettings": v})
# Submit all at once (max 100 per batch)
for batch in chunks(renders, 100):
r = requests.post(f"{API}/renders/batch", headers=HEADERS, json={"renders": batch})
Upload your client's branded background once, then reference it across all renders:
# Upload the background image
curl -X POST https://api.dx.gl/v1/overlays \
-H "Authorization: Bearer dxgl_sk_..." \
-F "[email protected]" \
-F "layer=background" \
-F "name=Acme Brand Gradient"
# Use it in renders
curl -X POST https://api.dx.gl/v1/renders \
-H "Authorization: Bearer dxgl_sk_..." \
-H "Content-Type: application/json" \
-d '{"modelId": "...", "renderSettings": {"bgImageId": "overlay_id_here", "aspect": "16:9"}}'
The image is scaled to cover the output dimensions and cropped from the top-left anchor, keeping logos safe across all aspect ratios.
Tags are the key to organizing models across clients, projects, and campaigns. Every model can have up to 20 tags (lowercased, trimmed).
# List all models for a specific client
curl "https://api.dx.gl/v1/models?tags=client-acme&limit=100" \
-H "Authorization: Bearer dxgl_sk_..."
# List models matching any of multiple tags (OR filter)
curl "https://api.dx.gl/v1/models?tags=client-acme,campaign-fall-2026" \
-H "Authorization: Bearer dxgl_sk_..."
Tag every model with the client name. To calculate spend per client, list their models, count their renders, multiply by credit cost per quality tier. The quote endpoint can help estimate future costs.
curl https://api.dx.gl/v1/account \
-H "Authorization: Bearer dxgl_sk_..."
# Response: { "data": { "credits": 847 } }
curl -X POST https://api.dx.gl/v1/quote \
-H "Authorization: Bearer dxgl_sk_..." \
-H "Content-Type: application/json" \
-d '{"renders": [{"quality": "standard"}, {"quality": "standard"}, {"quality": "4k"}]}'
# Response: { "data": { "creditsRequired": 6, "creditsAvailable": 847, "sufficient": true } }
Always quote large batches before submitting. The quote is free and returns per-item breakdown, total cost, and whether you have enough credits.
Build a simple dashboard that checks GET /v1/account periodically and alerts when credits drop below a threshold. Contact us for volume pricing at scale.
Every completed render has a public share page at https://dx.gl/portal/v/{renderId}. No authentication required for viewing. Useful for sharing with stakeholders or embedding in emails.
All asset endpoints support Range headers for streaming and return Cache-Control: immutable. Assets per render:
| Endpoint | Format | Use case |
|---|---|---|
GET /v1/renders/:id/video |
MP4 or MOV (Pro) | Full-quality download |
GET /v1/renders/:id/video?quality=web |
MP4 (lower bitrate) | Web embedding, streaming |
GET /v1/renders/:id/poster?quality=full |
PNG | Product page hero, social sharing |
GET /v1/renders/:id/poster |
PNG (thumbnail) | Grid previews, hover states |
GET /v1/renders/:id/thumb |
MP4 (quarter res) | Hover video preview |
GET /v1/renders/:id/bundle |
ZIP | Bulk download of all assets |
The web video variant is optimized for <video> tags:
<video autoplay loop muted playsinline>
<source src="https://api.dx.gl/v1/renders/{id}/video?quality=web" type="video/mp4">
</video>
| Field | Type | Default | Description |
|---|---|---|---|
quality |
string | "standard" |
"standard" (1080p), "4k" (4K), "pro" (4K ProRes with alpha) |
aspect |
string | "16:9" |
"16:9", "1:1", "9:16" |
bgColor |
string | "#ffffff" |
Any 6-digit hex (ignored when bgImageId is set) |
bgImageId |
string | — | Overlay asset ID for background image |
length |
integer | 6 |
Duration in seconds (3–30) |
shadows |
boolean | true |
Ground shadows |
reflector |
boolean | false |
Reflective ground plane |
easing |
boolean | true |
Smooth rotation start/stop |
rotateY |
integer | 0 |
Starting angle (−180° to 180°) |
tiltX |
integer | 0 |
Forward/back tilt (−90° to 90°) |
effect |
string | "turntable" |
Camera motion: "turntable", "hero-spin", "showcase", "zoom-orbit", "reveal" |
output |
string | "video" |
"video" or "dataset" |
datasetQuality |
string | — | "100x800", "196x1024", "400x2048" (when output is dataset) |
coverage |
string | "hemisphere" |
"hemisphere" or "sphere" (dataset only) |
All errors return a consistent format:
{
"error": {
"code": "no_credits",
"message": "No render credits remaining",
"status": 402
}
}
Key error codes for integrators:
| Code | Status | Action |
|---|---|---|
unauthorized |
401 | Check API key |
no_credits |
402 | Purchase more credits or contact for volume pricing |
file_too_large |
400 | File exceeds 100 MB — reduce before upload |
invalid_format |
400 | Only GLB, glTF, and ZIP (OBJ+MTL) are supported |
model_not_found |
404 | Model was deleted or ID is wrong |
too_many |
400 | Batch exceeds 100 renders — split into smaller batches |
upload_limit |
429 | Free upload limit reached — purchase credits to unlock |