upload_media_batch
Upload up to 10 photos in one request.
/v1/media/upload-batchmultipart/form-data with one form field per file and a manifest field: a JSON array with one item per file, naming the form field (file) and the same fields as upload_media ({ file, ownerType, ownerId, caption, credit, alt, ... } or { file, target: "blob" }). Every file counts against the upload limit and succeeds or fails on its own; the response lists each result. The status is 201 when at least one file was stored and 400 when every file failed, with the same body. At most 10 files and 19 MB per request.
curl -X POST "https://sceniq.earth/api/v1/media/upload-batch" \
-H "Authorization: Bearer $SCENIQ_API_KEY" \
-F 'manifest=[{"file":"overlook","ownerType":"spot","ownerId":"k1709e2vj4sk8q6x0d3m5r7t9w1y3b5c","caption":"Grand Prismatic Spring from the overlook, mid morning","credit":"Mara Lindgren","alt":"A deep blue pool ringed with orange and yellow, steam drifting over the boardwalk below","shows":{"kind":"spot"},"takenAt":{"lat":44.5199,"lon":-110.8406,"precision":"gps"}},{"file":"boardwalk","ownerType":"spot","ownerId":"k1709e2vj4sk8q6x0d3m5r7t9w1y3b5c","caption":"Grand Prismatic Spring and Midway Geyser Basin from above","credit":"Brocken Inaglory","licence":"CC BY-SA 3.0","sourcePageUrl":"https://commons.wikimedia.org/wiki/File:Grand_Prismatic_Spring_and_Midway_Geyser_Basin_from_above.jpg","creditUrl":"http://commons.wikimedia.org/wiki/User:Brocken_Inaglory"},{"file":"yellowstone-cover","target":"blob"}]' \
-F "overlook=@overlook.jpg" \
-F "boardwalk=@boardwalk.jpg" \
-F "yellowstone-cover=@yellowstone-cover.jpg"Form fields
- manifestarray of valuesrequired
JSON array, one item per file: { file (the form field name),
ownerType,ownerId, and anyupload_mediafield }, or { file, target: "blob" }. - <file field>stringrequired
One form field per file, named as the manifest item's file.
Returns
201 Created with BatchUpload: The results of upload_media_batch.
- uploadednumberrequired
Files stored.
- failednumberrequired
Files refused.
- resultsarray of objectsrequired
One result per manifest item, in order.
Show 23 fieldsHide fields
- filestring or nullrequired
The form field name from the manifest.
- okbooleanrequired
Whether this file was stored.
- mediaIdstring · photo idoptional
The new photo's id (media uploads).
- storageIdstring · stored file idoptional
The stored file's id (target blob).
- urlstring or nulloptional
The stored photo.
- thumbUrlstring or nulloptional
The thumbnail.
- ownerTypestringoptional
What the photo belongs to.
spotproductcollectionlisting - ownerIdstringoptional
The owner row's id.
- captionstring or nulloptional
Caption.
- creditstring or nulloptional
Photographer credit.
- ordernumberoptional
Position in the owner's gallery.
- contentTypestringoptional
The stored file's type.
- widthnumberoptional
Stored width in pixels.
- heightnumberoptional
Stored height in pixels.
- bytesnumberoptional
Stored size in bytes.
- thumbBytesnumber or nulloptional
The thumbnail's size in bytes.
- originalBytesnumberoptional
The uploaded file's size in bytes.
- optimizedbooleanoptional
false when the original was kept.
- metadataStrippedbooleanoptional
true when metadata was removed.
- capturedOnstring or nulloptional
Capture date.
- usestringoptional
Where a
storageIdgoes. - warningsarray of stringsoptional
Notes about things the write accepted but you should look at (a plain http link). Present only when there are any.
- errorobjectoptional
Why the file was refused.
Show 2 fieldsHide fields
- codestringrequired
The error code (see Errors).
- messagestringrequired
What went wrong.
{
"uploaded": 3,
"failed": 0,
"results": [
{
"file": "overlook",
"ok": true,
"mediaId": "kg71s3d5f7g9h1j3k5k7z9x1c3v5b7n9",
"url": "https://quiet-heron-512.convex.cloud/api/storage/grand-prismatic-overlook.webp",
"thumbUrl": "https://quiet-heron-512.convex.cloud/api/storage/grand-prismatic-overlook-640.webp",
"ownerType": "spot",
"ownerId": "k1709e2vj4sk8q6x0d3m5r7t9w1y3b5c",
"caption": "Grand Prismatic Spring from the overlook, mid morning",
"credit": "Mara Lindgren",
"width": 2048,
"height": 1365,
"order": 1024,
"contentType": "image/webp",
"bytes": 684312,
"thumbBytes": 61240,
"originalBytes": 5412880,
"optimized": true,
"metadataStripped": true,
"capturedOn": "2026-09-14"
},
{
"file": "boardwalk",
"ok": true,
"mediaId": "kg7p2p4j6v8y0t2r4e6w8q0a2s4d6f8g",
"url": "https://quiet-heron-512.convex.cloud/api/storage/grand-prismatic-aerial.webp",
"thumbUrl": "https://quiet-heron-512.convex.cloud/api/storage/grand-prismatic-aerial-640.webp",
"ownerType": "spot",
"ownerId": "k1709e2vj4sk8q6x0d3m5r7t9w1y3b5c",
"caption": "Grand Prismatic Spring and Midway Geyser Basin from above",
"credit": "Brocken Inaglory",
"width": 2048,
"height": 1365,
"order": 2048,
"contentType": "image/webp",
"bytes": 597004,
"thumbBytes": 55872,
"originalBytes": 2811264,
"optimized": true,
"metadataStripped": false,
"capturedOn": null,
"warnings": [
"creditUrl uses http, not https: http://commons.wikimedia.org/wiki/User:Brocken_Inaglory"
]
},
{
"file": "yellowstone-cover",
"ok": true,
"storageId": "kg2a8c4e0g6j2k8m4p0q6s2v8w4y0a6c",
"contentType": "image/webp",
"width": 2048,
"height": 1152,
"bytes": 402118,
"originalBytes": 3170912,
"optimized": true,
"metadataStripped": true,
"use": "Pass storageId as coverStorageId (update_chapter) or photoStorageId (set_chapter_intro sections)"
}
]
}Errors
Every error is { error: { code, message } }. Act on the code.
- 400invalid_argumentA required field is missing, a field is unknown, or a value does not match its type (the Convex validator's path and value are in the message).
- 400invalid_requestA writer refused a value: a map link on a host that is not allowed, a title that is too long, a date that does not exist, too many items in a list. The message is the writer's own sentence.
- 400unsupported_imageAn upload's bytes are not a JPEG, PNG or WebP file, or their dimensions cannot be read. The bytes are checked, not the Content-Type header.
- 400image_too_largeAn upload is more than 6,000 px on a side.
- 401unauthenticatedThe request carries no key. Send it as Authorization: Bearer sk_sceniq_... (or X-Api-Key).
- 401invalid_api_keyThe key is unknown, revoked or expired.
- 403api_scope_requiredA read-only key called a write operation, or a key without the publish option called publish_changes.
- 403forbiddenThe key belongs to a creator account that is no longer active.
- 413payload_too_largeA JSON body over 2 MB, an image over 6 MB, a batch upload over 19 MB, or an MCP message over 2 MB.
- 429rate_limitedOver a limit: requests per key per minute, per creator per hour, uploads per key per minute, or check_links per guide per hour.
- 500internal_errorAn unexpected failure. The message is hidden on purpose.