Documentation

Uploading Images

Upload images with automatic AI description generation — via Python SDK or REST API

Upload

The SDK and REST API use a single request that handles upload and AI description queueing. Supports files up to 100MB. For larger files, compress or resize before uploading.

Supported File Types

.jpg, .jpeg, .png, .webp, and .gif. Unsupported file types are automatically filtered when uploading directories.

File types are auto-detected from content — you don't need to set the correct Content-Type header. If omitted or mismatched, the API detects the actual type from the file's contents and processes it accordingly.

Quick Start

Upload a Single Image

Upload an image and get an AI-generated description

python
async with Scopix(api_key="scopix_...") as client:
# Upload a single file - returns UploadResult directly
result = await client.files.upload("photo.jpg")
print(f"Image ID: {result.image_id}")
print(f"Description: {result.description}")
print(f"Tags: {result.tags}")
print(f"Visible Text: {result.visible_text}")

Upload

Single request for files up to 100MB — use this for almost all uploads

Single and Batch

The SDK handles method selection automatically. The REST API has separate single and batch endpoints.

python
# Single upload — returns UploadResult directly
result = await client.files.upload("photo.jpg")
print(f"Image ID: {result.image_id}")
print(f"Description: {result.description}")

Batch Upload

Up to 10,000 Files (SDK)

The SDK supports uploading up to 10,000 files in a single call. Large batches are automatically chunked into 1,000-file batches. The REST API supports 10–200 files per batch request, depending on your plan tier. Total request payload must not exceed 2 GB. Individual files are limited to 100 MB (images) or 50 MB (documents).

Upload Multiple Files

python
# Just pass a list - server-managed concurrency
results = await client.files.upload_batch(
["img1.jpg", "img2.jpg", "img3.jpg"]
)
for result in results:
print(f"{result.filename}: {result.description[:50]}...")

Directory Upload

SDK only — no REST equivalent

Upload Entire Directory

Pass a directory path to upload all supported images inside it

python
# Upload all images in a directory (recursive by default)
results = await client.files.upload_batch("/path/to/photos")
print(f"Uploaded {len(results)} images")
for result in results:
print(f" {result.filename}: {result.description[:50]}...")

Directory Options

python
# Non-recursive (only top-level files)
results = await client.files.upload_batch(
"/path/to/photos",
recursive=False # Don't include subdirectories
)
# Include hidden files and directories
results = await client.files.upload_batch(
"/path/to/photos",
include_hidden=True # Include .hidden files and .cache/ dirs
)
# Mix files and directories
results = await client.files.upload_batch([
"/path/to/folder1", # All images in folder1
"/path/to/folder2", # All images in folder2
"/path/to/specific_image.jpg" # Plus this specific file
])

Automatic Filtering

When uploading directories, only supported image files (.jpg, .jpeg, .png, .webp, .gif) are included. Hidden files and directories (starting with .) are excluded by default. Other file types like .pdf, .txt, or .mp4 are silently skipped.

Content Categories

Specify a content category to get AI descriptions tailored to the type of content being uploaded.

Using Content Categories

python
from scopix import Scopix, ContentCategory
async with Scopix(api_key="scopix_...") as client:
# Upload a blueprint with tailored analysis
result = await client.files.upload(
"blueprint.jpg",
content_category=ContentCategory.BLUEPRINT
)
print(result.description) # Blueprint-specific description
# Also works with string values
result = await client.files.upload(
"living-room.jpg",
content_category="architectural_design"
)

Available Categories

  • GENERAL — Default, no specialization
  • BLUEPRINT — Architectural / engineering blueprints
  • CE_PLAN — Civil engineering plans (extracts sheet type, standards, jurisdiction)
  • TECHNICAL_DIAGRAM — Technical diagrams and schematics
  • ARCHITECTURAL_DESIGN — Architectural design photos and renders (extracts style, room type, materials)
  • PRODUCT_PHOTO — Product photography
  • REAL_ESTATE — Real estate and property photos
  • MINING — Mining operations and geological surveys
  • ROBOTICS — Robotics scenes for ML dataset curation (extracts object affordances, grasp strategies, spatial relationships, perception challenges)
  • ARTWORK — Art, paintings, illustrations
  • SCREENSHOT — Screenshots and UI captures
  • DOCUMENT — Scanned documents and forms
  • MAP — Maps, diagrams, and charts (extracts legend data)
  • CONSTRUCTION — Construction sites and active building projects (extracts safety, equipment, materials, structural elements)

Progress Callbacks

SDK only

Track Upload Progress

Callbacks are silent no-ops in v2.0.0

on_progress and on_file_complete are accepted for signature compatibility but are not invoked in v2.0.0. For batched progress, poll session status via client.files.wait_for_session(session_id, on_progress=...) instead.

python
from scopix import Scopix
# NOTE: on_progress / on_file_complete are silent no-ops in v2.0.0.
# Use client.files.wait_for_session(session_id, on_progress=...) for
# batched progress tracking.
async with Scopix(api_key="scopix_...") as client:
results = await client.files.upload_batch(
["img1.jpg", "img2.jpg", "img3.jpg"],
)
for r in results:
print(f"{r.filename}: {r.image_id}")

Custom Storage (BYOB)

Upload to your own storage bucket instead of Scopix's default storage

python
# First, configure your storage bucket (one-time setup)
await client.settings.configure_custom_s3(
access_key_id="AKIAIOSFODNN7EXAMPLE",
secret_access_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
bucket_name="my-company-uploads",
region="us-east-1"
)
# Upload to your custom bucket
result = await client.files.upload_batch(
"photo.jpg",
storage_target="custom" # Use your configured bucket
)

Error Handling

Handle Upload Errors

python
from scopix import Scopix, DescriptionStatus
results = await client.files.upload_batch(files)
# Check status using enum
for r in results:
if r.description_status == DescriptionStatus.FAILED:
print(f"Failed: {r.image_id}")
# Use convenience methods
if results.has_failures:
print(f"Summary: {results.summary()}")
for r in results.retryable():
print(f"Can retry: {r.image_id}")

UploadResult Type

Fields available on the SDK upload result

python
@dataclass(frozen=True)
class UploadResult:
# Core fields
image_id: str # Unique identifier
filename: str # Original filename
# AI description fields
description: Optional[str] # AI-generated description
tags: Optional[list[str]] # Extracted tags
visible_text: Optional[str] # OCR extracted text
text_regions: Optional[list[dict[str, Any]]] # Structured text regions (normalized 0-1 bboxes)
confidence_score: Optional[float] # Always null for descriptions; populated for extractions/rules
description_status: DescriptionStatus # PENDING | QUEUED | PROCESSING | COMPLETED | FAILED | SKIPPED
# Media fields
thumbnail_url: Optional[str]
created_at: Optional[datetime]
# Error fields (when description_status == DescriptionStatus.FAILED)
description_error: Optional[str]
description_error_type: Optional[str]
description_is_retryable: Optional[bool]
# Presigned / multipart fields (None on streaming uploads)
deduplicated: Optional[bool] # True if server matched an existing hash
media_type: Optional[str] # 'image' | 'document' | 'video' | 'link'
status: Optional[str] # 'processing' | 'completed' — upload status
# Properties
@property
def is_failed(self) -> bool
@property
def is_completed(self) -> bool
@property
def is_pending(self) -> bool
# Serialization
def to_dict(self, exclude_none: bool = True) -> dict[str, Any]