Remote loading¶
sleap-io can load .slp/.pkg.slp labels and media video directly from
http/https, cloud storage (S3, GCS, Azure), and Google Drive URLs — with
lazy, range-based streaming by default, so only the bytes you actually read are
pulled over the network.
Quick start¶
load_slp and the universal
load_file accept a URL anywhere a local path is
accepted. HTTP/HTTPS works with a base install:
import sleap_io as sio
# http/https works out of the box
labels = sio.load_slp("https://example.com/labels.slp")
# load_file dispatches by extension and also accepts URLs
labels = sio.load_file("https://example.com/labels.slp")
.pkg.slp files with embedded frames work too — the embedded
Video backends reopen the remote file lazily when you read
frames (see Embedded pkg.slp streaming).
When you pass a local path, all of the URL keyword arguments below are no-ops, so the same call works for local and remote files.
What is and isn't supported
URL loading covers .slp/.pkg.slp labels and remote media video over
http/https only. Every other labels format (NWB, COCO, Label Studio,
JABS, DLC, CSV, TrackMate, LEAP, GeoJSON, Ultralytics) raises
NotImplementedError over a URL — fetch it to disk first with
sio.download, then load the local copy.
Command-line interface¶
The sio read commands accept a URL anywhere they accept a local input file —
the input is streamed over the network just like the Python loaders. This works
for show, filenames, convert, split, render, export, fix, embed,
and unembed:
# Inspect a remote labels file
sio show https://example.com/labels.slp
# Convert a remote file to a local output (output paths stay local)
sio convert s3://bucket/labels.slp -o labels.nwb
# The -i/--input option accepts URLs too
sio filenames -i https://example.com/labels.slp
Commands that write a result require a local output path when the input is a
URL. render and fix derive a default output next to the input for local
files, but a URL has no local location to write to, so they require an explicit
local -o/--output (for fix, --dry-run works on a URL without -o). The
embed and unembed commands already require -o, which must be local.
Output paths and commands that re-encode video locally (trim, reencode,
transform, apply-crops) require local filesystem paths.
Downloading files to disk¶
To simply fetch a remote file to local disk — a drop-in replacement for curl
or wget in notebooks and demos — use sio.download. It
accepts the same schemes as the loaders (http/https, cloud, Google Drive). HTTP
and cloud files are streamed straight to disk with a progress bar; Google Drive
files are buffered in memory first (see Google Drive):
import sleap_io as sio
# Into the current directory, using the filename from the URL.
path = sio.download("https://example.com/labels.slp") # -> ./labels.slp
# Into a directory, or to an exact path.
sio.download("s3://my-bucket/run/video.mp4", "data/") # -> data/video.mp4
sio.download("https://example.com/a.slp", "downloads/b.slp") # -> downloads/b.slp
# Fetch-then-load (handy for formats not yet loadable directly over a URL):
labels = sio.load_nwb(sio.download("https://example.com/labels.nwb"))
By default the download is idempotent: if the destination already exists it
is returned without re-downloading, so re-running a cell does not refetch a large
file. Pass overwrite=True to force a fresh download. Authenticated sources work
via headers=, e.g. sio.download(url, headers={"Authorization": "Bearer …"}).
download() vs. stream_mode="download"
sio.download writes a reusable file to disk and
returns its path. The stream_mode="download" option on the loaders instead
reads the bytes into memory for a single load (see
Streaming modes & caching).
On the command line, sio download is the equivalent (and a true curl/wget
replacement):
# Into the current directory (filename from the URL)
sio download https://example.com/labels.slp
# Into a directory, or to an exact path
sio download s3://my-bucket/run/video.mp4 data/
sio download https://example.com/a.slp out.slp
# With an auth header (repeatable) and forced overwrite
sio download https://example.com/a.slp -H 'Authorization: Bearer <token>' -f
Supported schemes & install matrix¶
HTTP/HTTPS needs nothing beyond the base install. Cloud schemes require the
cloud extra, which pulls in the per-provider fsspec adapters:
| Scheme | Requires | Notes |
|---|---|---|
http, https |
nothing extra | Works with a plain pip install sleap-io |
s3 |
sleap-io[cloud] |
Amazon S3 (via s3fs) |
gs, gcs |
sleap-io[cloud] |
Google Cloud Storage (via gcsfs) |
az, abfs |
sleap-io[cloud] |
Azure Blob / ADLS (via adlfs) |
pip install sleap-io # http/https only
pip install "sleap-io[cloud]" # + s3, gs/gcs, az/abfs
pip install "sleap-io[pyav]" # remote media video (provides av)
pip install "sleap-io[all]" # everything (cloud + pyav included)
# Cloud schemes need the [cloud] extra (s3fs / gcsfs / adlfs)
labels = sio.load_slp("s3://my-bucket/path/labels.slp")
labels = sio.load_slp("gs://my-bucket/path/labels.slp")
Missing cloud extra
Using a cloud scheme without the [cloud] extra raises an ImportError
whose message names the missing package and the
pip install 'sleap-io[cloud]' install hint.
Streaming modes & caching¶
The stream_mode keyword argument controls how bytes are fetched:
stream_mode |
Backing strategy | Memory | Disk cache | Revalidation | Best for |
|---|---|---|---|---|---|
"auto" (default) |
fsspec blockcache |
Low (LRU of max_blocks) |
None | n/a | One-off lazy reads, low memory |
"blockcache" |
fsspec blockcache |
Low | None | n/a | Same as auto (explicit) |
"cache" |
fsspec simplecache |
Whole file on disk | Persistent | None | Repeated opens of an immutable file |
"filecache" |
fsspec filecache |
Whole file on disk | Persistent | ETag / Last-Modified after cache_expiry |
Repeated opens of a file that may change |
"download" |
Full read into memory | Whole file in RAM | None | n/a | Small files, ephemeral environments |
# Default: lazy range reads via blockcache, low memory
labels = sio.load_slp("https://example.com/labels.slp")
# Persistent on-disk cache with daily ETag revalidation
labels = sio.load_slp(
"https://example.com/labels.slp",
stream_mode="filecache",
cache_storage="~/.cache/sleap-io",
cache_expiry=86400, # revalidate after a day
)
# Ephemeral full download into memory (no disk cache)
labels = sio.load_slp("https://example.com/labels.slp", stream_mode="download")
The "auto"/"blockcache" reads can be tuned with block_size (range block
size, default 1 MiB) and max_blocks (in-memory LRU cap per open file,
default 32 → ~32 MiB per file). For "filecache", cache_expiry defaults to
3600 seconds (1 hour) when not given.
CI and ephemeral environments
In CI prefer the default stream_mode="auto" (no persistent cache to
manage), or scope a per-run cache to a temporary directory you control:
Clearing the cache¶
For "cache" and "filecache" modes, downloaded files live in the directory
you pass as cache_storage=. To clear them, call
clear_remote_cache with the same
cache_storage you loaded with:
import sleap_io as sio
# Delete every sleap-io cache file in the directory
sio.clear_remote_cache(cache_storage="~/.cache/sleap-io")
# Or only files older than an hour (older_than is in seconds)
sio.clear_remote_cache(cache_storage="~/.cache/sleap-io", older_than=3600)
An explicit cache_storage is required
clear_remote_cache only operates on a
directory that contains the sleap-io marker file, and only deletes files
matching fsspec's cache-key naming pattern — so it never touches unrelated
files even in a shared directory. It refuses to run on a directory with no
marker, or on forbidden paths like / or $HOME. Because fsspec's default
cache directory is a per-process temporary location, you must pass the
explicit cache_storage you used when loading.
Authentication & security¶
Pass HTTP headers (such as a bearer token) with headers=:
labels = sio.load_slp(
"https://my-org.example/private/labels.slp",
headers={"Authorization": "Bearer <token>"},
)
Cloud schemes (s3://, gs://, …) ignore headers= and use their own
per-provider credential chains (environment variables, credential files,
instance metadata).
headers= is for labels, not media video
headers= (and the streaming options below) authenticate and configure
remote .slp/.pkg.slp label loading. They are not supported for
remote media video, which FFmpeg decodes from the URL directly — see
Remote video. For an auth-gated remote video, use a
pre-signed URL instead.
Headers are stripped on cross-origin redirect
For security, sensitive headers (Authorization, Cookie,
Proxy-Authorization) are dropped automatically if a request is
redirected to a different origin (scheme/host/port). This prevents leaking
credentials to a third-party host. If a download redirects cross-origin
(e.g. to a pre-signed CDN URL), put the credentials in the redirect
target's query string rather than in headers.
Other security guarantees: TLS is always on; URLs are redacted (userinfo and
token-like query parameters stripped) in error messages and tracebacks so
credentials never leak into logs; and an identity Accept-Encoding is forced
so range reads stay byte-exact. Remote loading needs aiohttp >= 3.13.5 for
the cross-origin header stripping — an older version emits a RuntimeWarning
at import time.
Embedded pkg.slp streaming¶
A .pkg.slp URL streams its embedded frames lazily: opening the file reads
only metadata, and each embedded image is fetched on demand with a
range-request (blockcache) when you index into the video.
# Embedded frames stream over the network on access
labels = sio.load_slp("https://example.com/project.pkg.slp")
frame = labels.videos[0][0] # range-reads just the bytes for frame 0
This means you can inspect a remote packaged project without downloading the
whole archive — the embedded video backends reopen the remote file using the
same streaming configuration and authentication headers= as the initial load,
so an auth-gated .pkg.slp streams its frames without re-authenticating.
Remote video¶
load_video (and load_file for
video extensions) reads a media video directly from an http/https URL.
Frames are decoded on demand:
# Reads frames lazily over the network; needs the [pyav] extra
video = sio.load_video("https://example.com/video.mp4")
frame = video[0] # decoded on demand
Supported container extensions match local media videos (mp4, avi, mov,
mj2, mkv). Only http/https URLs are accepted — cloud schemes and Google
Drive are not supported for video. The query string and fragment are
ignored for extension detection, so pre-signed URLs like
https://host/video.mp4?token=... route correctly. Remote video requires the
pyav extra (auto-selected as the backend); without it, load_video(url)
raises an ImportError with the install hint.
Auth headers and stream modes do not apply to remote video
Unlike remote .slp/.pkg.slp labels, remote media video is decoded by
handing the URL straight to FFmpeg (via pyav), which has no hook for custom
HTTP request headers or fsspec stream modes. Passing headers=,
url_headers=, stream_mode=, or url_stream_mode= to load_video (or
Video.from_filename) for an http/https media URL raises a ValueError
rather than silently returning an unauthenticated backend. To read an
auth-gated remote video, use a pre-signed URL that embeds credentials in
the query string (e.g. https://host/video.mp4?X-Amz-...), or download the
file locally first.
Security: remote video hands untrusted data to FFmpeg
Decoding a remote video streams bytes from the URL into FFmpeg (via pyav).
FFmpeg's demuxers and decoders are a large, historically
vulnerability-prone attack surface, so a malicious URL or stream can attempt
to exploit the decoder running in your process. sleap-io only passes
http/https URLs through to the decoder (no other schemes), but you
should:
- Load remote video only from sources you trust — treat an arbitrary third-party URL the same as running untrusted code.
- Sandbox untrusted inputs — decode from an untrusted source only in an isolated environment (container/VM with no credentials, restricted network, non-privileged user) and keep FFmpeg/pyav up to date.
Google Drive¶
Google Drive file share links are recognized and resolved to a direct
download, so you can pass a Drive URL straight to
load_slp or load_file:
# Any of these Drive file-share shapes resolve to a direct download:
labels = sio.load_slp("https://drive.google.com/file/d/<FILE_ID>/view")
labels = sio.load_slp("https://drive.google.com/uc?id=<FILE_ID>&export=download")
labels = sio.load_slp("https://drive.google.com/open?id=<FILE_ID>")
# load_file resolves the link, sniffs the bytes to detect the format, and routes
# it. The sniffed bytes are reused, so the file is downloaded only once:
labels = sio.load_file("https://drive.google.com/file/d/<FILE_ID>/view")
The file must be shared as "Anyone with the link" (no sign-in required).
Because Drive download links carry no extension and reject the HEAD/range
requests that lazy streaming relies on, a Drive file is fully downloaded into
memory during resolution — the stream_mode/cache keyword arguments do not
apply. The two-hop confirmation page Drive serves for larger files is handled
transparently, and the resolver only ever follows Google download hosts.
Some limitations:
- Folder links are not supported — pass a single-file share link
(
…/file/d/<FILE_ID>/view), not a…/drive/folders/<ID>URL. A folder link raises aValueError. - Drive videos are not supported — download the video file first, then load it locally.
- Quota / permission errors — if Drive returns its "too many users have
viewed or downloaded this file recently" page, a
RemoteIOErroris raised; retry later or re-check the file's sharing settings. - Large files — the in-memory prefetch is capped (8 GiB by default); a file
exceeding the cap raises a
RemoteIOErrorinstead of exhausting memory.
For load_file, the format is detected from the
downloaded bytes, and those bytes are reused for the load — so a Drive .slp is
downloaded only once whether you call load_slp or load_file (an explicit
format= is optional, and skips the format-detection step).
Error handling¶
Remote HTTP/cloud failures surface as
RemoteIOError (a subclass of OSError). It carries
a status (HTTP code or None) and a credential-redacted url, so tokens
never leak into logs or tracebacks:
import sleap_io as sio
try:
labels = sio.load_slp("https://example.com/labels.slp")
except sio.RemoteIOError as e:
print(e.status) # e.g. 404, 416, 503, or None for connection errors
print(e.url) # redacted URL (tokens/userinfo stripped)
Only transient statuses (429, 500, 502, 503, 504) are retried (with
exponential backoff, honoring an integer Retry-After); the retry count is
controlled by retries= (default 3).
Troubleshooting¶
RemoteIOError— HTTP-level failures (404 not found, 416 range past end of file, 5xx after retries, connection errors, timeouts). Carriesstatusand a redactedurl.ImportErrorfor cloud schemes — install the cloud adapters withpip install 'sleap-io[cloud]'(coverss3,gs/gcs,az/abfs).ImportErrorfromload_video(url)— remote video needs thepyavextra; install withpip install 'sleap-io[pyav]'. Onlyhttp/httpsURLs are supported for video.RuntimeWarningaboutaiohttp— remote loading needsaiohttp >= 3.13.5for safe cross-origin header stripping. Upgrade withpip install --upgrade 'aiohttp>=3.13.5'.ValueErrorfor a URL — an ambiguous-extension URL (.h5/.json/.csv) withsniff=Falseand no explicitformat=, or a Google Drive folder / unparsable link.
See also
load_slp: Full URL keyword-argument referenceload_file: Universal loader with URL sniffingload_video: Loads remote media video over http/httpsclear_remote_cache: Cache cleanup helperRemoteIOError: Remote I/O error surface- SLP Format: The on-disk
.slplayout that URL loading streams