dx.gl

API Documentation

Markdown Portal Guide

Programmatic access to the DXGL rendering pipeline

Contents
AuthenticationResponse FormatModelsRendersAssetsRender SettingsRender StatusOverlaysAccountQuoteHealth CheckRender CreditsRate LimitsErrorsQuick Start

Authentication

All requests require a Bearer token in the Authorization header:

Authorization: Bearer dxgl_sk_...

Tokens are created in the Client Portal via the API Keys button in the utility strip. The raw token is shown once on creation — store it securely.


Response Format

Success:

{
  "data": { ... }
}

List (paginated):

{
  "data": [ ... ],
  "meta": { "total": 142, "offset": 0, "limit": 50 }
}

Error:

{
  "error": {
    "code": "model_not_found",
    "message": "Model not found",
    "status": 404
  }
}

Models

A model is an uploaded 3D file (GLB/glTF) or a converted 3D scan. Uploading a model automatically creates a render job.

Upload Model

POST /v1/models

Field Type Required Description
file File Yes .glb, .gltf, or .zip (OBJ+MTL+textures)
renderSettings JSON string No Render configuration (see below)

Response 201:

{
  "data": {
    "modelId": "Ab3kF9x2qL1m",
    "renderId": "Xz7pQ4w8nR2k"
  }
}

If the file's SHA-256 matches an existing upload, a new render is created for the existing model and duplicate: true is returned.

Instant preview: Every upload automatically triggers a free system preview render (no credits deducted). The preview generates a lightweight thumbnail video within seconds, which also serves as a model integrity check — if the preview fails, the model likely has issues (broken geometry, missing textures, invalid glTF). Check the model's renders list for a render with is_system_preview: true.

3D scan support: Upload a .zip containing one OBJ file with its MTL and texture files (JPG/PNG). One model per ZIP. The server converts to GLB automatically, with materials set to roughness 1.0 for a natural matte finish. The converted GLB is stored as your model and can be downloaded via GET /v1/models/:id/file. Ideal for photogrammetry and structured-light scan exports (Artec Studio, RealityCapture, Metashape, etc.).

Ingest from URL

POST /v1/models/ingest

curl -X POST https://dx.gl/v1/models/ingest \
  -H "Authorization: Bearer dxgl_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/model.glb", "renderSettings": {"aspect": "16:9"}}'

Response 201: Same as upload.

Downloads the file from the URL (max 100 MB, 2-minute timeout). SHA-256 deduplication applies. URL ingest currently supports GLB/glTF only. For OBJ scans, use the file upload endpoint with a ZIP.

List Models

GET /v1/models?limit=50&offset=0&tags=furniture

Param Type Default Description
limit integer 50 Max 100
offset integer 0 Pagination offset
tags string Comma-separated tags (OR filter)
curl "https://dx.gl/v1/models?limit=10&tags=furniture" \
  -H "Authorization: Bearer dxgl_sk_..."

Response 200:

{
  "data": [
    {
      "id": "Ab3kF9x2qL1m",
      "originalName": "chair.glb",
      "title": "Ergonomic Chair",
      "sku": "EC-1001",
      "tags": ["furniture", "office"],
      "fileSize": 4521984,
      "sha256": "a1b2c3...",
      "createdAt": "2026-02-15T20:00:00.000Z"
    }
  ],
  "meta": { "total": 42, "offset": 0, "limit": 50 }
}

Get Model

GET /v1/models/:id

Returns the model detail with all its renders:

curl https://dx.gl/v1/models/Ab3kF9x2qL1m \
  -H "Authorization: Bearer dxgl_sk_..."
{
  "data": {
    "id": "Ab3kF9x2qL1m",
    "originalName": "chair.glb",
    "title": "Ergonomic Chair",
    "sku": "EC-1001",
    "tags": ["furniture", "office"],
    "fileSize": 4521984,
    "sha256": "a1b2c3...",
    "createdAt": "2026-02-15T20:00:00.000Z",
    "renders": [
      {
        "id": "Xz7pQ4w8nR2k",
        "status": "done",
        "renderSettings": { ... },
        "createdAt": "2026-02-15T20:00:01.000Z",
        "updatedAt": "2026-02-15T20:01:30.000Z"
      }
    ]
  }
}

Update Model

PATCH /v1/models/:id

Field Type Description
title string Display title
sku string Product code
tags string[] Up to 20 tags (lowercased, trimmed)

All fields are optional — only provided fields are changed.

curl -X PATCH https://dx.gl/v1/models/Ab3kF9x2qL1m \
  -H "Authorization: Bearer dxgl_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"title": "Ergonomic Chair Pro", "sku": "ECP-2001", "tags": ["furniture", "office"]}'

Archive Model

PATCH /v1/models/:id/archive

Archives the model. Archived models are hidden from default list queries but can be restored.

curl -X PATCH https://dx.gl/v1/models/Ab3kF9x2qL1m/archive \
  -H "Authorization: Bearer dxgl_sk_..."

Unarchive Model

PATCH /v1/models/:id/unarchive

Restores an archived model back to active status.

Download Model File

GET /v1/models/:id/file

Downloads the original GLB file. Returns Content-Type: model/gltf-binary with a Content-Disposition: attachment header.

curl -o chair.glb https://dx.gl/v1/models/Ab3kF9x2qL1m/file \
  -H "Authorization: Bearer dxgl_sk_..."

Check Hash (Dedup Pre-flight)

POST /v1/models/check-hash

{ "sha256": "a1b2c3d4..." }

Response 200:

{
  "data": {
    "exists": true,
    "modelId": "Ab3kF9x2qL1m"
  }
}

Pre-flight check to avoid uploading duplicate files. Compute the SHA-256 of your file locally, then call this endpoint before uploading.

Delete Model

DELETE /v1/models/:id

Soft-deletes the model. Existing renders remain accessible until independently deleted.


Renders

A render is a video generated from a model. Creating a render queues it for processing by the render pipeline.

Create Render

POST /v1/renders

{
  "modelId": "Ab3kF9x2qL1m",
  "renderSettings": {
    "quality": "share",
    "aspect": "16:9",
    "bgColor": "#ffffff",
    "length": 6,
    "animation": "Walk",
    "animationSpeed": 1,
    "shadows": true,
    "reflector": false,
    "easing": true
  }
}

The quality field is optional (defaults to "share", which is Standard 540p — 1 credit). Set to "standard" for HD 1080p H.264 (4 credits), "4k" for 4K H.264 (16 credits), or "pro" for 4K ProRes 4444 with alpha (64 credits). Note that the JSON enum values are kept for API stability; they do not match the portal's display labels one-to-one. See Render Settings for all options.

Response 201:

{
  "data": {
    "id": "Xz7pQ4w8nR2k",
    "modelId": "Ab3kF9x2qL1m",
    "status": "pending",
    "renderSettings": { ... }
  }
}

List Renders

GET /v1/renders?model=Ab3kF9x2qL1m&status=done&limit=50&offset=0

Param Type Description
model string Filter by model ID
status string Filter by status
limit integer Max 100, default 50
offset integer Pagination offset

Get Render

GET /v1/renders/:id

{
  "data": {
    "id": "Xz7pQ4w8nR2k",
    "modelId": "Ab3kF9x2qL1m",
    "status": "done",
    "renderSettings": { ... },
    "fileSize": 2457600,
    "errorMessage": null,
    "hasWebVariant": true,
    "createdAt": "2026-02-15T20:00:01.000Z",
    "updatedAt": "2026-02-15T20:01:30.000Z"
  }
}
Field Type Description
fileSize integer Video file size in bytes (null until done)
errorMessage string Error detail when status is error
hasWebVariant boolean Whether a web-optimized MP4 is available

Dismiss Render

PATCH /v1/renders/:id/dismiss

Dismisses an errored render (transitions errorfailed). Useful for acknowledging errors programmatically.

Delete Render

DELETE /v1/renders/:id

Permanently deletes the render and its files (video, poster, thumbnail).

Batch Render

POST /v1/renders/batch

{
  "renders": [
    { "modelId": "Ab3kF9x2qL1m", "renderSettings": { "aspect": "16:9", "bgColor": "#ffffff" } },
    { "modelId": "Ab3kF9x2qL1m", "renderSettings": { "aspect": "1:1", "bgColor": "#000000" } },
    { "modelId": "Yz9mK3v7pN4j", "renderSettings": { "aspect": "16:9" } }
  ]
}

Maximum 100 renders per batch. Each item requires a modelId and optional renderSettings. All credits are deducted atomically — if there aren't enough credits, the entire batch fails.

Response 201:

{
  "data": [
    { "id": "Xz7pQ4w8nR2k", "modelId": "Ab3kF9x2qL1m", "status": "pending" },
    { "id": "Bc5nL2x9mQ3r", "modelId": "Ab3kF9x2qL1m", "status": "pending" },
    { "id": "Wv8jR6t4kP1s", "modelId": "Yz9mK3v7pN4j", "status": "pending" }
  ]
}

Renders for the same model are automatically grouped into a batch — the worker renders shared frames once and encodes variants in parallel.


Assets

Download the output files of a completed render.

Video

GET /v1/renders/:id/video

Returns the video file. Supports Range headers for streaming.

Pass ?quality=web to get the lighter web-optimized variant (when available, see hasWebVariant).

Content-Type: video/mp4 (standard/4k) or video/quicktime (pro — ProRes .mov)

Poster

GET /v1/renders/:id/poster

Returns the poster image (first frame). By default returns a small thumbnail suitable for previews. Pass ?quality=full to get the full-resolution PNG at the video's native dimensions (e.g. 1920×1080).

Content-Type: image/png

Thumbnail

GET /v1/renders/:id/thumb

Returns a quarter-resolution MP4 thumbnail video. Supports Range headers.

Content-Type: video/mp4

Bundle (Zip Download)

GET /v1/renders/:id/bundle

Downloads all render assets as a single ZIP file. The zip is streamed directly — no server-side buffering, suitable for large ProRes files. Contains:

The response includes Cache-Control: immutable — renders never change after completion.

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

Render Settings

Field Type Default Description
quality string "share" Quality tier: "share" (Standard, 540p H.264 — default), "standard" (HD, 1080p H.264), "4k" (4K H.264), "pro" (ProRes 4444, 4K with alpha). Portal display labels differ from JSON enum values; enums are frozen for API stability.
effect string "turntable" Camera path: "turntable" (360°), "hero-spin" (fast spin to front), "showcase" (look-around), "zoom-orbit" (360° + zoom), "reveal" (scale-up entrance), "dataset" (vision training, 4 credits)
sweepAngle number Override sweep angle in degrees (hero-spin default 540, reveal default 180). Range: 10–1080.
easeOutRatio number Hero-spin deceleration fraction (default 0.6). Range: 0–1.
amplitude number Showcase swing angle in degrees (default 60). Range: 5–180.
zoomStart number Zoom-orbit starting radius multiplier (default 1.0). Range: 0.1–3.
zoomEnd number Zoom-orbit ending radius multiplier (default 0.7). Range: 0.1–3.
scaleFrom number Reveal starting model scale (default 0). Range: 0–1.
vignette number Vignette intensity (0 = none, 1 = full). Range: 0–1.
aspect string "16:9" Video aspect ratio: "16:9", "1:1", "9:16"
bgColor string "#ffffff" Background hex color, e.g. "#ff8800" (6-digit hex, must include #)
bgImageId string Overlay asset ID for background image (overrides bgColor). See Overlays.
bgAlign string "top-left" Crop anchor when background image doesn't match output aspect: "top-left", "top-center", "top-right", "center-left", "center", "center-right", "bottom-left", "bottom-center", "bottom-right"
length number 6 Video duration in seconds: 330 (decimal OK, e.g. 4.7). Admin accounts: up to 600.
animation string Animation clip name to play (from model's embedded animations). Use "*" for the first clip.
animationSpeed number 1 Animation playback speed multiplier (e.g. 0.5 for half speed, 2 for double).
shadows boolean true Enable ground shadows
reflector boolean false Enable reflective ground plane
easing boolean true Ease in/out on turntable rotation
rotateY integer 0 Turntable camera starting angle in degrees (-180 to 180). Determines the initial camera direction before rotation begins. Does not rotate the model.
tiltX integer 0 Forward/back tilt in degrees (-90 to 90). Useful for angling products in portrait aspect. Bypasses shadow caching.
panX integer 0 Horizontal pan (yaw) of the model in degrees (-180 to 180). Applied after tilt.
rollZ integer 0 Roll rotation of the model around Z axis in degrees (-180 to 180).
zoom number 1.0 Camera distance multiplier (0.5 to 2.0). Values below 1 move closer, above 1 move farther.

Quality Tiers

JSON value Portal label Resolution Codec Alpha Credits Output
share Standard (default) 960×540 H.264 MP4 No 1 .mp4
standard HD 1920×1080 H.264 MP4 No 4 .mp4
4k 4K 3840×2160 H.264 MP4 No 16 .mp4
pro ProRes 4444 3840×2160 ProRes 4444 Yes 64 .mov

The JSON enum values (share/standard/4k/pro) are frozen for API stability. The portal's display labels were renamed in a later UX pass; use the JSON values shown in the first column when making API calls.

pro renders always include an alpha channel — the bgColor is applied only to the web/thumbnail/poster variants. The .mov file has a transparent background for compositing in professional editors (DaVinci Resolve, After Effects, Final Cut Pro).

All tiers render with GPU-accelerated 2× supersampling (SSAA) for anti-aliased edges.

Note: New accounts receive 10 free credits on signup. These credits behave identically to purchased credits — no watermarks, no restrictions, and they can be used on any quality tier.

Dataset Export (Vision Training)

Set output: "dataset" with datasetQuality to generate a NeRF/3DGS-ready training dataset. Three tiers:

Tier (datasetQuality) Views Resolution Credits
100x800 100 800×800 4
196x1024 196 1024×1024 16
400x2048 400 2048×2048 64

Use coverage: "hemisphere" (default) or coverage: "sphere" for full sphere viewpoints.

Output ZIP contains:

POST /v1/renders

Quick start: Unzip the dataset and train a 3D Gaussian Splat with nerfstudio:

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

The background-color white flag is required because images are composited on a white background.


Render Status

Renders progress through these statuses:

pendingposter-processingposter-donevideo-processingdone

Any status can transition to error if the render fails.

Status Description
pending Queued, waiting for a worker to pick it up
poster-processing Worker is rendering frames, poster extracted
poster-done Poster uploaded, video encoding in progress
video-processing Video being encoded (ffmpeg)
done All assets ready (video, poster, thumbnail)
error Render failed

Overlays

Overlay assets are reusable background (or foreground) images for renders. Upload once, reference by ID in render settings. System overlays (published by admins) are available to all users.

Upload Overlay

POST /v1/overlays

Field Type Default Description
file file Image file (PNG, JPEG, WebP; max 10 MB)
layer string "background" "background" or "foreground"
name string Display name (max 100 chars)
description string Short description (max 500 chars)
alignment string "top-left" Crop anchor when image doesn't match output aspect ratio

Response 200:

{ "data": { "id": "abc123DEF456", "layer": "background", "alignment": "top-left", "name": "My Gradient" } }

List Overlays

GET /v1/overlays

Returns your overlay assets plus all published system overlays.

Delete Overlay

DELETE /v1/overlays/:id

Deletes an overlay you own. Removes the image from storage.

Using Overlays in Renders

Pass the overlay id as bgImageId in render settings:

{ "renderSettings": { "bgImageId": "abc123DEF456", "aspect": "16:9", "length": 6 } }

When bgImageId is set, bgColor is ignored. The image is scaled to cover the output dimensions and cropped from the bgAlign anchor (default: top-left), keeping logos safe across all aspect ratios.


Account

GET /v1/account

Returns the authenticated user's account info and credit balance.

curl https://dx.gl/v1/account \
  -H "Authorization: Bearer dxgl_sk_..."
{
  "data": {
    "id": "Ab3kF9x2qL1m",
    "email": "user@example.com",
    "credits": 47
  }
}

Quote

Estimate the credit cost for a set of renders before committing. Useful for agents and scripts that need to budget credits across multiple models.

POST /v1/quote

{
  "renders": [
    { "quality": "standard" },
    { "quality": "4k" },
    { "output": "dataset", "datasetQuality": "196x1024" }
  ]
}

Each item in the renders array accepts the same fields as render settings (quality, output, datasetQuality). Only cost-relevant fields are used.

Response 200:

{
  "data": {
    "creditsRequired": 36,
    "creditsAvailable": 47,
    "sufficient": true,
    "breakdown": [
      { "quality": "standard", "credits": 4 },
      { "quality": "4k", "credits": 16 },
      { "datasetQuality": "196x1024", "credits": 16 }
    ]
  }
}
Field Type Description
creditsRequired integer Total credits needed for all renders
creditsAvailable integer Current credit balance (paid + free)
sufficient boolean Whether the account has enough credits
breakdown array Per-item credit cost

Maximum 100 items per quote request.

curl -X POST https://api.dx.gl/v1/quote \
  -H "Authorization: Bearer dxgl_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"renders": [{"quality": "4k"}, {"quality": "4k"}, {"quality": "4k"}]}'

Health Check

GET /v1/health

No authentication required. Returns database connectivity status.


Render Credits

Render cost depends on quality tier:

Quality Credits
share 1
standard 4
4k 16
pro 64

New accounts include 10 free credits on signup. These credits behave identically to purchased credits — no watermarks, no restrictions, and they work on any quality tier. Credits never expire.

Batch renders deduct credits atomically based on the sum of all items' quality costs. If there aren't enough credits, the entire batch fails.

When credits are exhausted, render requests return 402 with error code no_credits.


Rate Limits

Not currently enforced. Planned for a future release.


Errors

Code Status Description
unauthorized 401 Missing, invalid, or revoked token
token_expired 401 Token has expired
forbidden 403 Account suspended
insufficient_scope 403 Token lacks required scope
no_file 400 No file in upload request
invalid_format 400 File is not .glb, .gltf, or .zip
invalid_url 400 Malformed URL
url_required 400 Missing url field
invalid_settings 400 Invalid renderSettings value
model_id_required 400 Missing modelId field
file_too_large 400 File exceeds 100 MB limit
no_credits 402 No render credits remaining
model_not_found 404 Model does not exist
render_not_found 404 Render does not exist
renders_required 400 Missing or empty renders array
too_many 400 Batch exceeds 100 renders
upload_limit 429 Free upload limit reached
video_not_found 404 Video not yet available
poster_not_found 404 Poster not yet available
thumb_not_found 404 Thumbnail not yet available
file_not_found 404 Model file not found in storage
sha256_required 400 Missing sha256 field
timeout 408 URL download timed out
internal 500 Internal server error

Quick Start

# Upload a GLB model and start rendering
curl -X POST https://api.dx.gl/v1/models \
  -H "Authorization: Bearer dxgl_sk_..." \
  -F "file=@chair.glb" \
  -F 'renderSettings={"aspect":"16:9","bgColor":"#ffffff","length":6}'

# Upload a 3D scan (OBJ+MTL+textures in a ZIP)
curl -X POST https://api.dx.gl/v1/models \
  -H "Authorization: Bearer dxgl_sk_..." \
  -F "file=@artec-scan.zip" \
  -F 'renderSettings={"aspect":"1:1","bgColor":"#ffffff","length":9}'

# Create a 4K render (16 credits)
curl -X POST https://api.dx.gl/v1/renders \
  -H "Authorization: Bearer dxgl_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"modelId":"Ab3kF9x2qL1m","renderSettings":{"quality":"4k","aspect":"16:9","bgColor":"#ffffff"}}'

# Create a Pro render (ProRes 4444 with alpha, 64 credits)
curl -X POST https://api.dx.gl/v1/renders \
  -H "Authorization: Bearer dxgl_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"modelId":"Ab3kF9x2qL1m","renderSettings":{"quality":"pro","aspect":"16:9","bgColor":"#000000"}}'

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

# Download the video when done
curl -o chair.mp4 https://api.dx.gl/v1/renders/Xz7pQ4w8nR2k/video \
  -H "Authorization: Bearer dxgl_sk_..."

# Download all assets as a zip
curl -o chair.zip https://api.dx.gl/v1/renders/Xz7pQ4w8nR2k/bundle \
  -H "Authorization: Bearer dxgl_sk_..."

Python Example

import requests, time

API = "https://dx.gl/v1"
HEADERS = {"Authorization": "Bearer dxgl_sk_..."}

# Upload
with open("chair.glb", "rb") as f:
    r = requests.post(f"{API}/models", headers=HEADERS,
        files={"file": f},
        data={"renderSettings": '{"aspect":"16:9","length":6}'})
render_id = r.json()["data"]["renderId"]

# Poll
while True:
    r = requests.get(f"{API}/renders/{render_id}", headers=HEADERS)
    status = r.json()["data"]["status"]
    if status == "done": break
    if status == "error": raise Exception("Render failed")
    time.sleep(5)

# Download
r = requests.get(f"{API}/renders/{render_id}/video", headers=HEADERS)
with open("chair.mp4", "wb") as f:
    f.write(r.content)

Node.js Example

const fs = require('fs');

const API = 'https://dx.gl/v1';
const headers = { 'Authorization': 'Bearer dxgl_sk_...' };

// Upload
const form = new FormData();
form.append('file', fs.createReadStream('chair.glb'));
form.append('renderSettings', JSON.stringify({ aspect: '16:9', length: 6 }));

const upload = await fetch(API + '/models', { method: 'POST', headers, body: form });
const renderId = (await upload.json()).data.renderId;

// Poll
let status;
do {
  await new Promise(r => setTimeout(r, 5000));
  const res = await fetch(API + '/renders/' + renderId, { headers });
  status = (await res.json()).data.status;
} while (status !== 'done' && status !== 'error');

// Download
const video = await fetch(API + '/renders/' + renderId + '/video', { headers });
fs.writeFileSync('chair.mp4', Buffer.from(await video.arrayBuffer()));