Skip to main content

Python SDK

Official Python SDK for the Aholo Open API.

Installation

Install only the packages you need:

pip install manycore-aholo-sdk-asset # file upload
pip install manycore-aholo-sdk-world # world recon & generation (v1.3.0+ supports insv)
pip install manycore-aholo-sdk-lux3d # Lux3D 3D generation

Authentication

Recommended: set the environment variable; the SDK reads AHOLO_API_KEY automatically:

export AHOLO_API_KEY=your_api_key_here

Or pass the key explicitly:

from manycore.aholo_sdk_world import create_world_client
from manycore.aholo_sdk_core import AholoClientConfig

world = create_world_client(AholoClientConfig(api_key='your_api_key_here', region='com'))
Security

Never hardcode API keys in source code, packages, or public repositories.

Region

ValueDescriptionAPI endpoint
cnChinahttps://api.aholo3d.cn
comGlobalhttps://api.aholo3d.com

Asset upload

from manycore.aholo_sdk_asset import create_asset_client

asset = create_asset_client(region='com')

Upload a file

result = asset.upload_file('video.mp4')
print(result.url) # public URL

Upload bytes

with open('image.jpg', 'rb') as f:
data = f.read()

result = asset.upload_bytes(data, filename='image.jpg')

Progress callback

def on_progress(uploaded: int, total: int) -> None:
pct = round(uploaded / total * 100)
print(f'\rUpload progress: {pct}%', end='', flush=True)

result = asset.upload_file('video.mp4', on_progress=on_progress)

UploadResult fields

FieldTypeDescription
urlstrPublic file URL
md5strFile MD5
upload_keystr | NoneOUS upload key
obs_task_idstr | NoneOUS task ID

World

from manycore.aholo_sdk_world import create_world_client

world = create_world_client(region='com')

Reconstruction type supports image, video, and insv (manycore-aholo-sdk-world v1.3.0+). Generation resources accept image only (at most one).

3DGS reconstruction (video / images)

op = world.reconstructions.create(
name='Living room',
resources=[{'url': 'https://cdn.example.com/room.mp4', 'type': 'video'}],
task_quality='normal', # 'low' | 'normal' | 'high'
scene='model', # 'model' | 'space'
use_mask=False, # optional: segment uploaded resources when True
)

detail = world.wait_for(op['worldId'])
print(detail.get('assets', {}).get('splats', {}).get('urls', {}).get('plyPath'))
Image reconstruction requirements

Image reconstruction requires ≥ 20 image resources (type image; extensions .jpg/.jpeg/.png/.webp). Use type=video for .mp4/.mov; Insta360 panoramic video uses type=insv (.insv). URL extension must match type.

Insta360 example:

op = world.reconstructions.create(
name='Panoramic living room',
resources=[{'url': 'https://cdn.example.com/room.insv', 'type': 'insv'}],
task_quality='high',
scene='space',
)

3DGS generation (from prompt)

Generation resources accept images only (type image, at most one) — not video / insv.

op = world.generations.create(
name='Forest cabin',
prompt='A modern cabin in the forest',
# resources=[{'url': 'https://cdn.example.com/ref.jpg', 'type': 'image'}], # optional, at most one
)

detail = world.wait_for(op['worldId'])

Get world detail

detail = world.retrieve(world_id)
print(detail.get('status'))

Task status & polling

PhaseStatusDescription
In progressPENDINGQueued
In progressPREPROCESSINGPreprocessing
In progressRUNNINGRunning
SuccessSUCCEEDEDDone
FailedFAILEDFailed
FailedCANCELEDCanceled
FailedTIMEOUTTimed out
FailedREJECTEDRejected

world.wait_for(world_id) returns details on SUCCEEDED; failure terminal states raise PollingFailedError.

WorldDetail fields

FieldTypeDescription
worldIdstrWorld ID
statusstrTask status
assets.splats.urls.plyPathstr | NonePLY download URL
assets.splats.urls.spzPathstr | NoneSPZ download URL
assets.splats.urls.lodMetaPathstr | NoneLOD metadata URL
assets.imagery.panoUrlstr | NoneAI-generated panorama URL
assets.semanticsMetadata.upAxisstr | NoneWorld up axis (Y / Z)

Lux3D

from manycore.aholo_sdk_lux3d import create_lux3d_client

lux3d = create_lux3d_client(region='com')

Image to 3D

# From URL (omit version for default v3.0-standard)
task_id = lux3d.img_to_3d.create(
img='https://example.com/object.jpg',
)

# v3.0 options: face count and optional exports
task_id_v3 = lux3d.img_to_3d.create(
img='https://example.com/object.jpg',
face_count=80_000,
output_format=['zip', 'glb', 'usdz', 'obj_zip'],
)

# From local file
task_id = lux3d.img_to_3d.create_from_file('./object.jpg')

result = lux3d.tasks.wait_for(task_id)
print(result['outputs'][0]['content']) # default zip download URL

Text to 3D

task_id = lux3d.text_to_3d.create(
prompt='A wooden chair with carved legs',
style='photorealistic', # see styles below
)
result = lux3d.tasks.wait_for(task_id)

Text-to-3D styles: photorealistic (default) | cartoon | anime | hand_painted | cyberpunk | fantasy | glass

Material transfer

task_id = lux3d.material_transfer.create(
img='https://example.com/material.jpg',
mesh_url='https://example.com/model.glb',
)
result = lux3d.tasks.wait_for(task_id)

Version differences

VersionDefaultOutput formats (outputs indices)Notes
v3.0-standardYesFive slots zip / glb / usdz / obj_zip / fbx_zipChoose exports with output_format; unrequested slots may be NOT_REQUESTED
v2.0-previewSame five slots as v32.0 architecture
v1.0-proSingle ZIPFull PBR with transparency
G1zip / glb / plybeta; enable_pbr / texture_size; multi-view imgs

faceCount (10_000–500_000) applies to v2 / v3 / G1 (v2/v3 default 60_000, G1 default 200_000); v1.0-pro ignores it.

Lux3dTaskResult fields

FieldTypeDescription
taskIdintTask ID
statusint0 init; 1 running; 3 success; 4 failed
outputslistOutput files (outputs[n]['content'] is download URL, valid ~2 hours after success)

lux3d.tasks.wait_for(task_id) returns when status == 3; raises PollingFailedError on status == 4. Poll every 10–15 seconds.


Error handling

from manycore.aholo_sdk_core import (
AuthenticationError,
RateLimitError,
BusinessError,
PollingTimeoutError,
PollingFailedError,
)

try:
detail = world.wait_for(world_id)
except AuthenticationError:
print('Invalid or missing API Key')
except RateLimitError:
print('Rate limit exceeded')
except BusinessError as e:
print('Business error:', e.code, e)
except PollingTimeoutError:
print('Polling timed out')
except PollingFailedError as e:
print('Task failed:', e)
ExceptionDescription
AuthenticationErrorInvalid or missing API Key
RateLimitErrorRate limit exceeded
BusinessErrorAPI business error (includes code)
PollingTimeoutErrorPolling timed out
PollingFailedErrorTask failed

More examples

See GitHub examples:

FileDescription
upload_file.pyUpload a local file and print URL
world_reconstruct.pyUpload video / Insta360 .insv → 3DGS reconstruction → poll to completion
lux3d_img_to_3d.pyLocal image → Lux3D image-to-3D → poll to completion

After cloning the repo:

export AHOLO_API_KEY=your_api_key_here
# optional: export AHOLO_REGION=com # default cn

pip install -e packages/aholo-sdk-core -e packages/aholo-sdk-asset \
-e packages/aholo-sdk-world -e packages/aholo-sdk-lux3d

python examples/upload_file.py ./photo.jpg
python examples/world_reconstruct.py ./room.mp4 # also .mov, .insv
python examples/lux3d_img_to_3d.py ./chair.png

GitHub README is for installation only. If it conflicts with this page, this page wins. Source and runnable examples: GitHub.