Import assets to Media Library

Import assets in bulk and automatically categorize with aspect data.

When importing assets to Media Library, there are two primary goals:

  • Upload the media so it can be used in a studio or another application.
  • Assign aspect data so that the asset is categorized and tagged correctly.

There are multiple ways to import assets into your library to accomplish these goals together. The recommended way is to use the Sanity CLI. You can run npx sanity@latest media import --help for a quick summary of syntax and options.

Import using the CLI

Before you import, you need:

  • A Sanity CLI login. Run npx sanity@latest login to log in.
  • A project with an active Media Library.

For bulk imports, the media import command operates on a directory or archive that can have three components:

  • An images directory for files that should be uploaded as an image.
  • A files directory for non-image files.
  • A data.ndjson file that contains aspect data for any of the files contained in the images or files directories.
product-photos
├── data.ndjson
├── images
│   └── ...all image files
└── files
    └── ...all other files

The import command uploads every file directly inside the images and files directories. Files in subdirectories of those directories are not imported. Flatten them first.

data.ndjson format

data.ndjson is a newline-delimited JSON file (NDJSON) that can contain aspect information for any of the processed files. Each line must be a valid JSON object containing the information that should be associated with a single file.

These example JSON objects each define aspect data for one file:

{
  "filename": "images/hero-shot.jpg",
  "aspects": {
    "description":"A dog chasing a stick"
  }
}

However, NDJSON uses the newline character as a delimiter to combine multiple JSON objects into a single file. Here are the two example objects combined into a single NDJSON file:

{"filename": "images/hero-shot.jpg","aspects":{"description":"A dog chasing a stick"}}
{"filename": "images/studio-portrait.jpg","aspects":{"licensedPhotograph":{"expiration": "2026-05-02T17:34:00.000Z","photographer": {"_ref":"dataset:.example:photographer-7926527","_type":"globalDocumentReference","_weak": true}}}}

Each JSON object for an asset has two components:

  • filename: The path to the file, relative to the folder that contains data.ndjson, for example images/hero-shot.jpg.
  • aspects: The aspect data to attach to the asset.

After you've prepared your files, run the import with the Sanity CLI:

npx sanity@latest media import product-photos

# or

npx sanity@latest media import product-photos.tar.gz

What should I import?

The command prompts you to choose a media library, and a project if it can't find one in sanity.cli.ts. To run the import without prompts, for example in CI, pass both as flags:

npx sanity@latest media import product-photos \
  --project-id  \
  --media-library-id YOUR_MEDIA_LIBRARY_ID

Replace with your project ID and YOUR_MEDIA_LIBRARY_ID with the ID of the target library. --media-library-id is required when the terminal isn't interactive, or when you pass --yes or --json.

Existing assets keep their aspect data

Import a single asset from a URL

The media import command also accepts an https URL. Sanity fetches the asset from the URL and adds it to your library as a single asset. This requires @sanity/cli 8.12.0 or later:

npx sanity@latest media import https://example.com/hero.png \
  --aspect department=Brand

Two flags apply only to URL imports:

  • --aspect: Sets an aspect value, written as key=value. Repeat the flag to set more than one aspect. Values are always sent as strings. For non-string or nested values, use a directory import with data.ndjson.
  • --filename: Sets the original filename stored on the asset. Defaults to a name derived from the URL.

The URL must use https and can't contain a username or password. For a source that needs authentication, use a presigned URL. The --replace-aspects flag only applies to directory and archive imports.

Import using a client library

If you prefer not to use the Sanity CLI import tool, you can run the import yourself with the HTTP API:

You can also set aspects in the upload request itself, which saves one request per asset. See Upload with aspect values.

There are some common pitfalls to keep in mind:

  • Concurrency: While you may have thousands of assets to import, don't trigger thousands of requests in parallel. Parallel requests exceed API rate limits and can fail. Use a queue with a low concurrency to keep your import below the API rate limit.
  • API usage limits: Importing large libraries can quickly cause a lot of requests, especially if you import a single asset per request. Send multiple mutations within a single transaction.
  • Mutation size limits: While it's a good idea to do multiple mutations per transaction, make sure the size of the request is within our limits, in terms of byte size.

Was this page helpful?

sanity.ioAll Systems Operational© Sanity 2026
Change site theme