Content Lake (Datastore)

Upload, query, and delete assets

Programmatically manage assets in your project dataset.

In cases where the UI doesn't offer enough control or automation, you can use Sanity's APIs to interact with assets.

The techniques in this guide apply to assets stored in a project's dataset. To work with Media Library assets, see Upload assets programmatically in the Media Library documentation.

Dataset assets are always public

For details on rendering and transforming assets for your front-end applications, see the Presenting images documentation.

Prerequisites:

  • For the Node.js examples, Node.js 22.12 or later and @sanity/client v8. The client is ESM-only and the examples use top-level await, so save them as .mjs files or set "type": "module" in package.json.
  • Your project ID and dataset name, which replace <your-project-id> and <your-dataset> in the examples.
  • A robot token with write access to the dataset, stored in the SANITY_API_TOKEN environment variable. See Authentication and tokens. The Studio example uses the signed-in user's credentials and doesn't need a token.

Upload an asset

Gotcha

In cases where uploading assets in the UI is impractical, like batch uploads or migrations, you can use the client or API directly.

Upload an image and update a draft

The JS Client example below uploads an image and adds its reference to a draft of post-123. Replace this ID with an existing document ID. The edit action updates the existing draft, or creates one from the published document if there is no draft.

Review and publish the draft in Studio when all its changes are ready. Until then, the published document stays unchanged. See Mutate documents with actions for the draft and publishing workflow.

The client.assets.upload() method resolves to the asset document shown in the Asset response shape tab. The HTTP API returns the same document wrapped in a document property. In both, size is in bytes, and metadata.location is present only if you request location metadata (extract: ['location'] in the client, meta=location in the HTTP API) and the image contains location data.

To upload an image or file from an HTTPS URL, use the /from-url endpoints in the Assets API reference. At this time, the client does not support uploading from a URL with the assets.upload method.

Query and browse assets

To browse project assets from the Studio interface, install the Sanity Media plugin. This adds a new tool to the toolbar and enables Studio users to browse and manage assets.

You can also query image and file assets with GROQ. These queries return up to 20 assets each, with their ID, URL, and original filename:

Run GROQ queries through Vision, the client, or the Query API.

You can also use this method to query Media Library assets that have been linked to your dataset.

To view an asset's metadata or URL from a document that references it, follow the asset reference. For example:

Delete assets

To delete an asset, delete its asset document.

import {createClient} from '@sanity/client'
const config = {
  projectId: 'YOUR_PROJECT_ID',
  dataset: 'YOUR_DATASET',
  apiVersion: '2026-03-01',
  useCdn: false,
  token: process.env.SANITY_API_TOKEN
}
const client = createClient(config)
// Note: this is the _id of the asset document.
const result = await client.delete('image-7f3c9a1e5b2d4f6a8c0e1b3d5f7a9c2e4b6d8f0a-538x538-jpg')
console.log('deleted image asset', result)

Content Lake rejects the delete if other documents still hold a strong reference to the asset, such as an image field that points to it. The error names the referencing documents, for example: Document "image-7f3c9a1e5b2d4f6a8c0e1b3d5f7a9c2e4b6d8f0a-538x538-jpg" cannot be deleted as there are references to it from "post-123". Remove those references, or make them weak, then delete the asset.

While the file is deleted, the CDN might have your asset cached, so it may not disappear immediately.

Download assets

To download an asset, append ?dl=YOUR_FILENAME.jpg to the asset URL. If you leave the filename blank, the original filename is used if present. If the original filename isn't available, the file's ID is used instead.

// GROQ query

*[_type == "post"] {
  title,
  "imageUrl": mainImage.asset->url
}
// Then you can use the URL in HTML for example like this:
// <a href={`${imageUrl}?dl=`}>Hero Image</a>

Was this page helpful?