DX.GL

Developer Docs

Markdown API Reference

REST API and MCP for turntable videos and multi-view datasets

Contents
Authentication & API KeysREST Quick StartPython ExampleNode.js ExampleCommon PatternsCredit ManagementAsset DeliveryMCP for AI AgentsTiers & Render SettingsError HandlingFurther Reading

Authentication & API Keys

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.

The REST base URL is https://api.dx.gl/v1. Every request sends Authorization: Bearer dxgl_sk_... in the header.

Two Integration Paths

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.


REST Quick Start

Base URL: https://api.dx.gl/v1

All requests require Authorization: Bearer dxgl_sk_... in the header.

1. Upload a model

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.

2. Ingest from URL (no file upload)

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 1 GB, 2-minute timeout). SHA-256 deduplication prevents re-uploading identical files.

3. Create additional renders

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
    }
  }'

4. Batch render (multiple variants at once)

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.

5. Poll for completion

curl https://api.dx.gl/v1/renders/Xz7pQ4w8nR2k \
  -H "Authorization: Bearer dxgl_sk_..."

Status progression: pendingposter-processingvideo-processingdone (any status can move to error; poster-done is a rare recovery state, not part of the happy path — poll for done or error and treat the rest as opaque "in progress").

6. Download assets

# 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.


Python Example

import requests, time, json

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})

Node.js Example

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}`);
  }
}

Common Patterns

Catalog Sync

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"]
    })

Variant Matrix

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})

Background Image Branding

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 for Multi-Tenant Organization

Tags organize models across clients, projects, and campaigns. Every model can have up to 20 tags (lowercased, trimmed).

client-{name}per-client isolation (client-acme, client-widgets-inc) campaign-{name}per-campaign grouping (campaign-fall-2026) batch-{id}per-import-batch tracking status-{state}workflow state (status-pending-review, status-approved)
# 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_..."

To calculate spend per client, list their models, count their renders, and total each render's credit cost — video is billed per second by quality tier, datasets are flat per set. The quote endpoint can help estimate future costs.


Credit Management

Check Balance

curl https://api.dx.gl/v1/account \
  -H "Authorization: Bearer dxgl_sk_..."

# Response: { "data": { "credits": 847 } }

Quote Before Committing

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": 144, "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. Batch renders deduct atomically — if you don't have enough credits, nothing is charged.

Credit packs are $9.90, $79, and $490. 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.


Asset Delivery

Every completed render produces four assets: the full video, a web-optimized variant, a thumbnail, and a poster.

Public Share URLs

Every completed render has a public share page at https://dx.gl/portal/v?id={renderId}. No authentication required for viewing. Useful for sharing with stakeholders or embedding in emails.

Direct Download

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

Embedding

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>

MCP for AI Agents

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 MCP-compatible host):

{
  "mcpServers": {
    "dxgl": {
      "serverUrl": "https://mcp.dx.gl/",
      "headers": {
        "Authorization": "Bearer dxgl_sk_..."
      }
    }
  }
}

Replace dxgl_sk_... with your API key from the Portal → API Keys.

Your First Prompt

Paste this into your AI agent to verify everything works:

List my models and tell me how many I have.

The agent will call list_models and return a summary. If this works, you're connected.

Prompt Examples

Render a single model in multiple formats (video):

Render model Ab3kF9x2qL1m in all three aspect ratios (16:9, 1:1, 9:16) with a white background, 9 seconds, shadows enabled.

The agent calls create_batch_renders with 3 variants and polls until all complete.

Full catalog in multiple variants (video):

I just uploaded 30 shoe models tagged "ss26". Render each one in 16:9 white, 1:1 black, and 9:16 with shadows. Quote the total cost first.

The agent will:

  1. Call list_models with tags: "ss26" to find the 30 models
  2. Call quote with 90 renders (30 models × 3 variants) to show total cost
  3. Wait for your confirmation
  4. Call create_batch_renders to submit all 90 at once
  5. Credits are deducted atomically — if you don't have enough, nothing is charged
  6. Poll and return video URLs when complete

Generate a dataset for one model:

Generate a 196×1024 hemisphere dataset for model Ab3kF9x2qL1m.

The agent calls create_render with output: "dataset", datasetQuality: "196x1024", coverage: "hemisphere", then polls get_render until done, and returns the download URL.

Generate datasets for all models with a tag:

Generate 100×800 hemisphere datasets for every model tagged "validation-set". Quote the cost first and wait for my approval before proceeding.

The agent will:

  1. Call list_models with tags: "validation-set" to find matching models
  2. Call quote with the dataset configuration to show the total credit cost
  3. Wait for your confirmation
  4. Call create_batch_renders to submit all datasets at once
  5. Poll get_render for each until complete
  6. Return download URLs for all ZIPs

Available Tools

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

Tips

Tag everything. Tags are the key to efficient batch operations. Tag models on upload or via the portal, then target them in prompts:

Render all models tagged "new-arrivals" in 16:9 white with shadows.

Quote first on large batches. The quote tool prevents surprises. Ask the agent to quote before any batch:

Quote 16:9 + 1:1 + 9:16 renders for my 80 shoe models, then proceed only if it's under 250 credits.

Atomic credit deduction. Batch renders deduct all credits at once. If you don't have enough for the full batch, nothing is charged — the batch fails cleanly and you can adjust.


Tiers & Render Settings

Video Quality Tiers

Tier (API value) Label Resolution Codec Alpha Credits/sec Best for
share Standard 960×540 H.264 MP4 No 1 Quick shares, web, messaging, social
standard HD 1920×1080 H.264 MP4 No 4 Web, social media, product pages
4k 4K 3840×2160 H.264 MP4 No 16 In-store displays, kiosks, high-DPI screens
pro ProRes 4444 3840×2160 ProRes 4444 Yes 64 Video editing, compositing, broadcast

Video is charged per second of output based on quality tier.

Dataset Tiers

Tier Views Resolution Credits Best for
100x800 100 800×800 40 Quick experiments, proof-of-concept
196x1024 196 1024×1024 160 Production training, best quality-to-cost ratio
400x2048 400 2048×2048 640 Maximum fidelity, large-scale reconstruction

Datasets are a flat cost per set.

Coverage

Output ZIP contents

Every dataset ZIP contains:

After downloading a dataset ZIP, you can train directly with nerfstudio (the --pipeline.model.background-color white flag is required, since images are composited on white):

unzip dataset.zip -d mymodel
ns-train splatfacto --data ./mymodel \
  --max-num-iterations 15000 \
  --pipeline.model.sh-degree 3 \
  --pipeline.model.background-color white

Render Settings

Field Type Default Description
quality string "standard" "share" (540p, 1 credit/sec), "standard" (HD 1080p, 4 credits/sec — the default when omitted: a default 6s render with no quality set costs 24 credits), "4k" (16/sec), "pro" (4K ProRes with alpha, 64/sec)
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 number 6 Duration in seconds (3–120, decimal OK e.g. 4.7)
animation string Animation clip name (or "*" for first clip)
animationSpeed number 1 Animation speed multiplier (e.g. 0.5, 2)
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)

Error Handling

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 1 GB — reduce before upload
invalid_format 400 Only .glb and .zip (OBJ+MTL) are supported — pack .gltf as .glb
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

Further Reading