Nodaro Docs
DocumentationNode ReferenceModelsAI Agents (MCP)DevelopersSelf-hostingResearch
REST API

Uploads

Upload images, video and audio to Nodaro with POST /v1/upload, copy files from a URL, import social videos, trim stored media for free and list your library.

Uploads put your own images, video and audio into Nodaro's storage, so that any node can use them by URL. POST /v1/upload stores one file and returns its URL. Other routes copy a file from a URL, import a video from a social network, cut a stored file for free, or list the media you have stored.

Endpoints

MethodPathWhat it does
POST/v1/uploadUpload one file, as multipart form data.
POST/v1/save-to-storageCopy a file from a URL into your storage, as a job.
POST/v1/download-videoImport a video from a social network or a direct video link.
GET/v1/download-video/progress/:downloadIdThe import's progress, as server-sent events.
POST/v1/video-metadataRead a video's duration, size and title without downloading it.
POST/v1/media/processTrim or crop a stored video or audio file. Free and synchronous.
GET/v1/libraryYour stored media, with a storage summary.

Upload a file

Send the file as multipart form data in the file field. You can also send type, set to image, video or audio.

curl -s -X POST https://app.nodaro.ai/v1/upload \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -F "file=@portrait.png;type=image/png" \
  -F "type=image"
{
  "data": {
    "url": "https://…/portrait.png",
    "assetId": "5d6e7f80-91a2-4b3c-8d4e-5f60718293a4",
    "thumbnailUrl": "https://…/portrait-thumb.webp",
    "category": "image",
    "filename": "portrait.png",
    "mimeType": "image/png",
    "sizeBytes": 1482233,
    "r2Key": "…/portrait.png"
  }
}
// In a browser, file comes from an <input type="file">.
// In Node 20 or newer, build it from bytes:
import { readFile } from 'node:fs/promises'

const file = new File([await readFile('portrait.png')], 'portrait.png', { type: 'image/png' })
const upload = await client.uploads.upload(file)
console.log(upload.url) // pass it as imageUrl, videoUrl or audioUrl
FieldMeaning
urlThe stored file's public URL. Use it in any node.
assetIdThe id of the file in your storage.
thumbnailUrlA thumbnail for images and video, or null for audio.
categoryimage, video or audio, as the server classified the file.
filenameThe display name of the file.
mimeTypeThe file type the server settled on. See below.
sizeBytesThe stored size in bytes.
r2KeyThe file's key in storage.

The CLI has no upload command. To copy a file that is already online into your storage from the terminal, use nodaro media save <url>.

Formats and file types

MediaFormats
ImagePNG, JPEG, WebP
VideoMP4, MOV, WebM
AudioMP3, WAV, M4A and AAC, OGG, WebM, FLAC. Up to 50 MB.

Each media type has its own size limit. The type your client declares does not have to be the standard one, because the server works out the real type before it checks the file:

  • Parameters are removed. audio/webm;codecs=opus becomes audio/webm.
  • Common alternative spellings are mapped. audio/vnd.dlna.adts, which Windows uses for a plain .aac file, becomes audio/aac, image/jpg becomes image/jpeg, and video/mov becomes video/quicktime.
  • An unhelpful type is read from the file name. With application/octet-stream, or no type at all, the extension decides: clip.mp4 is stored as video/mp4.

A type that still matches no accepted format answers 400 validation_error, with the list of accepted formats. The type the server settled on comes back as mimeType, and the file is served with it.

When an upload is refused

StatusCodeMeaning
400validation_errorThe file is not in an accepted format.
413Your storage is full. The SDK throws StorageExceededError, with your limit in limitBytes.
422upload_blockedThe deployment's upload policy refused the file before it was stored. Show the message to your user as it is. Only on deployments with such a policy.

Use a URL instead

Most routes take media by URL, such as imageUrl, videoUrl, audioUrl and referenceImageUrls, so a file that is already online does not need an upload. The URL must be a public http or https address: a URL that points at a private network address, or uses another scheme, is refused.

Copy the file into Nodaro's storage when the URL is temporary, such as a signed link that expires, or when you want the file to stay available:

curl -s -X POST https://app.nodaro.ai/v1/save-to-storage \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"mediaUrl": "https://example.com/clip.mp4", "filename": "clip.mp4", "mediaType": "video"}'
# {"jobId":"…"}  poll the job; its output carries the stored file
const { jobId } = await client.media.saveToStorage({
  mediaUrl: 'https://example.com/clip.mp4',
  filename: 'clip.mp4',
  mediaType: 'video',
})
nodaro media save https://example.com/clip.mp4 --filename clip.mp4 --type video --watch

In a workflow, pass an uploaded file's URL to an Upload Image, Upload Video or Upload Audio node through the run's inputs. The media url fields of input nodes can always be overridden by a run. See Run a workflow with new input values.

Import a video from a social network

POST /v1/download-video imports a video from YouTube, TikTok, Instagram, X or Facebook, or from a direct link to a video file, into your storage. It answers with a downloadId, not a job id:

{ "url": "https://youtu.be/XXXX", "maxHeight": 720, "sectionStartSec": 30, "sectionEndSec": 90 }
  • maxHeight limits the resolution; leave it out for the best available. sectionStartSec and sectionEndSec fetch only that part of the video: send both or neither.
  • A video without a sound track fails, unless you send requireAudio: false.
  • Each account can run 4 imports at once. A fifth answers 429 too_many_downloads.
  • GET /v1/download-video/progress/:downloadId streams the progress as server-sent events, about twice a second: { phase, percent, videoUrl?, thumbnailUrl?, error? }. The stream ends when the import is completed, with the stored videoUrl, or failed, with an error. The progress is kept only for a short time after the import ends, so start reading it right away.
  • The finished video is saved in your library.

To check a video before you import it, POST /v1/video-metadata with { url } reads its duration, size and title without downloading it. From the terminal: nodaro media download <url> --max-height 720 --watch and nodaro media metadata <url>. The other media tools are in Voice and media.

Trim or crop a stored file

POST /v1/media/process cuts or crops a stored video or audio file. It is free and answers at once, without a job:

{
  "sourceUrl": "https://…/interview.mp4",
  "type": "video",
  "trim": { "startTime": 12.5, "endTime": 48 },
  "crop": { "x": 0, "y": 0, "width": 1080, "height": 1080 }
}

The answer is { "data": { url, thumbnailUrl, assetId, sizeBytes, mimeType, metadata } }. format sets the output format: mp4 or webm for video, mp3, wav, m4a or aac for audio. With deleteSource: true, the source file is removed afterwards when it is yours and nothing else uses it, so the cut replaces the original. In the SDK, call client.media.process(input). For trimming inside a workflow, use the Trim Video node, which costs credits.

List your stored media

GET /v1/library returns your stored images, video and audio, with a summary of your storage. Filter it with type (image, video or audio) and page through it with limit and cursor. In the SDK, call client.library.list({ type: 'image' }).

Upload from an AI assistant

The MCP server offers three ways to upload, each for images, audio and video. They all need the assets:write scope.

WayToolsWorks in
In-chat file pickerupload_image_widget, upload_audio_widget, upload_video_widgetClaude.ai on the web. The image picker takes up to 10 files.
Browser upload pagerequest_image_upload, request_audio_upload, request_video_uploadEvery MCP client. The tool returns a page to open in a browser and the file's final public URL.
Presigned upload URLprepare_image_upload, prepare_audio_upload, prepare_video_uploadClients that can run shell commands, such as Claude Code, Cursor or Cline. Send the file with curl -X PUT --data-binary @file. Not on Claude.ai web or Android.

Frequently asked questions

Last updated on

On this page