Programmatic access to the DXGL rendering pipeline
All requests require a Bearer token in the Authorization header:
Authorization: Bearer dxgl_sk_...
Create tokens in the Portal under the API tab. The raw token is shown once on creation — store it securely.
Success:
{
"data": { ... }
}
Paginated list:
{
"data": [ ... ],
"meta": { "total": 142, "offset": 0, "limit": 50 }
}
Error:
{
"error": {
"code": "model_not_found",
"message": "Model not found",
"status": 404
}
}
A model is an uploaded 3D file (GLB/glTF) or a converted 3D scan. Uploading automatically creates a render job.
Upload a model file. Multipart form data.
| Field | Type | Required | Description |
|---|---|---|---|
file | File | Yes | .glb, .gltf, or .zip (OBJ+MTL+textures) |
renderSettings | JSON string | No | Render configuration |
# Upload a GLB
curl -X POST https://dx.gl/v1/models \
-H "Authorization: Bearer dxgl_sk_..." \
-F "[email protected]" \
-F 'renderSettings={"aspect":"16:9","bgColor":"#ffffff","length":6}'
# Upload a 3D scan (OBJ+MTL+textures in a ZIP)
curl -X POST https://dx.gl/v1/models \
-H "Authorization: Bearer dxgl_sk_..." \
-F "[email protected]" \
-F 'renderSettings={"aspect":"1:1","bgColor":"#ffffff","length":9}'
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.
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.).
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.
Ingest a model from a URL. Max 100 MB, 2-minute timeout. URL ingest currently supports GLB/glTF only. For OBJ scans, use the file upload endpoint with a ZIP.
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:
{
"data": {
"modelId": "Ab3kF9x2qL1m",
"renderId": "Xz7pQ4w8nR2k"
}
}
List all models. Paginated.
| 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_..."
Get model detail with all its renders.
curl https://dx.gl/v1/models/Ab3kF9x2qL1m \
-H "Authorization: Bearer dxgl_sk_..."
Response:
{
"data": {
"id": "Ab3kF9x2qL1m",
"originalName": "chair.glb",
"title": "Ergonomic Chair",
"sku": "EC-1001",
"tags": ["furniture", "office"],
"fileSize": 4521984,
"createdAt": "2026-02-15T20:00:00.000Z",
"renders": [
{ "id": "Xz7pQ4w8nR2k", "status": "done", "renderSettings": { ... } }
]
}
}
Update model metadata. 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"]}'
| Field | Type | Description |
|---|---|---|
title | string | Display title |
sku | string | Product code |
tags | string[] | Up to 20 tags (lowercased, trimmed) |
Archive a model. Archived models are hidden from default list queries but can be restored.
Restore an archived model back to active status.
Download 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_..."
SHA-256 dedup pre-flight. Compute your file's hash locally, then check before uploading.
{ "sha256": "a1b2c3d4..." }
Response 200:
{ "data": { "exists": true, "modelId": "Ab3kF9x2qL1m" } }
Soft-delete a model. Existing renders remain accessible until independently deleted.
A render is a video generated from a model. Creating a render queues it for processing.
Create a render for an existing model.
{
"modelId": "Ab3kF9x2qL1m",
"renderSettings": {
"quality": "standard",
"aspect": "16:9",
"bgColor": "#ffffff",
"length": 6,
"shadows": true,
"reflector": false,
"easing": true
}
}
The quality field is optional (defaults to "standard"). Set to "4k" for 4K H.264 (4 credits) or "pro" for 4K ProRes 4444 with alpha (12 credits). See Render Settings for all options.
List renders. Filterable.
| Param | Type | Description |
|---|---|---|
model | string | Filter by model ID |
status | string | Filter by status (e.g. done) |
limit | integer | Max 100, default 50 |
offset | integer | Pagination offset |
Get render status and metadata.
{
"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 an errored render (transitions error → failed). Useful for acknowledging errors programmatically.
Permanently delete a render and its files (video, poster, thumbnail).
Create multiple renders in a single atomic request. All credits are deducted at once — if there aren't enough credits, the entire batch fails.
{
"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 render in the array requires a modelId and optional renderSettings.
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.
Download output files of a completed render.
Stream the MP4 video. Supports Range headers. Pass ?quality=web to get the lighter web-optimized variant (when available, see hasWebVariant).
Download the poster image (first frame, WebP).
Stream a quarter-resolution thumbnail MP4. Supports Range headers.
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 an overlay image. Accepts multipart/form-data with a file field (PNG, JPEG, or WebP, max 10 MB).
| Field | Type | Default | Description |
|---|---|---|---|
file | file | — | Image file (required) |
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 alignment when image doesn't match output aspect ratio |
Returns { "data": { "id": "...", "layer": "background", ... } }
List your overlay assets plus all published system overlays. Filter by layer with ?layer=background.
Delete an overlay asset you own. Removes the image from storage.
Pass the overlay's id as bgImageId in your 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.
Returns the authenticated user's account info and credit balance.
curl https://dx.gl/v1/account \
-H "Authorization: Bearer dxgl_sk_..."
Response 200:
{
"data": {
"id": "Ab3kF9x2qL1m",
"email": "[email protected]",
"credits": 47
}
}
No authentication required. Returns database connectivity status.
| Field | Type | Default | Options |
|---|---|---|---|
quality | string | "standard" | "standard" (1080p H.264) · "4k" (4K H.264) · "pro" (4K ProRes 4444 with alpha) |
effect | string | "turntable" | "turntable" (360°) · "hero-spin" (fast spin to front) · "showcase" (look-around) · "zoom-orbit" (360° + zoom) · "reveal" (scale-up entrance) · "dataset" (vision training, 1 credit) |
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. |
aspect | string | "16:9" | "16:9" "1:1" "9:16" |
bgColor | string | "#ffffff" | Any 6-digit hex color, e.g. "#ff8800" (must include #). Ignored when bgImageId is set. |
bgImageId | string | — | Short ID of an overlay asset (background layer). Replaces bgColor with the uploaded image. |
bgAlign | string | "top-left" | Image alignment when cropping to fit aspect ratio. "top-left" keeps logos anchored in the corner. |
length | integer | 6 | 3 – 15 (seconds) |
shadows | boolean | true | Ground shadows |
reflector | boolean | false | Reflective ground plane |
easing | boolean | true | Ease in/out on rotation |
rotateY | integer | 0 | Starting angle in degrees (−180 to 180). Rotates the model so a different side faces camera first. |
tiltX | integer | 0 | Forward/back tilt in degrees (−90 to 90). Useful for angling products in portrait aspect. Bypasses shadow caching. |
| Tier | Resolution | Codec | Alpha | Credits | Output |
|---|---|---|---|---|---|
standard | 1920×1080 | H.264 | No | 1 | .mp4 |
4k | 3840×2160 | H.264 | No | 4 | .mp4 |
pro | 3840×2160 | ProRes 4444 | Yes | 16 | .mov |
Pro renders always include an alpha channel — the bgColor is applied only to 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).
Note: The PREVIEW watermark is automatically applied when using free render credits (standard quality only). Free credits cannot be used for 4k or pro renders.
Set output: "dataset" to generate a NeRF/3DGS-ready training dataset instead of a video. Use datasetQuality to select a tier and coverage for hemisphere or full sphere viewpoints.
Tier (datasetQuality) | Views | Resolution | Credits |
|---|---|---|---|
100x800 | 100 | 800×800 | 1 |
196x1024 | 196 | 1024×1024 | 4 |
400x2048 | 400 | 2048×2048 | 16 |
Output is a ZIP containing:
images/ — RGB PNG frames (composited on white background)depth/ — 8-bit grayscale depth PNGs (closer = darker, farther = lighter). Tight near/far planes maximize precision across the model’s actual depth span.depth_16bit/ — 16-bit grayscale depth PNGs (65,536 levels of precision for surface reconstruction)normals/ — world-space normal map PNGsmasks/ — foreground/background alpha mask PNGstransforms.json — Camera intrinsics + per-frame 4×4 transform matrices (instant-ngp / nerfstudio format). Includes depth_near and depth_far for decoding: depth = pixel_value/255 * (depth_far - depth_near) + depth_nearoverview.webp — 4-quadrant contact sheet showing all views across RGB, depth, normals, masksPOST /v1/renders
{ "modelId": "Ab3kF9x2qL1m", "renderSettings": { "effect": "dataset" } }
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.
Any status can transition to error if the render fails.
| Status | Description |
|---|---|
pending | Queued, waiting for a worker |
poster-processing | Rendering frames, poster extracted |
poster-done | Poster uploaded, video encoding starting |
video-processing | Video being encoded |
done | All assets ready (video, poster, thumbnail) |
error | Render failed |
| 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 |
no_credits | 402 | No render credits remaining |
model_not_found | 404 | Model does not exist |
render_not_found | 404 | Render does not exist |
video_not_found | 404 | Video not yet available |
timeout | 408 | URL download timed out |
internal | 500 | Internal server error |
# GLB file
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}'
# Or a 3D scan ZIP
curl -X POST https://api.dx.gl/v1/models \
-H "Authorization: Bearer dxgl_sk_..." \
-F "[email protected]"
# Response: { "data": { "modelId": "Ab3k...", "renderId": "Xz7p..." } }
# 4K render (4 credits)
curl -X POST https://api.dx.gl/v1/renders \
-H "Authorization: Bearer dxgl_sk_..." \
-H "Content-Type: application/json" \
-d '{"modelId":"Ab3k...","renderSettings":{"quality":"4k","aspect":"16:9"}}'
# Pro render — ProRes 4444 with alpha (16 credits)
curl -X POST https://api.dx.gl/v1/renders \
-H "Authorization: Bearer dxgl_sk_..." \
-H "Content-Type: application/json" \
-d '{"modelId":"Ab3k...","renderSettings":{"quality":"pro","aspect":"16:9"}}'
curl https://api.dx.gl/v1/renders/Xz7pQ4w8nR2k \
-H "Authorization: Bearer dxgl_sk_..."
# Response: { "data": { "status": "done", ... } }
# Single video
curl -o chair.mp4 https://api.dx.gl/v1/renders/Xz7pQ4w8nR2k/video \
-H "Authorization: Bearer dxgl_sk_..."
# All assets as zip
curl -o chair.zip https://api.dx.gl/v1/renders/Xz7pQ4w8nR2k/bundle \
-H "Authorization: Bearer dxgl_sk_..."
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)
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()));