Python client for the Pure Frame API — search any moment inside your videos with natural language
Project description
Pureframe Python API library
Pureframe Python library provides convenient access to the Pureframe REST API from any Python 3.9+ application. The library includes type definitions for all request params and response fields, and offers both synchronous and asynchronous clients powered by httpx.
It is generated from our OpenAPI specification with Fern. The import name is pureframe; the PyPI distribution is pureframe-ai.
Documentation
The REST API documentation can be found on docs.pureframe.ai.
Installation
# install from PyPI
pip install pureframe-ai
Usage
The primary workflow is: create a collection, upload videos to it, then search across indexed video segments with natural language, an image, or both.
from pureframe import Pureframe
client = Pureframe(
token="pf_...",
)
collection = client.collections.create_collection(name="Product demos")
results = client.search.search(
query="pricing objection",
collection_id=collection.data.id,
)
for result in results.data:
print(result.filename, [segment.timestamp_start for segment in result.segments])
While you can provide a token keyword argument,
we recommend using python-dotenv
to store your API key in a .env file and reading it with os.environ.get(...)
so that your API key is not stored in source control.
import os
from pureframe import Pureframe
client = Pureframe(
token=os.environ.get("PUREFRAME_API_KEY"),
)
Uploading videos
Upload a video to a collection for indexing. Processing happens asynchronously — use the returned job_id to poll the job until it completes.
from pathlib import Path
from pureframe import Pureframe
client = Pureframe(token="pf_...")
upload = client.upload.upload_video(
collection_id=collection.data.id,
file=Path("input.mp4"),
)
job = client.jobs.get_job(upload.data.job_id)
print(job.data.status) # "queued" -> "processing" -> "done" (or "failed")
Once the job status is done, the video is fully searchable.
Search
At least one of query, image, or image_url is required. Results are grouped by video and include timestamped segments.
from pureframe import Pureframe
client = Pureframe(token="pf_...")
# Text search
results = client.search.search(query="pricing objection")
# Image search, by file
with open("path/to/frame.png", "rb") as image_file:
results = client.search.search(image=image_file)
# Image search, by URL
results = client.search.search(image_url="https://example.com/frame.png")
You can also submit feedback on a search result to help improve ranking:
client.search.submit_search_feedback(
video_id=results.data[0].video_id,
timestamp_start=results.data[0].segments[0].timestamp_start,
signal="positive",
query="pricing objection",
)
Async usage
Simply import AsyncPureframe instead of Pureframe and use await with each API call:
import os
import asyncio
from pureframe import AsyncPureframe
client = AsyncPureframe(
token=os.environ.get("PUREFRAME_API_KEY"),
)
async def main() -> None:
results = await client.search.search(query="pricing objection")
print(results.data)
asyncio.run(main())
Functionality between the synchronous and asynchronous clients is otherwise identical.
Using types
Responses are Pydantic models which also provide helper methods for things like:
- Serializing back into JSON,
model.model_dump_json()(ormodel.json()on Pydantic v1) - Converting to a dictionary,
model.model_dump()(ormodel.dict()on Pydantic v1)
Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set python.analysis.typeCheckingMode to basic.
Pagination
List methods in the Pureframe API are paginated via page and per_page parameters. Each list response includes a meta object with pagination details (total, page, per_page).
from pureframe import Pureframe
client = Pureframe(token="pf_...")
all_videos = []
page = 1
while True:
response = client.videos.list_videos(
collection_id=collection.data.id,
page=page,
per_page=50,
)
all_videos.extend(response.data)
if len(all_videos) >= (response.meta.total or 0):
break
page += 1
print(all_videos)
Or, asynchronously:
import asyncio
from pureframe import AsyncPureframe
client = AsyncPureframe(token="pf_...")
async def main() -> None:
all_videos = []
page = 1
while True:
response = await client.videos.list_videos(
collection_id="collection_id",
page=page,
per_page=50,
)
all_videos.extend(response.data)
if len(all_videos) >= (response.meta.total or 0):
break
page += 1
print(all_videos)
asyncio.run(main())
File uploads
Request parameters that correspond to file uploads (video uploads, image search) can be passed as bytes, a PathLike instance, a file-like object, or a tuple of (filename, contents, media type).
from pathlib import Path
from pureframe import Pureframe
client = Pureframe(token="pf_...")
client.upload.upload_video(
collection_id="collection_id",
file=Path("input.mp4"),
)
The async client uses the exact same interface.
Webhooks
Register a webhook endpoint to receive notifications about processing job events instead of polling.
from pureframe import Pureframe
client = Pureframe(token="pf_...")
endpoint = client.webhooks.create_endpoint(
url="https://example.com/webhook",
events=["job.completed", "job.failed"],
)
# Update, list, or delete endpoints
client.webhooks.update_endpoint("we_abc123", is_active=False)
client.webhooks.list_endpoints()
client.webhooks.delete_endpoint("we_abc123")
You can also pass a webhook_url directly when uploading a video, to be notified once that specific job finishes:
client.upload.upload_video(
collection_id="collection_id",
file=Path("input.mp4"),
webhook_url="https://example.com/webhook",
)
AI agent tool-calling
The agent client exposes an OpenAI-compatible tool schema so LLM agents can call Pureframe tools directly.
from pureframe import Pureframe
client = Pureframe(token="pf_...")
schema = client.agent.agent_schema()
result = client.agent.agent_call(
tool="search_videos",
input={"query": "pricing objection"},
)
search_videos results include thumbnail_base64 — a base64-encoded JPEG that vision-capable models (GPT-4o, Claude) can consume directly.
Handling errors
When the API returns a non-success status code (4xx or 5xx response), a pureframe.core.api_error.ApiError is raised, containing status_code and body properties. For 422 validation errors, an UnprocessableEntityError (a subclass of ApiError) is raised with a structured body.
from pureframe import Pureframe
from pureframe.core.api_error import ApiError
from pureframe.errors import UnprocessableEntityError
client = Pureframe(token="pf_...")
try:
client.collections.create_collection(name="Product demos")
except UnprocessableEntityError as e:
print("Validation error:", e.body)
except ApiError as e:
print(e.status_code)
print(e.body)
Retries
Certain requests can be retried by passing max_retries via request_options. Retries use a short exponential backoff. This is off by default (max_retries=0) and is configured per-request rather than on the client:
from pureframe import Pureframe
client = Pureframe(token="pf_...")
client.videos.list_videos(
collection_id="collection_id",
request_options={"max_retries": 3},
)
Timeouts
By default requests time out after 60 seconds. You can configure this globally on the client, or per-request via request_options:
from pureframe import Pureframe
# Configure the default for all requests:
client = Pureframe(
token="pf_...",
timeout=20.0,
)
# Override per-request:
client.videos.list_videos(
collection_id="collection_id",
request_options={"timeout_in_seconds": 5},
)
Advanced
Undocumented request params
If you want to explicitly send an extra param, you can do so via the additional_query_parameters, additional_body_parameters, and additional_headers keys of request_options.
client.videos.list_videos(
collection_id="collection_id",
request_options={
"additional_query_parameters": {"debug": "true"},
},
)
Configuring the HTTP client
You can directly override the httpx client to customize it for your use case, including support for proxies, custom transports, and other advanced functionality.
import httpx
from pureframe import Pureframe
client = Pureframe(
token="pf_...",
base_url="https://api.pureframe.ai",
httpx_client=httpx.Client(
proxy="http://my.test.proxy.example.com",
transport=httpx.HTTPTransport(local_address="0.0.0.0"),
),
)
Requirements
Python 3.9 or higher.
Versioning
This package generally follows SemVer conventions, though certain backwards-incompatible changes may be released as minor versions:
- Changes that only affect static types, without breaking runtime behavior.
- Changes to library internals which are technically public but not intended or documented for external use.
- Changes that we do not expect to impact the vast majority of users in practice.
We are keen for your feedback; please open an issue with questions, bugs, or suggestions.
Contributing
This library is generated from our OpenAPI specification, so most code changes need to happen at the spec level — see the contributing documentation for details on what to file where.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pureframe_ai-1.0.0.tar.gz.
File metadata
- Download URL: pureframe_ai-1.0.0.tar.gz
- Upload date:
- Size: 32.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
12f30052565d51b73be6d3075f35f7039d3e7e94953ae8152ac0a559febf3a68
|
|
| MD5 |
146e5859d94d8b60ab2b72672d18492e
|
|
| BLAKE2b-256 |
04219a5f9a795a918eb067fc7feaee481c2d9a28f8262a94bf7c1055dfd787fc
|
File details
Details for the file pureframe_ai-1.0.0-py3-none-any.whl.
File metadata
- Download URL: pureframe_ai-1.0.0-py3-none-any.whl
- Upload date:
- Size: 64.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
927ebb2b7f2ae463d88777f5606e312a89faa3be1009e73dfe56ac7015895957
|
|
| MD5 |
230024fe920dff1066dd9ccfb6f7e613
|
|
| BLAKE2b-256 |
e9f88d160ad8c83f9cb427f0f98b3cce35a7e884c20920fc96283d76b7d58049
|