Skip to main content

CreativAI Python SDK

Official Python SDK for the CreativAI Video Intelligence Platform.

Upload, index, search, and extract structured knowledge from video libraries at scale — all from Python.


Installation

pip install creativai

For SSE streaming support (agentic chat, live stream events), httpx-sse is installed automatically as a dependency.

Requires Python 3.9+


Authentication

Get your API key from the CreativAI app (profile avatar → API Key). Keys begin with sk_live_.

import creativai

# Option 1 — pass directly
client = creativai.CreativAI(api_key="sk_live_...")

# Option 2 — environment variable (recommended for production)
# export CREATIVAI_API_KEY="sk_live_..."
client = creativai.CreativAI()

MCP Quick Start — Use CreativAI inside Claude, Cursor, and Copilot

CreativAI supports the Model Context Protocol (MCP), letting any compatible AI assistant call CreativAI tools directly in conversation.

Option A — uvx (runs the Python package without a manual install)

Add to your Claude Desktop / Cursor config (install uv first):

{
  "mcpServers": {
    "creativai": {
      "command": "uvx",
      "args": ["creativai-mcp"],
      "env": { "CREATIVAI_API_KEY": "sk_live_..." }
    }
  }
}

Option B — pip

pip install creativai-mcp
CREATIVAI_API_KEY="sk_live_..." creativai-mcp          # stdio (Claude Desktop)
creativai-mcp --transport streamable-http --host 0.0.0.0 --port 8090   # host it yourself

Option C — from the SDK

import creativai

client = creativai.CreativAI()
server = client.as_mcp_server()   # returns a FastMCP instance
server.run(transport="stdio")     # local use only: it carries this client's one key

Hosted (no install)

Connect a web agent (a claude.ai custom connector, Replit Agent, the Claude API's MCP connector) or any client that speaks MCP over streamable HTTP to https://ws.creativai-apis.com/api/v2/mcp, sending your own key on every request as X-API-Key (or Authorization: Bearer). For example, in Cursor:

{
  "mcpServers": {
    "creativai": {
      "url": "https://ws.creativai-apis.com/api/v2/mcp",
      "headers": { "X-API-Key": "sk_live_..." }
    }
  }
}

The hosted server leaves out the delete_* tools and the blocking helpers index_and_wait / search_and_import.

Available tools (62 total): collections, media, indexing, search, agentic chat, knowledge extraction, data plates, tasks, live stream, online search, YouTube, organizations/projects, account info.

MCP setup page: creativ-ai.com/mcp


Quick Start

import time
import creativai

client = creativai.CreativAI()

# Verify your key and check credits
info = client.users.get_users_info()
print(f"Credits: {info['credits']}")

# Create a collection
collection = client.collections.create("my-dashcam-footage", model="video_only")
cid = collection["collection_id"]

# Upload a local file, then confirm it so the backend starts preprocessing
upload = client.media.upload_file(cid, "dashcam_2026.mp4")
client.media.confirm_upload(cid, [upload["media_id"]])

# Start indexing (async — returns immediately with a job ID)
job = client.indexing.start(cid)
indexing_id = job["indexing_id"]

# Poll until complete
while True:
    status = client.indexing.get_status(indexing_id)
    if status["status"] == "completed":
        break
    time.sleep(10)

# Semantic search
results = client.search.query(cid, "pedestrian crossing the road")
for hit in results["results"][:5]:
    print(f"[{hit['score']:.2f}] {hit['video_name']} @ {hit['start_time']}s")

Resource Reference

All resources are accessed as attributes on the CreativAI client instance.

client.health

client.health.check()        # GET /health
client.health.versioned()    # GET /api/v2/health

client.users

client.users.me()
client.users.info()
client.users.get_users_info()
client.users.claim_welcome_credits()

client.collections

client.collections.create("name", model="video_only")   # model: "video_only" | "multimodal"
client.collections.list()
client.collections.get(collection_id)
client.collections.update(collection_id, collection_name="new-name")
client.collections.delete(collection_id)
client.collections.restore(collection_id)
client.collections.list_by_organization(org_id)
client.collections.list_by_project(org_id, project_name)

client.media

client.media.list(collection_id)
client.media.upload_file(collection_id, "/path/to/video.mp4")  # convenience helper
client.media.get_upload_url(collection_id, "video.mp4")        # get presigned URL
client.media.get_upload_urls(collection_id, ["a.mp4", "b.mp4"])
client.media.confirm_upload(collection_id, [media_id])         # REQUIRED after an upload
client.media.delete(collection_id, ["s3://bucket/key1.mp4"])

Uploading is two steps. A presigned PUT only places bytes in storage — it does not tell the backend a file arrived. Call confirm_upload() with the media_id from the upload-url response to start preprocessing (chunking, thumbnails, vector placeholders); until that runs, indexing.start() has nothing ready to index. Tags must also be declared here, at upload time, because they are stamped onto chunk rows as those rows are created:

upload = client.media.get_upload_url(cid, "line_3.mp4")
requests.put(upload["upload_url"], data=open("line_3.mp4", "rb"),
             headers={"Content-Type": "video/mp4"})
client.media.confirm_upload(cid, [upload["media_id"]], tags={"*": ["line-3", "night-shift"]})

client.uploads — multipart

upload = client.uploads.initiate(collection_id, "large-video.mp4")
client.uploads.complete(upload["upload_id"], parts=[{"part_number": 1, "etag": "..."}])
client.uploads.abort(upload["upload_id"])
client.uploads.regenerate_urls(upload["upload_id"])

client.transfers — external S3 / URL

job = client.transfers.start(collection_id, "s3://my-bucket/video.mp4")
client.transfers.get_status(job["job_id"])
client.transfers.validate("https://example.com/video.mp4")

client.indexing

job = client.indexing.start(collection_id)
client.indexing.get_status(job["indexing_id"])
client.indexing.estimate_cost(collection_id)
client.indexing.get_preprocessing_status(collection_id)
client.indexing.list_preprocessed_videos(collection_id)

client.search

results = client.search.query(
    collection_id,
    "person wearing PPE",
    search_type="hybrid",   # "hybrid" | "vision" | "audio"
    page_number=1,
    page_size=50,
    refine_query=True,
)

client.data_plates

plate_job = client.data_plates.create_from_collection(collection_id, plate_name="All Segments")
plate_id = poll_until_done(client.data_plates.get_creation_job, plate_job["job_id"])["plate_id"]

plate = client.data_plates.get(collection_id, plate_id, page_size=100)
client.data_plates.update(collection_id, plate_id, plate_name="Renamed")
client.data_plates.delete(collection_id, plate_id)

# Segments
client.data_plates.add_segments(collection_id, plate_id, segments=[...])
client.data_plates.remove_segments(collection_id, plate_id, segment_ids=["seg_1"])
client.data_plates.update_extracted_info(collection_id, plate_id, "seg_1", "ppe_worn", True)

# Export
client.data_plates.generate_csv(collection_id, plate_id)
csv_bytes = client.data_plates.export_csv(collection_id, plate_id)

client.knowledge_extraction

ke_job = client.knowledge_extraction.add_columns(
    collection_id,
    plate_id,
    columns=[
        {"name": "ppe_worn", "question": "Is PPE worn?", "type": "boolean"},
        {"name": "activity",  "question": "What is happening?", "type": "text"},
    ],
)
client.knowledge_extraction.get_job(ke_job["job_id"])

# AI chat query over the plate data
answer = client.knowledge_extraction.chat_query(collection_id, plate_id, "How many PPE violations?")
print(answer["answer"])

# Charts
charts = client.knowledge_extraction.get_plate_charts(collection_id, plate_id)

client.agentic_chat — SSE streaming

session = client.agentic_chat.create_session(collection_id, title="My analysis")
sid = session["session_id"]

for event in client.agentic_chat.chat(sid, "Find all forklift incidents and summarize them"):
    match event["event"]:
        case "thinking":
            print(f"  [thinking] {event['data'].get('text', '')[:80]}")
        case "search":
            print(f"  [search] {event['data']}")
        case "answer":
            print(f"\n{event['data'].get('text', '')}")
        case "done":
            break

# Session management
client.agentic_chat.list_sessions(collection_id=collection_id)
client.agentic_chat.get_messages(sid)
client.agentic_chat.stop(sid)
client.agentic_chat.delete_session(sid)

client.live_stream

# RTMP push — point OBS or ffmpeg at publish_url
session = client.live_stream.stream_rtmp(
    collection_id=collection_id,
    name="Entrance Camera",
    model="video_only",
)
print(session["publish_url"])

# RTSP pull — IP camera
session = client.live_stream.stream_rtsp("rtsp://192.168.1.100/stream", collection_id=collection_id)

# WebRTC — browser webcam
session = client.live_stream.stream_webrtc(collection_id=collection_id)
print(session["whip_url"], session["whep_url"])

# Add questions and poll
client.live_stream.add_questions(sid, ["Is anyone present?", "Is the door open?"])
client.live_stream.stop_session(sid)

client.upload_integrations

# Google Drive
files = client.upload_integrations.google_drive_list_files(google_access_token)
client.upload_integrations.google_drive_transfer(
    collection_id, google_access_token,
    file_ids=["drive_file_id"], file_names=["video.mp4"]
)

# Dropbox
files = client.upload_integrations.dropbox_list_files(dropbox_access_token)
client.upload_integrations.dropbox_transfer(
    collection_id, dropbox_access_token,
    file_paths=["/Videos/clip.mp4"], file_names=["clip.mp4"]
)

# Hugging Face
files = client.upload_integrations.huggingface_list_files(hf_token, "username/my-dataset")
client.upload_integrations.huggingface_transfer(
    collection_id, hf_token, "username/my-dataset",
    file_paths=["videos/clip.mp4"]
)

client.organizations / client.projects

org = client.organizations.create("Acme Corp")
client.projects.create(org["org_id"], "production-analysis")
client.projects.list(org["org_id"])

client.sharing

client.sharing.invite(collection_id, "alice@example.com", role="viewer")
client.sharing.list_members(collection_id)
client.sharing.update_member(collection_id, user_id, role="editor")
client.sharing.remove_member(collection_id, user_id)
client.sharing.create_group(collection_id, "annotators")

client.tasks

task = client.tasks.create(collection_id, title="Review batch 1", assigned_to=[user_id])
client.tasks.update_status(task["task_id"], "in_progress")
client.tasks.update_progress(task["task_id"], 50)
client.tasks.add_comment(task["task_id"], "Segment 12 flagged for review")
client.tasks.my_tasks()

client.transactions / client.subscriptions / client.invoices

client.transactions.summary()
client.transactions.breakdown_by_collections()
client.transactions.export()  # CSV bytes

client.subscriptions.current()
client.subscriptions.list_plans()

client.invoices.list()
pdf = client.invoices.download("inv_123")

client.jobs — cancel any async job

client.jobs.cancel("indexing-chunk", "idx_abc123")
client.jobs.cancel("knowledge-extraction", "ke_job_xyz")

Error Handling

import creativai

client = creativai.CreativAI()

try:
    results = client.search.query("col_invalid", "query")
except creativai.NotFoundError as e:
    print(f"Not found: {e.message}")
except creativai.InsufficientCreditsError:
    print("Top up your credits at https://creativ-ai.com/pricing")
except creativai.AuthenticationError:
    print("Check your CREATIVAI_API_KEY")
except creativai.APIError as e:
    print(f"API error {e.status_code}: {e.message} (code={e.code})")
Exception HTTP status
AuthenticationError 401
InsufficientCreditsError 402
PermissionDeniedError 403
NotFoundError 404
ValidationError 400 / 422
RateLimitError 429
ServerError 5xx
StreamingError SSE connection failure
TimeoutError Request timeout

Context Manager

with creativai.CreativAI() as client:
    collections = client.collections.list()
# HTTP connection pool closed automatically

Examples

File Description
examples/quickstart.py Upload, index, search, and agentic chat
examples/knowledge_extraction.py Structured data extraction → CSV
examples/live_stream.py RTMP live stream session

License

MIT

Metadata

Release files for creativai 0.1.7

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for creativai 0.1.7
File Size Uploaded Provenance Actions
creativai-0.1.7.tar.gz 40.3 kB Without provenance Details for creativai-0.1.7.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for creativai 0.1.7
File Interpreter ABI Platform Actions
creativai-0.1.7-py3-none-any.whl Python 3 none any Details for creativai-0.1.7-py3-none-any.whl

Total release size: 77.8 kB

Release files / creativai-0.1.7.tar.gz

Download URL creativai-0.1.7.tar.gz
Size 40.3 kB
Tags Source
SHA-256 checksum
How to use checksums
80768aefd858e19406b2ee83d75cb084adbd8113a865a7a42c165d575c896d94
BLAKE2b-256 checksum
How to use checksums
4df4375a1b52e3ada9c84fed4b222281d4cb88d9af79a8465300ac590e465283
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / creativai-0.1.7-py3-none-any.whl

Download URL creativai-0.1.7-py3-none-any.whl
Size 37.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
27955c3ccde002fa890686dab5cd33f01f963ea4f979c668ae150e5c6b5a394e
BLAKE2b-256 checksum
How to use checksums
5385eeedafb3212608e2c399554af6687714077a0a3e75160f18a53559a904b8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Security for creativai 0.1.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page