Files
Large-file sharing on the owner's NAS (up to 5 GB, capability links), and the media APIs for captions, video, channel, playlist and web pages.
NAS file sharing
Upload a file once, get a shareable download link. Files are stored on the owner's NAS; metadata in the server database. Auth: key, approved session or trusted local network (require_user_or_local). No credit cost.
Public uploads are capped at about 100 MB by the edge, so large uploads must come from the owner's own network. Downloads through the public link work at any size.
Upload
POST /api/nas/upload (multipart/form-data)
curl -F "file=@lesson.mp4" -F "uploader=Anna" \
-H "x-api-key: $MAXIMIND_API_KEY" https://maximind.dev/api/nas/upload
{"ok": true, "token": "Nv7KF3AXi", "filename": "lesson.mp4", "size_bytes": 3221225472,
"url": "https://maximind.dev/nas/Nv7KF3AXi/lesson.mp4"}
| Name | Type | Required | Description |
|---|---|---|---|
file |
file | Yes | Hard cap 5 GB; over returns HTTP 413 File too large and the partial upload is deleted. |
uploader |
string | No | Display name attributed to the upload. Default: the caller's email. |
token is a random capability id; url is the shareable link. NAS not configured: HTTP 503. NAS write failure: HTTP 502.
Download
GET /nas/{token}/{filename}
No auth: anyone with the link can download. Streams with HTTP Range support (Accept-Ranges: bytes, 206 with Content-Range for partial requests), so video seeking and resumable downloads work. Content-Disposition: inline with the original filename; Content-Type is the stored MIME (default application/octet-stream). Unknown token: 404.
curl -r 0-1048575 https://maximind.dev/nas/Nv7KF3AXi/lesson.mp4 -o part.mp4
List
GET /api/nas/items
curl -s https://maximind.dev/api/nas/items -H "x-api-key: $MAXIMIND_API_KEY"
{"ok": true, "enabled": true, "items": [
{"token": "Nv7KF3AXi", "filename": "lesson.mp4", "size_bytes": 3221225472, "mime_type": "video/mp4",
"uploader_email": "Anna", "created_at": 1781268000.0, "url": "https://maximind.dev/nas/Nv7KF3AXi/lesson.mp4"}]}
enabled reflects whether NAS storage is configured; newest first.
Capacity
GET /api/nas/space
{"ok": true, "total": 8000000000000, "used": 5000000000000, "free": 3000000000000}
All values in bytes. NAS unavailable: {"ok": false} with an optional error.
Delete
DELETE /api/nas/{token}
An admin can delete anything; a non-admin can only delete files whose uploader_email equals their own email (else 403). Removes the record and the file, invalidating the link. Unknown token: 404.
curl -X DELETE https://maximind.dev/api/nas/Nv7KF3AXi -H "x-api-key: $MAXIMIND_API_KEY"
{"ok": true}
Media APIs
A Supadata-compatible drop-in for paid transcript APIs, backed by yt-dlp, local Whisper fallback and trafilatura. Auth: key or session (require_api_key); the key needs the media capability. No local-network bypass. Missing key: 401 {"detail":"Invalid or missing API key"}.
Transcript
GET /v1/transcript (also POST /v1/transcript with a JSON body, and alias GET /v1/youtube/transcript)
Platform captions first (1 credit); local Whisper only when needed (about 1 credit per 30 s).
curl "https://maximind.dev/v1/transcript?url=arj7oStGLkU&lang=en&mode=native" \
-H "x-api-key: $MAXIMIND_API_KEY"
{"lang": "en", "availableLangs": ["aa", "ab", "af", "..."],
"content": [{"text": "So in college,", "offset": 12645, "duration": 1370, "lang": "en"}],
"source": "native"}
| Name | Type | Required | Description |
|---|---|---|---|
url |
string | Yes | Full video link (YouTube, TikTok, Instagram, X, anything yt-dlp supports) or a bare 11-character YouTube id. Other input: 422. |
lang |
string | No | Preferred caption language (en, vi). auto or empty auto-detects; falls back to the video language, then en, then the first available. |
text |
boolean | No | false (default): content is an array of {text, offset, duration, lang} in milliseconds. true: one joined string. |
mode |
string | No | auto (default), native (captions only; 404 if none), generate (force Whisper). Other: 422. |
source is native or generated. For YouTube, availableLangs includes the auto-translate targets and can exceed 100 codes. YouTube's anti-bot gate is handled best-effort with a signed-in browser profile on the server; when it wins, the error says so and GET /health reports youtube_login.
Async media transcode
Submit POST /v1/media/transcode to convert a presigned source into presigned S3 destinations, then poll GET /v1/media/transcode/{job_id}. Use the same base URL and x-api-key: $MAXIMIND_API_KEY as /v1/transcript (including https://maximind.macsvff.uk where configured). The require_api_key dependency enforces the key's media capability; primary keys/admins and legacy unrestricted keys retain access. Restricted keys without media receive 403 "this API key is not allowed to use the media APIs".
{
"kind": "video",
"preset": "intro-720p",
"source_url": "https://legobit-media.s3.ap-southeast-1.amazonaws.com/raw.mov?PRESIGNED_GET",
"outputs": {
"main": {"put_url": "https://legobit-media.s3.ap-southeast-1.amazonaws.com/intro.mp4?PRESIGNED_PUT", "content_type": "video/mp4"},
"poster": {"put_url": "https://legobit-media.s3.ap-southeast-1.amazonaws.com/poster.jpg?PRESIGNED_PUT", "content_type": "image/jpeg"}
},
"client": "vietlingo",
"ref": "vl-mtj-123"
}
All fields shown are required for video. Audio uses kind: "audio", preset: "speech-64k", main.content_type: "audio/mp4", and omits poster. Unsupported kinds, mismatched presets/content types or invalid URLs return 422. Keep source and destination signatures valid for at least 3 hours, including queue time. MaxiMind needs no AWS credentials.
202 response: {"job_id":"…","status":"queued"}. Repeating the same (client, ref) while queued, running or done returns 202 with the existing job_id and its current status. A failed job may be resubmitted; use refreshed URLs and preferably a new ref suffix.
Poll with the same auth:
{
"job_id": "…", "ref": "vl-mtj-123", "status": "done", "error": null,
"outputs": {
"main": {"bytes": 1234567, "duration_s": 60.1, "width": 720, "height": 1280},
"poster": {"bytes": 45678}
},
"queued_at": "2026-10-03T10:00:00+00:00",
"started_at": "2026-10-03T10:00:01+00:00",
"finished_at": "2026-10-03T10:00:20+00:00"
}
status is queued|running|done|failed; error is null or a short failure message. Pending/failed jobs have outputs: {}. Unset timestamps are null; timestamps are UTC ISO 8601. Audio main.width and main.height are 0; audio has no poster. Unknown/pruned jobs return 404.
- intro-720p: MP4, fits 1280×720 landscape or 720×1280 portrait, honours rotation, never upscales, even dimensions and at most 30 fps. H.264 High, CRF 23, maxrate 4M/bufsize 8M, yuv420p; AAC 96 kbps/48 kHz when audio exists; faststart. JPEG poster at min(1 s, 10% of duration), width at most 1280, quality 3.
- speech-64k: M4A (
audio/mp4), AAC-LC mono, 64 kbps, 48 kHz, faststart. - Transfers: HTTPS/443 only, host ending
.amazonaws.comor explicitly inALLOWED_MEDIA_HOSTS(initiallymedia.vietlingo.comand the configured Legobit CDNdv4fftrcq5i64.cloudfront.net); all DNS results must be public, including a fresh check when opening a socket. Private, loopback, link-local and Tailscale addresses are refused. Redirects are refused for both GET and PUT. PUT streams the file with the exact supplied Content-Type. Transfer failures, including PUT failures, mark the job failed; signed URLs are never logged. - Limits: 250 MiB (250 × 1024² bytes), checked against Content-Length and during streaming; maximum duration 300 s, checked by ffprobe. Oversize/overlong input becomes
failedwith a clear error. Execution timeout: 20 minutes, including transfers; subprocess groups are killed on timeout/cancellation. - Scheduling/storage: its own single low-priority FIFO worker, separate from Whisper's queue. ffmpeg/ffprobe run behind
taskpolicy -bandnice -n 19. SQLite~/.maximind/media_jobs.dbuses WAL and retains completed jobs at least 7 days; older completed records are pruned. Queued jobs resume after restart; interrupted running jobs fail with"interrupted, please resubmit". Each job's temp directory under~/.maximind/tmp/transcode/is deleted infinally.
Video info
GET /v1/video (alias GET /v1/youtube/video), 1 credit. Parameter url as above.
Returns id, title, description, url, platform, channel{name,id,url}, durationSeconds, viewCount, likeCount, commentCount, uploadDate (YYYYMMDD), thumbnail, tags, availableTranscriptLangs. Missing fields are null.
curl "https://maximind.dev/v1/video?url=arj7oStGLkU" -H "x-api-key: $MAXIMIND_API_KEY"
Channel and playlist
GET /v1/channel and GET /v1/playlist (aliases /v1/youtube/channel/videos, /v1/youtube/playlist/videos), 1 credit each.
| Name | Type | Required | Description |
|---|---|---|---|
url |
string | Yes | Channel: full URL, /channel/ID, /c/name, /user/name or bare @handle. Playlist: a playlist?list=… URL. |
limit |
integer | No | Default 50, clamped to 1 to 200. |
curl "https://maximind.dev/v1/channel?url=@TED&limit=3" -H "x-api-key: $MAXIMIND_API_KEY"
Returns id, title, url, videoCount and videos[] of {id, title, url, durationSeconds, viewCount}; viewCount is often null in flat mode. Errors: 422 empty url; 400 not a channel or playlist; 504 if listing exceeds the 120 s yt-dlp timeout.
Web scrape
GET /v1/web/scrape, 1 credit.
| Name | Type | Required | Description |
|---|---|---|---|
url |
string | Yes | Full http(s) URL. Other: 422. |
markdown |
boolean | No | Default true: Markdown with links and tables. false: plain text. |
timeout |
number | No | Seconds, default 20, clamped 1 to 60. |
curl "https://maximind.dev/v1/web/scrape?url=https://example.com" -H "x-api-key: $MAXIMIND_API_KEY"
{"url": "https://example.com", "title": "Example Domain", "content": "Example Domain\nThis domain is for use in documentation examples…", "wordCount": 19}
Errors: upstream HTTP error returns that status; 502 fetch failure; 415 when the content type is not html, xml or text.
Errors
| Status | Meaning |
|---|---|
| 400 | URL unreadable; not a channel or playlist. |
| 401 | Missing or invalid key. |
| 403 | Deleting another user's NAS file as a non-admin. |
| 404 | No transcript in native mode; unknown NAS token. |
| 413 | NAS upload over 5 GB. |
| 415 | Scrape target is not html, xml or text. |
| 422 | Invalid url, mode or non-http(s) scrape URL. |
| 502 / 503 | NAS write failure / NAS not configured; scrape fetch failure. |
| 504 | Channel or playlist listing exceeded 120 s. |
Limits and costs
- NAS: 5 GB per file; about 100 MB per upload through the public edge; no credits.
- Media: 1 credit per call; Whisper-generated transcripts about 1 credit per 30 s of audio. See Limits.