Upload any file. Stow detects the type, generates metadata, deduplicates by content, and gives you a CDN URL.
Upload your first file in 30 seconds
All endpoints and parameters
Connect your AI coding assistant
Sign up for a free account — no credit card required.
Go to your dashboard and create an API key. Copy it — you'll only see it once.
Just send a file. That's it — no metadata required.
curl -X POST https://api.usestow.com/v1/assets \
-H "Authorization: Bearer stow_sk_..." \
-F file=@photo.pngStow auto-detects the media type, generates a title from the filename, and returns a CDN URL. If analysis is enabled on your plan, tags, description, and category are generated automatically.
curl https://api.usestow.com/v1/assets/search \
-H "Authorization: Bearer stow_sk_..." \
-H "Content-Type: application/json" \
-d '{"media_type":"image","tags":["nature"]}'When you upload a file, Stow automatically detects:
For images under 5 MB, Stow runs analysis before responding — your upload response includes auto-generated tags, description, category, and content rating. For larger files and non-image types, analysis runs in the background and the asset is updated once complete.
You can always override any auto-generated field by including it in the metadata form field.
Every file is hashed on upload using SHA256. If you upload the same file twice — even with a different filename — Stow returns the existing asset instead of creating a duplicate.
# First upload — creates the asset
curl -F file=@sunset.png ... → 201 Created
# Same file, different name — returns existing
curl -F file=@sunset-copy.png ... → 200 OK (existing asset)You can also check for duplicates before uploading by looking up a content hash:
GET /v1/assets/by-content-hash/a1b2c3d4...All API requests (except health check) require a Bearer token:
Authorization: Bearer stow_sk_...Create API keys in your dashboard. Each key is scoped to your account.
Base URL: https://api.usestow.com
/v1/healthHealth check. No auth required.
{ "status": "ok", "service": "stow-api", "version": "0.1.0" }/v1/assetsUpload a file. Send as multipart form data. Only the file is required — everything else is optional.
| Parameter | Type | Description |
|---|---|---|
file* | File | The file to upload (any type) |
metadata | JSON string | Optional metadata (see below). Omit to let Stow auto-generate everything. |
| Parameter | Type | Description |
|---|---|---|
title | string | Title. Auto-generated from filename if omitted. |
description | string | Description. Auto-generated by AI analysis if omitted. |
tags | string[] | Tags for filtering. Auto-generated by AI if omitted. |
category | string | Category path (e.g. "nature/landscape"). Auto-generated if omitted. |
content_rating | string | general, teen, mature, or adult. Auto-detected if omitted. |
key | string | Human-readable lookup key (e.g. "hero-banner") |
format | string | File format override. Auto-detected from file contents. |
duration_seconds | number | Duration for audio/video files |
Returns 201 with the created asset. If a duplicate exists (same content hash), returns 200 with the existing asset.
/v1/assets/:idGet an asset by UUID.
/v1/assets/by-content-hash/:hashLook up by content hash (SHA256). Use this to check for duplicates before uploading.
GET /v1/assets/by-content-hash/a1b2c3d4e5f6.../v1/assets/by-key/:type/:keyLook up by type and human-readable key. Edge-cached.
GET /v1/assets/by-key/image/hero-banner/v1/assets/by-hash/:type/:hashLook up by type and prompt hash (legacy). Edge-cached.
/v1/assets/searchSearch assets with filters.
| Parameter | Type | Description |
|---|---|---|
media_type | string | Filter by media type: image, audio, video, document, other |
tags | string[] | Filter by tags (all must match) |
category | string | Filter by category |
content_rating | string | Max content rating to include |
key | string | Filter by key (exact match) |
text | string | Full-text search across title, description, and filename |
limit | number | Max results (default 25, max 100) |
offset | number | Pagination offset |
{
"data": [{ ... }],
"count": 3
}/v1/assets/:id/useIncrement the use count for an asset. Fire-and-forget.
{ "ok": true }{
"id": "uuid",
"media_type": "image",
"mime_type": "image/png",
"content_hash": "a1b2c3d4e5f6...64 hex chars",
"original_filename": "sunset-mountains.png",
"title": "Sunset Over Mountains",
"description": "A vibrant sunset behind a mountain range",
"tags": ["nature", "landscape", "sunset"],
"category": "nature/landscape",
"content_rating": "general",
"analysis_status": "completed",
"storage_path": "image/a1b2c3d4...f6.png",
"public_url": "https://api.usestow.com/r2/image/a1b2c3d4...f6.png",
"format": "png",
"file_size_bytes": 2048000,
"use_count": 12,
"created_at": "2026-03-07T12:00:00Z"
}| Status | Meaning |
|---|---|
| none | No analysis requested (quota exceeded or not applicable) |
| pending | Analysis is running in the background |
| completed | Analysis finished, metadata is enriched |
| failed | Analysis failed (asset still usable, just not enriched) |
Assets can have a content rating, either set manually or auto-detected. When searching, pass content_rating to set the maximum rating to include.
| Rating | Description |
|---|---|
| general | Safe for all audiences |
| teen | Mild violence or tension |
| mature | Graphic violence, horror |
| adult | Explicit content |
All errors return JSON:
{ "error": "Human-readable error message" }| Status | Meaning |
|---|---|
| 400 | Bad request (validation error) |
| 401 | Missing or invalid API key |
| 402 | Plan limit reached (upgrade required) |
| 404 | Asset not found |
| 429 | Rate limit exceeded (try again later) |
| 500 | Internal server error |
Stow ships an MCP server so AI coding tools (Claude Code, Cursor, etc.) can search, upload, and manage your media library through natural language.
Add this to your MCP config (e.g. .claude/settings.json or Cursor settings):
{
"mcpServers": {
"stow": {
"command": "npx",
"args": ["@usestow/mcp-server"],
"env": {
"STOW_API_URL": "https://api.usestow.com",
"STOW_API_KEY": "stow_sk_..."
}
}
}
}stow_searchSearch your media library by type, tags, category, or text.
stow_getGet an asset by ID, content hash, or key.
stow_uploadUpload a file. Only the file is required — Stow auto-detects everything else.
stow_get_or_createCheck if a file exists by content hash before uploading. Prevents duplicates.
The simplest possible upload — just send a file:
const form = new FormData()
form.append('file', photoBlob, 'sunset.png')
const res = await fetch('https://api.usestow.com/v1/assets', {
method: 'POST',
headers: { Authorization: `Bearer ${STOW_KEY}` },
body: form,
})
const asset = await res.json()
console.log(asset.public_url) // CDN URL
console.log(asset.media_type) // "image"
console.log(asset.tags) // ["nature", "landscape", "sunset"]Override any auto-generated field by including a metadata JSON blob:
const form = new FormData()
form.append('file', audioBlob, 'tavern-ambiance.mp3')
form.append('metadata', JSON.stringify({
title: 'Tavern Ambiance',
tags: ['fantasy', 'interior', 'social'],
category: 'environment/interior',
key: 'tavern',
}))
const res = await fetch('https://api.usestow.com/v1/assets', {
method: 'POST',
headers: { Authorization: `Bearer ${STOW_KEY}` },
body: form,
})const { data } = await fetch('https://api.usestow.com/v1/assets/search', {
method: 'POST',
headers: {
Authorization: `Bearer ${STOW_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
media_type: 'image',
tags: ['landscape'],
limit: 10,
}),
}).then(r => r.json())Compute a SHA256 hash client-side and check before uploading:
// Hash the file
const hashBuffer = await crypto.subtle.digest('SHA-256', fileBuffer)
const hashHex = [...new Uint8Array(hashBuffer)]
.map(b => b.toString(16).padStart(2, '0')).join('')
// Check if it exists
const res = await fetch(
`https://api.usestow.com/v1/assets/by-content-hash/${hashHex}`,
{ headers: { Authorization: `Bearer ${STOW_KEY}` } }
)
if (res.ok) {
// Already exists — use the existing asset
const existing = await res.json()
console.log('Already uploaded:', existing.public_url)
} else {
// New file — upload it
}Need help? Create an account to get started.