Media Upload
5 endpoints from the published OpenAPI import.
Upload Media Image
Upload a single image to Cloudflare R2 (storage_tier='r2').
Validates → transcodes to WebP (5 MB cap) → R2 → content_media row → catalogs into media_assets. 201 + {success, media, asset} where media is the legacy record and asset is the catalog row (null only if the non-blocking catalog write missed — the upload still succeeded).
Try it
Examples
cURL
curl -X POST 'https://spideriq.ai/api/v1/dashboard/media/upload' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"file": "string",
"folder": "/",
"alt_text": "string"
}'Python
import httpx
resp = httpx.post(
"https://spideriq.ai/api/v1/dashboard/media/upload",
headers={"Authorization": "Bearer <token>", "Content-Type": "application/json"},
json={"file": "string", "folder": "/", "alt_text": "string"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/dashboard/media/upload", {
method: "POST",
headers: { "Authorization": "Bearer <token>", "Content-Type": "application/json" },
body: JSON.stringify({"file": "string", "folder": "/", "alt_text": "string"})
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"file": "string", "folder": "/", "alt_text": "string"}`)
req, _ := http.NewRequest("POST", "https://spideriq.ai/api/v1/dashboard/media/upload", 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 |
|---|---|
201 | Successful Response |
422 | Validation Error |
Upload Media Image From Url
Host a remote image URL on Cloudflare R2 (storage_tier='r2') — the agent-callable, JSON-only twin of POST /upload.
Agents reaching the content platform via skill_call can't send multipart, and a generated hero image lives at a provider URL (kie.* etc.) that the cover_image_url allowlist rejects. This fetches the URL SSRF-safely and runs the same validate → WebP-transcode → R2 → content_media → catalog pipeline as /upload; the returned media.r2_url is on our allowlisted host, so it then passes straight into createPost / content_update_page.
201 + {success, media, asset} (same shape as /upload). A bad/unreachable URL or non-image body is a 400; an oversized body (>5 MB) is a 413.
Try it
Examples
cURL
curl -X POST 'https://spideriq.ai/api/v1/dashboard/media/upload-from-url' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"media_url": "string",
"folder": "/",
"alt_text": "string"
}'Python
import httpx
resp = httpx.post(
"https://spideriq.ai/api/v1/dashboard/media/upload-from-url",
headers={"Authorization": "Bearer <token>", "Content-Type": "application/json"},
json={"media_url": "string", "folder": "/", "alt_text": "string"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/dashboard/media/upload-from-url", {
method: "POST",
headers: { "Authorization": "Bearer <token>", "Content-Type": "application/json" },
body: JSON.stringify({"media_url": "string", "folder": "/", "alt_text": "string"})
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"media_url": "string", "folder": "/", "alt_text": "string"}`)
req, _ := http.NewRequest("POST", "https://spideriq.ai/api/v1/dashboard/media/upload-from-url", 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 |
|---|---|
201 | Successful Response |
422 | Validation Error |
Register Media File
Register a NON-IMAGE file on R2 (storage_tier='r2') — press 3.1.
The third media door, and the only one that does not transcode. /upload and the legacy content/media uploader both hard-gate on validate_image() and rewrite everything to WebP, so a media kit's ZIP bundle, SVG/EPS logo variants and PDF brand guide had no way to obtain a content_media row — and therefore no media_id that POST /press/kits/{id}/assets accepts.
The bytes decide the type: the filename extension and the multipart Content-Type are both ignored (audit §12). Raster images are refused here with a 400 pointing at /upload, so nothing skips image validation. Archives are stored and served verbatim — never expanded or inspected server-side.
201 + {success, media, asset}, the same shape as /upload. 400 for an unsupported or image type, 413 over the cap (100 MB for ZIP, 20 MB otherwise).
Try it
Examples
cURL
curl -X POST 'https://spideriq.ai/api/v1/dashboard/media/upload-file' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"file": "string",
"folder": "press-kits",
"alt_text": "string"
}'Python
import httpx
resp = httpx.post(
"https://spideriq.ai/api/v1/dashboard/media/upload-file",
headers={"Authorization": "Bearer <token>", "Content-Type": "application/json"},
json={"file": "string", "folder": "press-kits", "alt_text": "string"},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/dashboard/media/upload-file", {
method: "POST",
headers: { "Authorization": "Bearer <token>", "Content-Type": "application/json" },
body: JSON.stringify({"file": "string", "folder": "press-kits", "alt_text": "string"})
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"file": "string", "folder": "press-kits", "alt_text": "string"}`)
req, _ := http.NewRequest("POST", "https://spideriq.ai/api/v1/dashboard/media/upload-file", 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 |
|---|---|
201 | Successful Response |
422 | Validation Error |
Upload Media Batch
Upload one or many files to SeaweedFS (storage_tier='seaweedfs').
Per-file (20 MB / 500 MB video) + batch (500 MB) weight caps and an extension allowlist run BEFORE any upload — a hard violation is a 413 and nothing uploads. Each file catalogs on success. 201 + {success, uploaded, failed, totals} (success is true only when nothing failed).
Try it
Examples
cURL
curl -X POST 'https://spideriq.ai/api/v1/dashboard/media/upload-batch' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"files": [
"string"
],
"folder": "string",
"preserve_filename": false
}'Python
import httpx
resp = httpx.post(
"https://spideriq.ai/api/v1/dashboard/media/upload-batch",
headers={"Authorization": "Bearer <token>", "Content-Type": "application/json"},
json={"files": ["string"], "folder": "string", "preserve_filename": false},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/dashboard/media/upload-batch", {
method: "POST",
headers: { "Authorization": "Bearer <token>", "Content-Type": "application/json" },
body: JSON.stringify({"files": ["string"], "folder": "string", "preserve_filename": false})
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"files": ["string"], "folder": "string", "preserve_filename": false}`)
req, _ := http.NewRequest("POST", "https://spideriq.ai/api/v1/dashboard/media/upload-batch", 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 |
|---|---|
201 | Successful Response |
422 | Validation Error |
Import Media Video
Import a remote video into the tenant's PeerTube channel (storage_tier='peertube').
LONG-RUNNING (encode takes minutes) — this does NOT block-wait the encode. The import is accepted (uuid minted, status processing, catalog row written) and returns 202 immediately; the client polls the existing status path (GET /api/v1/media/videos/{uuid}/status) for completion.
202 + {success, peertube_uuid, video_id, status, watch_url, embed_url, player_url, message}. 502 if the upstream import service rejects the kick-off.
Try it
Examples
cURL
curl -X POST 'https://spideriq.ai/api/v1/dashboard/media/import-video' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"url": "string",
"title": "string",
"description": "string",
"privacy": 2,
"use_proxy": false
}'Python
import httpx
resp = httpx.post(
"https://spideriq.ai/api/v1/dashboard/media/import-video",
headers={"Authorization": "Bearer <token>", "Content-Type": "application/json"},
json={"url": "string", "title": "string", "description": "string", "privacy": 2, "use_proxy": false},
)
resp.raise_for_status()
print(resp.json())JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/dashboard/media/import-video", {
method: "POST",
headers: { "Authorization": "Bearer <token>", "Content-Type": "application/json" },
body: JSON.stringify({"url": "string", "title": "string", "description": "string", "privacy": 2, "use_proxy": false})
});
const data = await resp.json();
console.log(data);Go
package main
import (
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"url": "string", "title": "string", "description": "string", "privacy": 2, "use_proxy": false}`)
req, _ := http.NewRequest("POST", "https://spideriq.ai/api/v1/dashboard/media/import-video", 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 |
|---|---|
202 | Successful Response |
422 | Validation Error |