Media Library
7 endpoints from the published OpenAPI import.
List Catalog Assets
List the tenant's media assets, newest first (soft-delete aware).
Filters are AND-combined. The effective client_id is resolved by require_content_scoped_user_or_api_client (session user or API client; header/brand/super-admin selection). Use ?format=yaml or ?format=md for agent-friendly responses.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
kind | query | any | false | Filter by asset class: image | video | doc |
folder | query | any | false | Filter by exact DAM folder path |
tags | query | any | false | Filter to assets carrying ALL given tags |
status | query | any | false | Filter by status: pending | processing | ready | failed |
storage_tier | query | any | false | Filter by storage tier: seaweedfs | r2 | peertube |
limit | query | integer | false | Page size (1-500) |
offset | query | integer | false | Pagination offset |
format | query | any | false | Response format. json (default) and llm return JSON; yaml returns text/yaml; md returns text/markdown. Any other value is a 422. |
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/dashboard/media/catalog/assets' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/dashboard/media/catalog/assets",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/dashboard/media/catalog/assets", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/dashboard/media/catalog/assets", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Count Catalog Assets
Aggregate counts over the tenant's WHOLE catalog (soft-delete aware).
Returns {total, by_kind, by_tier} — the TRUE totals the Library's filter chips need (FX2a). The dashboard previously computed chip counts client-side over a newest-200 page, so anything past the first page was uncounted (the "Images 0" bug when the newest 200 rows were all videos). This server-side surface counts every matching row in one GROUPING SETS pass.
Filters are AND-combined and mirror the list endpoint exactly, so the counts honour the same filter the grid is paging under. The effective client_id is resolved by require_content_scoped_user_or_api_client.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
kind | query | any | false | Filter by asset class: image | video | doc |
folder | query | any | false | Filter by exact DAM folder path |
tags | query | any | false | Filter to assets carrying ALL given tags |
status | query | any | false | Filter by status: pending | processing | ready | failed |
storage_tier | query | any | false | Filter by storage tier: seaweedfs | r2 | peertube |
format | query | any | false | Response format. json (default) and llm return JSON; yaml returns text/yaml; md returns text/markdown. Any other value is a 422. |
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/dashboard/media/catalog/counts' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/dashboard/media/catalog/counts",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/dashboard/media/catalog/counts", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/dashboard/media/catalog/counts", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Search Catalog Assets
Search the tenant's media assets.
q substring-matches key/folder; tags overlaps (ANY); kind narrows by type. All optional + AND-combined, newest first. Mirrors the 1.1 search surface (metadata containment stays service-only — it needs a typed JSON body; q + tags + kind cover the dashboard discovery path).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
q | query | any | false | Case-insensitive substring match on key/folder |
kind | query | any | false | Filter by asset class: image | video | doc |
tags | query | any | false | Filter to assets sharing ANY given tag |
limit | query | integer | false | Page size (1-500) |
format | query | any | false | Response format. json (default) and llm return JSON; yaml returns text/yaml; md returns text/markdown. Any other value is a 422. |
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/dashboard/media/catalog/search' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/dashboard/media/catalog/search",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/dashboard/media/catalog/search", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/dashboard/media/catalog/search", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Get Catalog Asset
Fetch a single media asset by id. 404 if it does not exist for the tenant.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
asset_id | path | string | true | |
format | query | any | false | Response format. json (default) and llm return JSON; yaml returns text/yaml; md returns text/markdown. Any other value is a 422. |
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/dashboard/media/catalog/assets/{asset_id}' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/dashboard/media/catalog/assets/{asset_id}",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/dashboard/media/catalog/assets/{asset_id}", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/dashboard/media/catalog/assets/{asset_id}", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Update Catalog Asset
Patch a media asset's mutable fields (folder / tags / status / metadata).
True PATCH — only the fields present in the body are written. alt_text and metadata both fold into a shallow JSONB merge (editing alt-text preserves content_hash etc.). 200 + the updated asset; 404 if no live asset with that id exists for the tenant.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
asset_id | path | string | true |
Try it
Examples
cURL
curl -X PATCH 'https://spideriq.ai/api/v1/dashboard/media/catalog/assets/{asset_id}' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"folder": "string",
"tags": [
"string"
],
"status": "string",
"metadata": {},
"alt_text": "string"
}'Python
import httpx
resp = httpx.patch(
"https://spideriq.ai/api/v1/dashboard/media/catalog/assets/{asset_id}",
headers={"Authorization": "Bearer <token>", "Content-Type": "application/json"},
json={"folder": "string", "tags": ["string"], "status": "string", "metadata": {}, "alt_text": "string"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/dashboard/media/catalog/assets/{asset_id}", {
method: "PATCH",
headers: { "Authorization": "Bearer <token>", "Content-Type": "application/json" },
body: JSON.stringify({"folder": "string", "tags": ["string"], "status": "string", "metadata": {}, "alt_text": "string"})
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"folder": "string", "tags": ["string"], "status": "string", "metadata": {}, "alt_text": "string"}`)
req, _ := http.NewRequest("PATCH", "https://spideriq.ai/api/v1/dashboard/media/catalog/assets/{asset_id}", body)
req.Header.Set("Authorization", "Bearer <token>")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |
Delete Catalog Asset
Soft-delete a media asset (deleted_at = now()). 204 on success, 404 if no live asset with that id exists.
For an R2 image (storage_tier='r2') the delete is COMPLETE, not just observational: an R2 image is dual-written (R2 object + legacy content_media row + this catalog row, all sharing one r2_key), so deleting only the catalog row would orphan the other two — leaving the image visible in the legacy page-image picker and the object billing in R2. We mirror the create and clear the matching content_media row + R2 object (best-effort — a cleanup miss never fails the delete; the catalog row is already hidden). SeaweedFS/PeerTube tiers keep the observational-only behaviour (their bytes live on LLM11, which app/ never mutates — Rule 3).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
asset_id | path | string | true |
Try it
Examples
cURL
curl -X DELETE 'https://spideriq.ai/api/v1/dashboard/media/catalog/assets/{asset_id}' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.delete(
"https://spideriq.ai/api/v1/dashboard/media/catalog/assets/{asset_id}",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/dashboard/media/catalog/assets/{asset_id}", {
method: "DELETE",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("DELETE", "https://spideriq.ai/api/v1/dashboard/media/catalog/assets/{asset_id}", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
204 | Successful Response |
422 | Validation Error |
Get Asset Derivative
Resolve (or lazily create + cache) an image derivative for this asset.
On the first request for a given spec the image is derived with Pillow and stored in R2 under a deterministic derivatives/{tenant}/{hash}.{ext} key; subsequent identical requests return the cached URL with cache_hit=true and no work. 422 for a non-image asset or one not on the r2 tier (unsupported_source); 404 if the asset doesn't exist for the tenant. Derivatives are an evictable cache regenerable from the original (Rule 7).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
asset_id | path | string | true | |
w | query | any | false | Target width in px (1-4096) |
h | query | any | false | Target height in px (1-4096) |
fit | query | string | false | cover | contain | fill | crop16x9 | crop1x1 | crop4x3 |
fmt | query | string | false | webp | avif | jpeg | png | auto |
q | query | integer | false | Encode quality 1-100 (png ignores it) |
Try it
Examples
cURL
curl -X GET 'https://spideriq.ai/api/v1/dashboard/media/catalog/assets/{asset_id}/derivative' \
-H 'Authorization: Bearer <token>'Python
import httpx
resp = httpx.get(
"https://spideriq.ai/api/v1/dashboard/media/catalog/assets/{asset_id}/derivative",
headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/dashboard/media/catalog/assets/{asset_id}/derivative", {
method: "GET",
headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/dashboard/media/catalog/assets/{asset_id}/derivative", nil)
req.Header.Set("Authorization", "Bearer <token>")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}Responses
| Status | Description |
|---|---|
200 | Successful Response |
422 | Validation Error |