업로드
POST /v1/upload로 이미지, 동영상, 오디오를 업로드합니다. URL에서 파일 복사, SNS 동영상 가져오기, 저장된 미디어 자르기, 라이브러리 검색, 파일 링크도 다룹니다.
업로드는 내 이미지, 동영상, 오디오를 Nodaro의 저장 공간에 넣어 어떤 노드든 URL로 사용할 수 있게 합니다. POST /v1/upload는 파일 하나를 저장하고 그 URL을 반환합니다. 다른 경로는 URL에서 파일을 복사하거나, SNS에서 동영상을 가져오거나, 저장된 파일을 무료로 자르거나, 저장한 미디어 목록을 보여 줍니다.
엔드포인트
| 메서드 | 경로 | 기능 |
|---|---|---|
POST | /v1/upload | 파일 하나를 multipart 폼 데이터로 업로드합니다. |
POST | /v1/save-to-storage | URL의 파일을 작업으로 내 저장 공간에 복사합니다. |
POST | /v1/download-video | SNS나 동영상 직접 링크에서 동영상을 가져옵니다. |
GET | /v1/download-video/progress/:downloadId | 가져오기 진행률을 서버 전송 이벤트로 보냅니다. |
POST | /v1/video-metadata | 동영상을 다운로드하지 않고 길이, 크기, 제목을 읽습니다. |
POST | /v1/media/process | 저장된 동영상이나 오디오 파일을 자르거나 크롭합니다. 무료이며 동기식입니다. |
GET | /v1/library | 내 미디어 라이브러리를 최신순으로, 검색과 필터와 함께 반환합니다. |
POST | /v1/library/save-generated | 내 파일 하나를 라이브러리에 저장합니다. |
파일 업로드
파일을 file 필드에 담아 multipart 폼 데이터로 보내세요. type을 image, video, 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| 필드 | 의미 |
|---|---|
url | 저장된 파일의 공개 URL입니다. 어떤 노드에서든 사용할 수 있습니다. |
assetId | 내 저장 공간에 있는 파일의 ID입니다. |
thumbnailUrl | 이미지와 동영상의 썸네일입니다. 오디오는 null입니다. |
category | 서버가 분류한 파일 종류로, image, video, audio 중 하나입니다. |
filename | 파일의 표시 이름입니다. |
mimeType | 서버가 최종적으로 정한 파일 형식입니다. 아래를 참고하세요. |
sizeBytes | 저장된 크기(바이트)입니다. |
r2Key | 저장 공간에서 파일의 키입니다. |
CLI에는 업로드 명령이 없습니다. 이미 온라인에 있는 파일을 터미널에서 내 저장 공간으로 복사하려면 nodaro media save <url>을 사용하세요.
지원 형식과 파일 유형
| 미디어 | 형식 |
|---|---|
| 이미지 | PNG, JPEG, WebP |
| 동영상 | MP4, MOV, WebM |
| 오디오 | MP3, WAV, M4A 및 AAC, OGG, WebM, FLAC. 최대 50MB. |
미디어 종류마다 크기 한도가 따로 있습니다. 클라이언트가 선언하는 형식이 표준 형식일 필요는 없습니다. 서버가 파일을 확인하기 전에 실제 형식을 알아내기 때문입니다.
- 파라미터가 제거됩니다.
audio/webm;codecs=opus는audio/webm이 됩니다. - 흔히 쓰는 다른 표기는 표준 형식으로 바뀝니다. Windows가 일반
.aac파일에 쓰는audio/vnd.dlna.adts는audio/aac가 되고,image/jpg는image/jpeg,video/mov는video/quicktime이 됩니다. - 쓸모없는 형식은 파일 이름으로 판단합니다.
application/octet-stream이거나 형식이 아예 없으면 확장자가 결정합니다.clip.mp4는video/mp4로 저장됩니다.
그래도 허용된 형식과 맞지 않으면 허용 형식 목록과 함께 400 validation_error로 응답합니다. 서버가 최종적으로 정한 형식은 mimeType으로 반환되며, 파일도 이 형식으로 제공됩니다.
MP4와 MOV 동영상은 색인을 앞에 두고 저장됩니다. OBS나 휴대폰으로 녹화한 파일은 이 색인을 파일 끝에 두는 경우가 많아서, 큰 파일의 첫 탐색이 느립니다. 모든 동영상 업로드 경로는 이런 파일을 저장하기 전에 한 번, 스트림을 새 컨테이너로 복사해 다시 씁니다. 다시 인코딩하지 않으므로 화면과 소리는 똑같습니다. 이미 색인이 앞에 있는 파일이나 다시 쓴 결과를 검증할 수 없는 파일은 보낸 그대로 저장됩니다. 다시 쓴 파일은 새로 감싸지므로, 저장된 크기가 보낸 바이트 수와 조금 다를 수 있습니다. sizeBytes는 항상 저장된 파일의 길이이므로, 로컬 파일 크기나 체크섬과 비교하지 마세요.
업로드가 거부되는 경우
| 상태 | 코드 | 의미 |
|---|---|---|
| 400 | validation_error | 파일이 허용된 형식이 아닙니다. |
| 413 | 저장 공간이 가득 찼습니다. SDK는 limitBytes에 한도를 담은 StorageExceededError를 발생시킵니다. | |
| 422 | upload_blocked | 배포 환경의 업로드 정책이 파일을 저장하기 전에 거부했습니다. message를 그대로 사용자에게 보여 주세요. 이런 정책이 있는 배포 환경에서만 발생합니다. |
대신 URL 사용
대부분의 경로는 imageUrl, videoUrl, audioUrl, referenceImageUrls처럼 미디어를 URL로 받으므로, 이미 온라인에 있는 파일은 업로드할 필요가 없습니다. URL은 공개 http 또는 https 주소여야 합니다. 사설 네트워크 주소를 가리키거나 다른 스킴을 쓰는 URL은 거부됩니다.
만료되는 서명 링크처럼 URL이 임시일 때나 파일을 계속 쓸 수 있게 두고 싶을 때는 파일을 Nodaro의 저장 공간에 복사하세요.
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 fileconst { 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워크플로에서는 업로드한 파일의 URL을 실행 입력으로 이미지 업로드, 동영상 업로드, 오디오 업로드 노드에 전달하세요. 입력 노드의 미디어 url 필드는 실행에서 언제든 덮어쓸 수 있습니다. 새 입력 값으로 워크플로 실행을 참고하세요.
SNS에서 동영상 가져오기
POST /v1/download-video는 YouTube, TikTok, Instagram, X, Facebook의 동영상이나 동영상 파일 직접 링크를 내 저장 공간으로 가져옵니다. 응답은 작업 ID가 아니라 downloadId입니다.
{ "url": "https://youtu.be/XXXX", "maxHeight": 720, "sectionStartSec": 30, "sectionEndSec": 90 }maxHeight는 해상도를 제한합니다. 생략하면 가능한 최고 해상도로 가져옵니다.sectionStartSec과sectionEndSec은 동영상의 해당 부분만 가져오며, 컷이 키프레임에 맞춰지기 때문에 앞뒤로 약 3초씩 더 포함됩니다. 둘 다 보내거나 둘 다 생략하세요. 정확하게 잘라야 하면 결과를 자르세요.- 사운드 트랙이 없는 동영상은 실패합니다.
requireAudio: false를 보내면 예외입니다. - 계정마다 가져오기를 동시에 4개까지 실행할 수 있습니다. 다섯 번째는
429 too_many_downloads로 응답합니다. GET /v1/download-video/progress/:downloadId는 진행률을 서버 전송 이벤트로 1초에 약 두 번 스트리밍합니다:{ phase, percent, videoUrl?, thumbnailUrl?, error? }. 가져오기가completed가 되면 저장된videoUrl과 함께,failed가 되면error와 함께 스트림이 끝납니다. 진행률은 가져오기가 끝난 뒤 잠시만 보관되므로 바로 읽기 시작하세요.- 완성된 동영상은 내 라이브러리에 저장됩니다.
가져오기 전에 동영상을 확인하려면 { url }로 POST /v1/video-metadata를 호출하세요. 다운로드하지 않고 길이, 크기, 제목을 읽습니다. 터미널에서는 nodaro media download <url> --max-height 720 --watch와 nodaro media metadata <url>을 사용하세요. 다른 미디어 도구는 보이스 및 미디어에 있습니다.
저장된 파일 자르기 또는 크롭
POST /v1/media/process는 저장된 동영상이나 오디오 파일을 자르거나 크롭합니다. 무료이며 작업 없이 바로 응답합니다.
{
"sourceUrl": "https://…/interview.mp4",
"type": "video",
"trim": { "startTime": 12.5, "endTime": 48 },
"crop": { "x": 0, "y": 0, "width": 1080, "height": 1080 }
}응답은 { "data": { url, thumbnailUrl, assetId, sizeBytes, mimeType, metadata } }입니다. format은 출력 형식을 정합니다. 동영상은 mp4 또는 webm, 오디오는 mp3, wav, m4a, aac입니다. deleteSource: true를 보내면 원본 파일이 내 것이고 다른 곳에서 쓰지 않을 때 작업 후 원본을 삭제하므로, 잘라 낸 파일이 원본을 대신합니다. SDK에서는 client.media.process(input)을 호출하세요. 워크플로 안에서 자르려면 크레딧이 드는 동영상 자르기 노드를 사용하세요.
내 미디어 라이브러리
GET /v1/library는 내 미디어를 최신순으로 나열합니다. 기본적으로 내가 라이브러리에 저장한 파일과 공유 라이브러리를 나열합니다. owned=true를 주면 대신 내가 소유한 모든 파일을 나열합니다. 저장 여부와 관계없이 업로드와 생성 결과가 모두 포함됩니다.
| 쿼리 | 의미 |
|---|---|
type | all(기본값), image, video, audio. |
q | 검색: 파일 이름에 q의 단어가 모두 들어 있는 파일만 가져오며, 최대 5단어까지이고 대소문자는 구분하지 않습니다. 모든 문자는 그 문자 그대로 비교하므로 %, _, *는 와일드카드가 아닙니다. 빈 q는 검색하지 않으며, q는 최대 200자까지 받습니다. 이전 이름인 search도 계속 작동합니다. |
owned | true이면 라이브러리 보기 대신 내가 소유한 모든 파일을 나열합니다. |
source | 한 곳에서 온 미디어만: internal, mcp, app, cli, sdk, extension, web, api. 이 값이 기록되기 전의 파일에는 출처가 없으므로, source를 생략했을 때만 나타납니다. |
limit | 페이지 크기, 1~100. 기본값은 40입니다. |
cursor | 이전 페이지의 nextCursor. |
응답은 { data, nextCursor, totalCount }입니다. 각 항목에는 id, type, filename, mimeType, sizeBytes, url, thumbnailUrl, metadata, isLibraryItem, uploadSource, source, sourceDetail, jobId, createdAt이 담깁니다. jobId는 파일을 만든 작업이며, 업로드라면 null입니다. nextCursor는 마지막 페이지에서 null이고, 일치하는 파일 수인 totalCount는 첫 페이지에만 담깁니다.
curl -s "https://app.nodaro.ai/v1/library?owned=true&type=image&q=poster" \
-H "Authorization: Bearer $NODARO_API_KEY"SDK에서는 client.library.list({ type: 'image', q: 'poster' })를 호출하세요. MCP 도구 browse_uploads는 owned=true 보기를 읽으며, 같은 파일 이름 검색을 search 인수로 받습니다.
{ url, type }과 함께 POST /v1/library/save-generated를 보내면 파일을 내 라이브러리에 저장합니다. Nodaro 저장소에 있는 파일은 내 파일일 때만 받습니다. 내 생성 결과, 내가 업로드한 파일, 내 업로드 폴더의 파일입니다. Nodaro 저장소의 다른 파일은 존재 여부와 관계없이 403 forbidden으로 응답합니다. 다른 사이트의 링크는 받은 그대로 저장됩니다.
사용자를 파일 하나로 연결
로그인한 사용자를 앱의 파일 라이브러리인 내 파일의 파일 하나로 보내려면, 두 쿼리 매개변수 중 하나를 붙여 /library로 링크하세요.
| 링크 | 여는 곳 |
|---|---|
https://app.nodaro.ai/library?job=<jobId> | 그 작업이 만든 파일: 생성 호출이 반환한 jobId입니다. |
https://app.nodaro.ai/library?asset=<assetId> | 그 파일: GET /v1/library?owned=true의 id입니다. owned=true가 없으면 목록에 내 파일에는 절대 표시되지 않는 공유 라이브러리 항목도 들어 있습니다. |
- 페이지는 먼저 찾은 다음 파일을 보여 줍니다. “파일을 찾는 중…”을 표시한 뒤, 파일 위치로 스크롤하고 표시한 다음 미리 보기를 엽니다.
- 사용자의 최신 파일 약 1,000개 안에서 찾습니다. 더 오래된 파일의 링크는 그 파일 없이 내 파일을 엽니다.
- 파일을 보여 줄 수 없으면 짧은 메시지가 이유를 알려 주고, 페이지는 평소처럼 열립니다. id를 알 수 없거나 사용자의 것이 아닐 때, 작업이 아직 실행 중일 때, 작업이 실패했거나 파일을 만들지 않았을 때입니다. 찾기 자체가 실패하면 메시지가 링크를 다시 시도해 달라고 안내합니다.
- 매개변수는 읽은 뒤 주소에서 제거되므로, 새로 고침이나 뒤로를 눌러도 파일이 다시 열리지 않습니다.
- 로그인하지 않은 사용자는 먼저 로그인한 다음 파일로 이동합니다. 셀프 호스팅 설치는 자체 주소에서 같은 경로에 응답합니다.
AI 어시스턴트에서 업로드
MCP 서버는 업로드 방법 세 가지를 제공하며, 각각 이미지, 오디오, 동영상용이 있습니다. 모두 assets:write 스코프가 필요합니다.
| 방법 | 도구 | 사용 가능한 곳 |
|---|---|---|
| 채팅 안 파일 선택기 | upload_image_widget, upload_audio_widget, upload_video_widget | 웹의 Claude.ai. 이미지 선택기는 파일을 최대 10개까지 받습니다. |
| 브라우저 업로드 페이지 | request_image_upload, request_audio_upload, request_video_upload | 모든 MCP 클라이언트. 도구가 브라우저에서 열 페이지와 파일의 최종 공개 URL을 반환합니다. |
| 미리 서명된 업로드 URL | prepare_image_upload, prepare_audio_upload, prepare_video_upload | Claude Code, Cursor, Cline처럼 셸 명령을 실행할 수 있는 클라이언트. curl -X PUT --data-binary @file로 파일을 보내세요. 웹이나 Android의 Claude.ai에서는 쓸 수 없습니다. |
자주 묻는 질문
API로 Nodaro에 파일을 업로드하려면 어떻게 하나요?
file 필드에 파일을 담아 POST /v1/upload를 multipart 폼 데이터로 보내세요. 응답에는 저장된 파일의 url이 들어 있으며, 이 값을 imageUrl, videoUrl, audioUrl로 어떤 노드에든 전달할 수 있습니다.
어떤 파일 형식을 업로드할 수 있나요?
이미지는 PNG, JPEG, WebP, 동영상은 MP4, MOV, WebM, 오디오는 MP3, WAV, M4A 또는 AAC, OGG, WebM, FLAC입니다. 오디오 파일은 최대 50MB까지입니다.
노드가 파일을 쓰려면 먼저 업로드해야 하나요?
아니요. 대부분의 경로는 공개 http 또는 https URL을 직접 받습니다. URL이 비공개이거나 임시일 때, 또는 Nodaro에 자체 사본을 남기고 싶을 때 파일을 업로드하거나 POST /v1/save-to-storage로 복사하세요.
저장 공간이 가득 차면 어떻게 되나요?
업로드가 413으로 응답하고, SDK는 limitBytes에 한도를 담은 StorageExceededError를 발생시킵니다. 더 이상 필요 없는 파일을 삭제하거나 공간을 확보한 다음 다시 업로드하세요.
사용자를 Nodaro 라이브러리의 파일 하나로 연결하려면 어떻게 하나요?
https://app.nodaro.ai/library?job= 뒤에 작업 id를 붙이거나, ?asset= 뒤에 GET /v1/library?owned=true의 id를 붙여 링크하세요. 페이지가 파일을 찾아 그 위치로 스크롤하고 미리 보기를 엽니다.
AI 어시스턴트는 MCP로 파일을 어떻게 업로드하나요?
MCP 서버는 채팅 안의 파일 선택기, 브라우저 업로드 페이지, 셸 명령을 실행할 수 있는 클라이언트를 위한 미리 서명된 업로드 URL의 세 가지 방법을 제공합니다. 각 방법은 이미지, 오디오, 동영상용으로 따로 있습니다.
관련 문서
노드
보이스 및 미디어
이미지 업로드
워크플로
MCP 도구 레퍼런스
최종 업데이트