Programmatic access to the DXGL rendering pipeline
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.
Success:
{
"data": { ... }
}
List (paginated):
{
"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 a model automatically creates a render job.
| 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.).
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.
| 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 }
}
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"
}
]
}
}
| 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"]}'
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_..."
Restores an archived model back to active status.
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_..."
{ "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.
Soft-deletes the model. Existing renders remain accessible until independently deleted.
A render is a video generated from a model. Creating a render queues it for processing by the render pipeline.
{
"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": { ... }
}
}
| Param | Type | Description |
|---|---|---|
model |
string | Filter by model ID |
status |
string | Filter by status |
limit |
integer | Max 100, default 50 |
offset |
integer | Pagination offset |
{
"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 |
Dismisses an errored render (transitions error → failed). Useful for acknowledging errors programmatically.
Permanently deletes the render and its files (video, poster, thumbnail).
{
"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.
Download the output files of a completed render.
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)
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
Returns a quarter-resolution MP4 thumbnail video. Supports Range headers.
Content-Type: video/mp4
Downloads all render assets as a single ZIP file. The zip is streamed directly — no server-side buffering, suitable for large ProRes files. Contains:
video.mp4 or video.mov (main video)web.mp4 (web-optimized variant)poster.png (full-resolution poster)thumb.mp4 (thumbnail video)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_..."
| 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: 3 – 30 (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. |
| 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.
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:
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 (10×10 grid of all views across RGB, depth, normals, masks)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.
Renders progress through these statuses:
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 |
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.
| 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" } }
Returns your overlay assets plus all published system overlays.
Deletes an overlay you own. Removes the image from storage.
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.
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
}
}
Estimate the credit cost for a set of renders before committing. Useful for agents and scripts that need to budget credits across multiple models.
{
"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"}]}'
No authentication required. Returns database connectivity status.
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.
Not currently enforced. Planned for a future release.
| 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 |
# 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_..."
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()));