Getting Started

Quick guide to get up and running with the vid2scene API.

šŸŽÆ What You'll Learn
  • How to get and use API keys
  • How to upload and process videos
  • How to monitor job progress
  • How to download results

Step 1: Get Your API Key

First, you'll need an API key to authenticate your requests.

  1. Log in and go to your profile page
  2. Scroll to the "API Keys" section
  3. Enter a name like "My First API Key"
  4. Click "Generate API Key"
  5. Copy the key (you'll only see it once!)
Important: Store your API key securely. Never commit it to version control or share it publicly.

Step 2: Upload a Video

The vid2scene API uses a two-step upload process for security and reliability:

2.1 Generate Upload URL

First, get a secure upload URL for your video. Send only the file extension:

curl -X POST \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"file_extension": "mp4"}' \
  https://vid2scene.com/api/v1/generate-upload-url/

This returns:

{
  "url": "https://storage.blob.core.windows.net/container/videos/uuid.mp4?sv=...",
  "blob_name": "videos/uuid.mp4"
}

2.2 Upload Your Video

Use the upload URL to upload your video file:

curl -X PUT \
  -H "x-ms-blob-type: BlockBlob" \
  --data-binary @my_video.mp4 \
  "SAS_URL_FROM_PREVIOUS_STEP"

Step 3: Create Processing Job

After uploading, create and start a processing job:

curl -X POST \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "blob_name": "videos/uuid.mp4",
    "title": "My Awesome Video",
    "public": false,
    "reconstruction_method": "glomap"
  }' \
  https://vid2scene.com/api/v1/submit-job/

This returns the job details:

{
  "success": true,
  "job_id": "123e4567-e89b-12d3-a456-426614174000",
  "title": "My Awesome Video",
  "status": "Queued",
  "uploaded_at": "2024-01-15T10:30:00Z",
  "public": false,
  "reconstruction_method": "glomap",
  "training_max_num_gaussians": 300000,
  "training_num_steps": 10000,
  "camera_data": {
    "position": {"x": 0.0, "y": 0.0, "z": 5.0},
    "lookAt": {"x": 0.0, "y": 0.0, "z": 0.0},
    "up": {"x": 0.0, "y": 1.0, "z": 0.0},
    "cameraType": "orbital"
  }
}

Step 4: Monitor Progress

Check the status of your job:

curl -H "Authorization: Api-Key YOUR_API_KEY" \
  https://vid2scene.com/api/v1/jobs/123e4567-e89b-12d3-a456-426614174000/

This returns the full job details including status:

{
  "job_id": "123e4567-e89b-12d3-a456-426614174000",
  "title": "My Awesome Video",
  "status": "Queued - position: 3",
  "percent_complete": null,
  "uploaded_at": "2024-01-15T10:30:00Z",
  "public": false,
  "reconstruction_method": "glomap",
  "training_max_num_gaussians": 300000,
  "training_num_steps": 10000,
  "has_ply": false,
  "has_spz": false,
  "camera_data": {
    "position": {"x": 0.0, "y": 0.0, "z": 5.0},
    "lookAt": {"x": 0.0, "y": 0.0, "z": 0.0},
    "up": {"x": 0.0, "y": 1.0, "z": 0.0},
    "cameraType": "orbital"
  }
}

Job status can be:

  • Queued - Waiting to start processing
  • Queued - position: X - Waiting in queue at position X
  • Processing video - Currently processing the video
  • Generating 3D scene - Creating the 3D reconstruction
  • Finished - Completed successfully
  • Failed - Processing failed
  • Not found - Job not found in queue

The percent_complete field shows progress during 3D scene generation (0-100), or null if not yet started.

Step 5: Get Results

Once processing is complete, you can:

5.1 Download Files

Download specific file types (PLY or SPZ) - these endpoints return 302 redirects:

# Download PLY file (follow redirect)
curl -H "Authorization: Api-Key YOUR_API_KEY" \
  -L "https://vid2scene.com/api/v1/jobs/123e4567-e89b-12d3-a456-426614174000/download/ply/" \
  -o scene.ply

# Download SPZ file (follow redirect)
curl -H "Authorization: Api-Key YOUR_API_KEY" \
  -L "https://vid2scene.com/api/v1/jobs/123e4567-e89b-12d3-a456-426614174000/download/spz/" \
  -o scene.spz

5.2 Get Preview Image

Fetch a short-lived preview image URL (returns a 302 redirect to the image):

curl -I -H "Authorization: Api-Key YOUR_API_KEY" \
  https://vid2scene.com/api/v1/jobs/123e4567-e89b-12d3-a456-426614174000/preview/

Complete Python Example

Here's a complete example in Python:

import requests
import time
import os
import json

# Your API key
API_KEY = "your-api-key-here"
BASE_URL = "https://vid2scene.com/api/v1"
headers = {"Authorization": f"Api-Key {API_KEY}"}

def print_response(step, response, show_json=True):
    """Print formatted response information"""
    print(f"\n{'='*50}")
    print(f"STEP {step}")
    print(f"{'='*50}")
    print(f"Status Code: {response.status_code}")
    print(f"URL: {response.url}")
    print(f"Headers: {dict(response.headers)}")
    
    if show_json and response.headers.get('content-type', '').startswith('application/json'):
        try:
            json_data = response.json()
            print(f"Response JSON:\n{json.dumps(json_data, indent=2)}")
        except:
            print(f"Response Text: {response.text}")
    elif response.text and len(response.text) < 1000:
        print(f"Response Text: {response.text}")
    print(f"{'='*50}\n")

# Step 1: Generate upload URL
print("šŸš€ Starting vid2scene API test...")
video_file = "my_video.mp4"
file_extension = os.path.splitext(video_file)[1].lstrip('.')

print(f"šŸ“ Video file: {video_file}")
print(f"šŸ“„ File extension: {file_extension}")
print("šŸ”— Generating upload URL...")

upload_response = requests.post(
    f"{BASE_URL}/generate-upload-url/",
    headers={**headers, "Content-Type": "application/json"},
    json={"file_extension": file_extension}
)
print_response("1: Generate Upload URL", upload_response)
upload_response.raise_for_status()
upload_data = upload_response.json()

# Step 2: Upload video
print("šŸ“¤ Uploading video to blob storage...")
file_size = os.path.getsize(video_file)
print(f"šŸ“Š File size: {file_size:,} bytes ({file_size/1024/1024:.2f} MB)")

with open(video_file, 'rb') as f:
    upload_result = requests.put(
        upload_data["url"],
        headers={"x-ms-blob-type": "BlockBlob"},
        data=f
    )
    print_response("2: Upload Video", upload_result, show_json=False)
    upload_result.raise_for_status()

print("āœ… Video upload complete!")

# Step 3: Create job
print("šŸŽ¬ Creating processing job...")
job_payload = {
    "blob_name": upload_data["blob_name"],
    "title": "My Python Upload",
    "public": False,
    "reconstruction_method": "glomap",
    "training_max_num_gaussians": 300000,
    "training_num_steps": 10000,
}
print(f"šŸ“‹ Job payload:\n{json.dumps(job_payload, indent=2)}")

job_response = requests.post(
    f"{BASE_URL}/submit-job/",
    headers={**headers, "Content-Type": "application/json"},
    json=job_payload
)
print_response("3: Create Job", job_response)
job_response.raise_for_status()
job_data = job_response.json()
job_id = job_data["job_id"]

print(f"šŸŽÆ Job created successfully!")
print(f"šŸ†” Job ID: {job_id}")

# Step 4: Monitor progress
print("ā³ Monitoring job progress...")
start_time = time.time()
check_count = 0

while True:
    check_count += 1
    elapsed_time = time.time() - start_time
    print(f"\nā±ļø  Check #{check_count} (elapsed: {elapsed_time:.1f}s)")
    
    status_response = requests.get(
        f"{BASE_URL}/jobs/{job_id}/",
        headers=headers
    )
    print_response(f"4.{check_count}: Check Status", status_response)
    status_response.raise_for_status()
    status_data = status_response.json()

    pc = status_data.get('percent_complete')
    pc_str = f" ({pc}%)" if pc is not None else ""
    status = status_data['status']
    
    print(f"šŸ“Š Status: {status}{pc_str}")
    
    # Print additional status details if available
    if 'current_step' in status_data:
        print(f"šŸ”„ Current step: {status_data['current_step']}")
    if 'estimated_time_remaining' in status_data:
        print(f"ā° ETA: {status_data['estimated_time_remaining']}")

    if status == "Finished":
        print("šŸŽ‰ Processing complete!")
        break
    if status == "Failed":
        print("āŒ Processing failed!")
        if 'error_message' in status_data:
            print(f"šŸ’„ Error: {status_data['error_message']}")
        break

    print("šŸ’¤ Waiting 30 seconds before next check...")
    time.sleep(30)

# Step 5: Get results
if status_data["status"] == "Finished":
    print("\nšŸŽ Downloading results...")
    
    # Download PLY file (follows redirect automatically)
    print("šŸ“„ Attempting to download PLY file...")
    try:
        ply_response = requests.get(
            f"{BASE_URL}/jobs/{job_id}/download/ply/",
            headers=headers,
            stream=True
        )
        print_response("5.1: Download PLY", ply_response, show_json=False)
        
        if ply_response.status_code == 200:
            filename = f"{job_id}_scene.ply"
            with open(filename, 'wb') as f:
                downloaded = 0
                for chunk in ply_response.iter_content(chunk_size=8192):
                    if chunk:
                        f.write(chunk)
                        downloaded += len(chunk)
                        if downloaded % (1024*1024) == 0:  # Print every MB
                            print(f"šŸ“„ Downloaded: {downloaded/1024/1024:.1f} MB")
            
            final_size = os.path.getsize(filename)
            print(f"āœ… PLY file downloaded: {filename} ({final_size:,} bytes)")
        else:
            print("āš ļø  PLY not available for this job")
    except requests.exceptions.RequestException as e:
        print(f"āŒ Error downloading PLY file: {e}")

    # Download SPZ file (follows redirect automatically)
    print("šŸ“„ Attempting to download SPZ file...")
    try:
        spz_response = requests.get(
            f"{BASE_URL}/jobs/{job_id}/download/spz/",
            headers=headers,
            stream=True
        )
        print_response("5.2: Download SPZ", spz_response, show_json=False)
        
        if spz_response.status_code == 200:
            filename = f"{job_id}_scene.spz"
            with open(filename, 'wb') as f:
                downloaded = 0
                for chunk in spz_response.iter_content(chunk_size=8192):
                    if chunk:
                        f.write(chunk)
                        downloaded += len(chunk)
                        if downloaded % (1024*1024) == 0:  # Print every MB
                            print(f"šŸ“„ Downloaded: {downloaded/1024/1024:.1f} MB")
            
            final_size = os.path.getsize(filename)
            print(f"āœ… SPZ file downloaded: {filename} ({final_size:,} bytes)")
        else:
            print("āš ļø  SPZ not available for this job")
    except requests.exceptions.RequestException as e:
        print(f"āŒ Error downloading SPZ file: {e}")

    # Get preview image (302 redirect)
    print("šŸ–¼ļø  Getting preview image URL...")
    preview_head = requests.head(
        f"{BASE_URL}/jobs/{job_id}/preview/",
        headers=headers,
        allow_redirects=False
    )
    print_response("5.3: Get Preview URL", preview_head, show_json=False)
    
    if preview_head.status_code == 302:
        preview_url = preview_head.headers.get('Location')
        print(f"šŸ–¼ļø  Preview image URL: {preview_url}")
    else:
        print("āš ļø  Preview image not available")

print(f"\nšŸ Script completed! Total runtime: {time.time() - start_time:.1f} seconds")

Best Practices

  • Video Quality: Use high-quality videos with good lighting for best results
  • Camera Movement: Smooth camera movements work better than shaky footage
  • Scene Content: Scenes with distinct features and textures process better
  • Duration: 2 to 3 minute videos typically produce the best results
  • Rate Limiting: Respect rate limits to avoid getting blocked
  • Error Handling: Always check response status codes and handle errors gracefully

Next Steps