Migration

How do you import content into Sanity safely: dry runs, draft prefixes, paused webhooks and rollback?

Short answer

A safe Sanity import adds its own controls, because sanity datasets import has no dry run, skips validation and can fire webhooks (Sanity Docs; Import data, October 2026). Validate the import file, rehearse in a scratch dataset, pause webhooks and Functions, import as drafts. for live sites, choose --replace or --missing, and keep an export and ID list for rollback.

Dry run on sanity datasets import
None: the command lists no dry-run flagsanity migrations run dry-runs by default and can also write new documents fetched from an external source; validate an import file with sanity documents validate --fileSources: Sanity Docs (Datasets CLI command reference, Migrations CLI command reference); Sanity Learn (Scripting content migrations)
Document ID already in the target
Import fails unless --replace or --missing--replace overwrites the document, --missing skips it, and no mode deletes documents that aren't in the fileSources: Sanity Docs (Import data, Development, staging, and production environments)
Scripted write rate
25 mutations per second per IP addressRequests over it return 429; @sanity/client retries queries but not mutations; each mutation request body is capped at 4 MB, as of October 2026Sources: Sanity Docs (Import data, Technical limits)
Managed backups
Certain Enterprise plans only, off by defaultOn Free and Growth the backup is your own export; document history covers 90 days on Growth, as of October 2026Sources: Sanity Docs (Backups, History experience); Sanity (Plans that scale with you)

Deploi’s call

Treat an import into Sanity as one-way unless you built the undo before it ran: a validated file, a rehearsal in a scratch dataset, an export of the target and the list of IDs the import creates. Use --missing on any dataset editors already work in, and keep --replace for documents only your migration writes.

MigrateImport straight into Sanity's production dataset when

  • No live site reads the production dataset yet, so a bad import breaks nothing public and documents can land published, without the drafts. prefix.
  • Every document ID comes from its source ID, so a rerun with --replace overwrites instead of duplicating.
  • The NDJSON (newline-delimited JSON) file passes sanity documents validate --file, since the import has no dry run, and you hold the list of IDs it creates for rollback.

PhaseImport into a Sanity staging dataset first, then promote to production, when

  • Production already serves pages or holds editors' work, because an import can overwrite documents but never deletes them (Sanity Docs, October 2026).
  • Your plan has a dataset and quota room to spare, since Free and Growth include 2 datasets, every copy counts toward that limit, and documents count across all datasets, as of October 2026.
  • Webhooks or Functions react to production writes and can't stay paused for the whole run, so the first import has to land somewhere they don't fire.

Does Sanity's import command have a dry run, and what replaces it?

No. sanity datasets import, Sanity's command-line interface (CLI) import, lists no dry-run flag. Sanity's refactoring course confirms it for the NDJSON (newline-delimited JSON) route (Sanity Docs, Datasets CLI reference; Sanity Learn, October 2026). The import skips validation too (Sanity Docs, Import data; Schema validation and the Content Lake, October 2026). sanity migrations run dry-runs by default and can also write new documents fetched from an external source (Sanity Learn; Sanity Docs, Migrations CLI reference), so a migration script dry-runs. For an NDJSON file, rehearse the import yourself; Deploi's September 2026 release scripts changed nothing without --apply. Safe importing is step 5 of planning a migration to Sanity.

A safe import into Sanity, with the check for each step

  1. Validate the file. Run sanity documents validate --file on the NDJSON file or tarball (Sanity Docs). Check: no errors.
  2. Rehearse. Import into a scratch dataset you can delete. Check: counts per type match the source.
  3. Export the target. Run sanity datasets export. Check: the file opens.
  4. Pause side effects. Disable webhooks; keep Functions off the target. Check: no deliveries.
  5. Import. Choose --missing or --replace; save the IDs the file creates. Check: a clean exit.
  6. Verify. Run sanity documents validate. Check: results match the rehearsal.
  7. Switch side effects back on. Re-enable webhooks and Functions. Check: one test edit fires once.

What do --replace and --missing do, and which should a migration use?

sanity datasets import fails on a document ID that already exists unless you pass --replace (overwrite it) or --missing (skip it); a rerun doesn't update in place by default (Sanity Docs, Import data, October 2026). No mode deletes: "Import never deletes documents" (Sanity Docs, Development, staging, and production environments, October 2026).

Pick the import mode by who else writes to the target dataset
ModeExisting ID in the targetFits
No flagThe import failsA first load into an empty dataset
--replaceOverwritten by the fileReruns of documents only your migration writes
--missingSkipped, the target's version staysSeeding a dataset editors already use
The fit column is Deploi's judgment; Sanity's environments page advises --missing for moving development content into production.Sources: Sanity Docs (Import data, Datasets CLI command reference, Development, staging, and production environments). As of 24 October 2026.

Deploi's October 2026 import package for its Shopify data section uses --replace, made safe by ID prefixes on every document it writes. When Deploi moved its FAQ into a shared Sanity project in September 2026, it imported without --replace, so editors' versions were never overwritten.

Should migrated content land in Sanity as drafts or as published documents?

Land migrated content as drafts when a live site already reads the target dataset, and as published documents when nothing public reads it yet. A draft is the document's ID with a drafts. prefix, and "Neither drafts nor versions are served to unauthenticated requests", so a front end that reads published content never shows them (Sanity Docs, Drafts and versions, October 2026). Two rules follow:

  • Point references at published IDs. Never prefix a reference. "A published document can't hold a strong reference to unpublished content" (Sanity Docs, Drafts and versions), so a reference to a target that is still a draft has to be weak. Deploi's import notes for its Build vs. Buy hub, now 701 decision pages, bring unpublished content in under the drafts. prefix and never prefix references, as publishing 701 pages at agent scale describes.
  • Count drafts in the quota. Sanity counts stored documents "(including drafts)" (Sanity Docs, Plans and payments, October 2026), so a draft beside its published version is a second document (what counts as a Sanity document).

Decide before the first import, because changing later means a second import and a cleanup of the first (Deploi's judgment). Drafts still have to be published, which for a large import takes a script rather than the Studio, so plan that step before the first import (Deploi's judgment).

What do you switch off in Sanity before an import?

Switch off the webhooks and Functions that react to the target dataset, because "Importing data can trigger your webhooks and functions and send high volumes of traffic to the endpoints they call" (Sanity Docs, Import data, October 2026).

  • Webhooks. Disable each one in Manage, Sanity's project settings. "When a webhook is disabled, all pending requests will be canceled" (Sanity Docs, webhooks, October 2026), so trigger one rebuild yourself after you switch them back on.
  • Functions. "A deployed function has no on/off switch" (Sanity Docs, Functions cheat sheet, October 2026). Sanity lists three ways around it, in its order of preference: scope the function to another dataset, remove it from the blueprint and redeploy, or return early when an environment variable is set. The last still counts every run as an invocation toward Functions usage.

Deploi's release runbook covers the webhook half: it has the revalidation webhook created only after the September 2026 bulk change, so the 256 changed FAQ answers wouldn't trigger 256 rebuilds. Functions or webhooks compares the two triggers.

What limits and rules apply when a script writes content into Sanity?

A script that writes content into Sanity through the API (application programming interface) has to stay at 25 mutations per second or fewer per IP (Internet Protocol) address, keep each mutation request body under 4 MB, and add the array keys Sanity Studio adds for editors (Sanity Docs, Import data; Technical limits; Learn, October 2026). Requests over the rate return 429 Too Many Requests, and @sanity/client retries rate-limited queries, not mutations (Sanity Docs, Import data). Four rules follow:

  • Queue and batch. Send several mutations per transaction and queue them under the rate, so a long run doesn't stall.
  • Key every array item. "Studio auto-generates keys when editors create content through the UI; the API doesn't" (Sanity Learn, Import content with the two-pass pattern, October 2026).
  • Two passes for references. Write _weak: true placeholders first and resolve them second (Sanity Learn, two-pass pattern). The CLI (command-line interface) import does this itself: references go in weak and turn strong once every document is in (Sanity Docs, Import data). Deploi's builds graph keeps all 2,940 of its references weak; reference-safe imports go deeper.
  • Rerun asset uploads freely. The same image binary always gets the same ID, with no duplicate (Sanity Learn, Uploading assets efficiently, October 2026). In the CLI import, an unavailable asset fails the run unless you pass --allow-failing-assets (Sanity Docs, Import data).

How do you roll back an import into Sanity?

You roll back an import into Sanity by deleting the documents it created, from the saved list of their IDs, with the command-line interface (CLI) command sanity documents delete, which takes one or more IDs (Sanity Docs, Documents CLI reference, October 2026). That removes new documents only. Documents a --replace run overwrote come back only from an export or from document history, so prepare both before the run:

Deploi's October 2026 data-section wave shipped 406 documents and 159 images with an ID list for count checks and rollback. Cutover and rollback covers undoing the go-live itself.

Deploi’s point of view

Where we disagree
The usual advice says --replace makes a Sanity import safe to rerun. It makes the import repeatable, not safe. A rerun with --replace overwrites every edit made since the last run, never removes documents you dropped from the source, and a restore with it leaves newer documents in place. Safety comes from what surrounds the command: ID prefixes on everything the import writes, a check for documents edited since the last run, and an ID list to delete from.
What we’ve seen
These controls come from deploi.ca's own Sanity releases. In September 2026 Deploi's release scripts changed nothing without --apply and skipped any document edited since the snapshot, and its release runbook has the revalidation webhook created only after the bulk change, so the 256 changed answers wouldn't trigger 256 rebuilds. The October 2026 wave of its Shopify data section went in as one package of 406 documents and 159 images, with check queries and an ID list for rollback.
What this adds
The root plans the migration and says to load staging first. This page is the import step itself: each failure mode Sanity documents for its import (no dry run, no validation, no deletes, webhook and Function triggers, rate limits, restores that aren't resets) paired with the control that covers it.
What we refuse to promise
We won't call an import on Free or Growth reversible unless an export of the target was taken before it ran. Managed backups are on certain Enterprise plans and off by default, and Growth's document history reaches back 90 days, as of October 2026, so the export is the only full copy you control.

When this answer changes

  • Sanity adds a dry-run or validation option to sanity datasets import
  • Sanity changes the 25 mutations per second rate limit or the 4 MB mutation body limit
  • Sanity gives deployed Functions an on/off switch
  • Managed backups reach the Growth plan, or Growth's 90-day document history changes
  • Sanity renames the datasets or documents CLI commands again

Sources

Every source opened and checked on 25 October 2026.

  1. Sanity Docs, Import datasanity.io · checked 24 October 2026
  2. Sanity Docs, Datasets CLI command referencesanity.io · checked 24 October 2026
  3. Sanity Learn, Scripting content migrationssanity.io · checked 24 October 2026
  4. Sanity Docs, Migrations CLI command referencesanity.io · checked 24 October 2026
  5. Sanity Docs, Documents CLI command referencesanity.io · checked 24 October 2026
  6. Sanity Docs, Development, staging, and production environmentssanity.io · checked 24 October 2026
  7. Sanity Docs, Drafts and versionssanity.io · checked 24 October 2026
  8. Sanity Docs, Functions cheat sheetsanity.io · checked 24 October 2026
  9. Sanity Docs, GROQ-powered webhookssanity.io · checked 24 October 2026
  10. Sanity Docs, Technical limitssanity.io · checked 24 October 2026
  11. Sanity Learn, Import content with the two-pass patternsanity.io · checked 24 October 2026
  12. Sanity Learn, Uploading assets efficientlysanity.io · checked 24 October 2026
  13. Sanity Docs, Export a datasetsanity.io · checked 24 October 2026
  14. Sanity Docs, Backupssanity.io · checked 24 October 2026
  15. Sanity Docs, History experiencesanity.io · checked 24 October 2026
  16. Sanity, Plans that scale with yousanity.io · checked 24 October 2026
  17. Sanity Docs, Plans and paymentssanity.io · checked 24 October 2026
  18. Sanity Docs, Schema validation and the Content Lakesanity.io · checked 24 October 2026
  19. Sanity Docs, Migrating your schema and contentsanity.io · checked 24 October 2026
  20. Sanity Docs, Important considerations for schema and content migrationssanity.io · checked 24 October 2026