Files (search + download)
Files uploaded by your devices through libscadable are queryable here.
Search — GET /v1/files
GET /v1/filesReturns a newest-first metadata array, scoped to the API key's org.
curl -H "Authorization: Bearer scd_live_..." \
"https://api.scadable.com/v1/files?from=$(date -v-1d +%s)&limit=50"Query parameters
| Param | Type | Default | Notes |
|---|---|---|---|
from | unix seconds | now − 7 days | Inclusive. |
to | unix seconds | now + 1 | Inclusive. |
ns | string | — | Filter to one namespace. |
device | string | — | Filter to one device CN (SC-<device_id>). |
sort | uploaded_at / size_bytes | uploaded_at | |
order | asc / desc | desc | |
limit | int | 50 | Max 200. |
offset | int | 0 | For pagination. |
Response
[
{
"file_id": "f_abc123...",
"org_id": "org_...",
"namespace_id": "ns_...",
"device_cn": "SC-a3f8b2c4e6d8",
"filename": "crashdump.bin",
"content_type": "application/octet-stream",
"uploaded_at": 1748678400,
"size_bytes": 524288,
"sha256": "..."
}
]Empty array if nothing matches the filters. No metadata about the bytes themselves — request the file itself for the content.
Filter rules
nsanddeviceare filters within your org, not access controls. A key with no scope can still only see its own org's files.- Range queries are inclusive on both ends.
- Pagination is offset-based because we sort by score in the underlying zset; cursor pagination arrives if a customer hits the offset ceiling at ~10k.
Download — GET /v1/files/{file_id}
GET /v1/files/{file_id}Streams the file's bytes. Standard HTTP — your client downloads it like any other URL.
curl -H "Authorization: Bearer scd_live_..." \
https://api.scadable.com/v1/files/f_abc123 \
--output crashdump.binResponse headers
| Header | Value |
|---|---|
Content-Type | the upload's recorded type (e.g. application/octet-stream) |
Content-Disposition | attachment; filename="<original>" |
Content-Length | full size in bytes (omitted on Range responses, replaced with Content-Range) |
X-File-SHA256 | hex digest computed at upload time |
Accept-Ranges | bytes (so clients know they can resume) |
Range / resumable downloads
# Get the file size (HEAD might be added later; for now do a tiny Range)
curl -I -H "Authorization: Bearer scd_live_..." \
-H "Range: bytes=0-0" \
https://api.scadable.com/v1/files/f_abc123
# → HTTP 206 Partial Content
# Content-Range: bytes 0-0/524288
# Resume from byte 1024
curl -H "Authorization: Bearer scd_live_..." \
-H "Range: bytes=1024-" \
https://api.scadable.com/v1/files/f_abc123 \
--output crashdump.binRange: bytes=start-end and Range: bytes=start- are both supported. Multi-range requests aren't supported in v1 (rare in practice).
Errors
| Status | Why |
|---|---|
401 | Bad / missing / revoked API key. |
404 | File doesn't exist, OR it belongs to a different org. The two are indistinguishable on purpose so a key can't probe file IDs across orgs. |
410 | File metadata exists but the bytes were already evicted (rare; happens if Valkey is reset without re-uploading). |
416 | Range request out of bounds. The response includes Content-Range: bytes */<size>. |
Languages / clients
Anything that does standard HTTP works. A few small examples:
# Python (requests)
import requests
r = requests.get(
"https://api.scadable.com/v1/files/f_abc123",
headers={"Authorization": "Bearer scd_live_..."},
stream=True,
)
with open("out.bin", "wb") as f:
for chunk in r.iter_content(chunk_size=64*1024):
f.write(chunk)// Node (fetch + stream)
const res = await fetch("https://api.scadable.com/v1/files/f_abc123", {
headers: { Authorization: "Bearer scd_live_..." },
});
const buf = await res.arrayBuffer();
fs.writeFileSync("out.bin", Buffer.from(buf));// Go
req, _ := http.NewRequest("GET", "https://api.scadable.com/v1/files/f_abc123", nil)
req.Header.Set("Authorization", "Bearer scd_live_...")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
io.Copy(out, resp.Body)Tips
- Stable identifiers.
file_idis server-assigned, immutable. Use it for archive-keys / dedup, not filenames (filenames can be anything the device chose at upload time). - SHA256 cross-check. The library hashes the file on the device side; the API records the digest. If you compute the SHA256 of what you downloaded and it matches
X-File-SHA256, you can be confident bytes were not tampered with in transit. - Bulk export. No bulk endpoint in v1; iterate with pagination. For large orgs, use
?sort=uploaded_at&order=asc&offset=…to scroll forward stably.
Updated 10 days ago
