stories find Examples and Troubleshoot
This page includes stories find snippets for common audits and maintenance tasks, and lists troubleshooting tips for slow runs, missing results, and rejected flags.
Examples
Section titled “Examples”The examples assume you defined a space in the configuration file and use the flags that need the fewest requests. For details, refer to content flags.
Check whether a story is still referenced
Section titled “Check whether a story is still referenced”List the stories that reference a story before you delete it. --references runs on the server, and --skip-content skips the per-story fetch. The search needs no story content. An empty result means that no story references the target.
storyblok stories find --references "0c4f0f4a-9b5e-4c3a-8e7d-2f1a6b8c9d0e" --skip-content | jq --raw-output '.full_slug'Check whether a block is still in use
Section titled “Check whether a block is still in use”Before you remove a block from the schema, list the stories that still use it. --includes-block finds the block at any depth on the server. The search runs without a --where expression or a per-story fetch. A count of zero means that no story uses the block.
# List the stories that use a blockstoryblok stories find --includes-block legacy_hero --skip-content | jq --raw-output '.full_slug'
# Count the stories that use each of several blocksfor block in legacy_hero old_teaser promo_banner; do echo "$block: $(storyblok stories find --includes-block "$block" --skip-content | wc -l)"doneAudit images without alt text
Section titled “Audit images without alt text”Find every story in the space with an asset that has a file but no alt text, at any depth. --capi-filter --skip-content suits a search that reads content across the whole space: bulk reads supply the content, and no story needs its own fetch. --entry-type story excludes folders, which have no bulk content.
storyblok stories find --entry-type story --capi-filter --skip-content \ --where '$..[?(@.fieldtype == "asset" && @.filename != null && @.filename != "" && (@.alt == null || @.alt == ""))]' \ | jq --raw-output '.full_slug' > missing-alt.txtAudit links and references
Section titled “Audit links and references”Check the references and links in every story. --capi-filter reads the content in bulk. For the issue types and the output format, refer to reference checks.
# Save every reference issue in the space as a repair liststoryblok stories find --check-references --capi-filter \ | jq --compact-output '{slug: .full_slug, issues: [._ref_issues[] | {type, field_path, cached_url, actual_url}]}' \ > reference-issues.jsonl
# List stories with outdated URLs. For example, after a slug or folder restructurestoryblok stories find --check-references stale_url --capi-filter | jq --raw-output '.full_slug'
# List published stories that reference unpublished targetsstoryblok stories find --check-references unpublished --publish-status published | jq --raw-output '.full_slug'List changes waiting to go live
Section titled “List changes waiting to go live”List stories by publish status, workflow stage, and scheduled publish. The story listing includes the publish status and the workflow stage. These searches need no story content.
# List published stories with unpublished changes, most recent firststoryblok stories find --publish-status published-with-changes --skip-content --sort updated_at:desc \ | jq --raw-output '[.full_slug, .updated_at] | @tsv'
# List draft stories, oldest firststoryblok stories find --publish-status draft --skip-content --sort created_at:asc | jq --raw-output '.full_slug'
# List stories at the workflow stage with the ID 42storyblok stories find --workflow-stage 42 --skip-content | jq --raw-output '.full_slug'
# List stories scheduled to publish in the next two weeksstoryblok stories find --scheduled 2w --skip-content | jq --raw-output '.full_slug'Find recent and outdated content
Section titled “Find recent and outdated content”Date flags read the story listing. A story outside the range costs no per-story fetch. For the accepted date ranges, refer to date filters.
# List the stories updated in the last 24 hours, most recent firststoryblok stories find --updated 1d --skip-content --sort updated_at:desc \ | jq --raw-output '[.updated_at, .full_slug] | @tsv'
# List published stories that nobody updated in the last yearstoryblok stories find --entry-type story --publish-status published --updated ..1y --skip-content \ | jq --raw-output '.full_slug'
# Count the stories first published in each month of 2025storyblok stories find --first-published 2025 --skip-content \ | jq --raw-output '.first_published_at[0:7]' | sort | uniq -cBuild a content inventory
Section titled “Build a content inventory”# Count the stories per content_typestoryblok stories find --skip-content | jq --raw-output '.content_type' | sort | uniq -c | sort -rn
# Count the stories below a path prefixstoryblok stories find --starts-with en/blog --skip-content | wc -l
# Save an inventory as CSV for a spreadsheetstoryblok stories find --skip-content | jq --raw-output '[.id, .full_slug, .content_type, .published] | @csv' > inventory.csvCheck translation coverage
Section titled “Check translation coverage”Each internationalization approach shows translation state in a different way, and --where covers every approach.
Field-level translation
Section titled “Field-level translation”Field-level translation stores translated values as <field>__i18n__<lang> keys in content. The Content Delivery API drops these keys. Use Management API content for these searches, and omit --capi-filter. Narrow the scope with server-side filters such as --container-block instead.
# Find stories with a missing or empty German titlestoryblok stories find --where '$[?(!$.content.title__i18n__de || $.content.title__i18n__de == "")]'
# Find stories with an empty German description at any depthstoryblok stories find --container-block product --where '$..[?(@.description__i18n__de == "")]'Folder-level translation
Section titled “Folder-level translation”Folder-level translation stores each language under its own path. Use --starts-with to restrict a search to one language.
# Find German draft storiesstoryblok stories find --starts-with "de/" --publish-status draft --skip-content
# Count the published stories per languagefor lang in en de fr; do echo "$lang: $(storyblok stories find --starts-with "$lang/" --publish-status published --skip-content | wc -l)"doneIndividual translation publishing
Section titled “Individual translation publishing”With individual translation publishing enabled, each story has a translated_stories array with one entry per language. The story listing omits this array. Run these searches without --skip-content. They need the per-story fetch, which returns the array.
# Find stories whose German version was never publishedstoryblok stories find --where '$.translated_stories[?(@.lang == "de" && @.published_at == null)]'
# Find stories whose French version has unpublished changesstoryblok stories find --where '$.translated_stories[?(@.lang == "fr" && @.unpublished_changes == true)]'Per-language workflow stages
Section titled “Per-language workflow stages”With workflows, the stages array holds one entry per language, each with a workflow_stage_id. The story listing includes stages. These searches work with --skip-content. Use --workflow-stage to match any language.
# Find stories with any language at the stage with the ID 42storyblok stories find --workflow-stage 42 --skip-content
# Find stories whose German version is at the stage with the ID 42storyblok stories find --skip-content --where '$.stages[?(@.language == "de" && @.workflow_stage_id == 42)]'Match any or all values
Section titled “Match any or all values”The following patterns match any block or reference, and all tags:
# Find stories that use either of two blocks, with one search per blockfor block in hero hero_v2; do storyblok stories find --includes-block "$block" --skip-contentdone | jq --slurp --compact-output 'unique_by(.uuid)[]'
# Find stories that reference either of two stories, with one search per UUIDfor uuid in 0c4f0f4a-9b5e-4c3a-8e7d-2f1a6b8c9d0e 7a2b3c4d-1e2f-4a5b-9c8d-0e1f2a3b4c5d; do storyblok stories find --references "$uuid" --skip-contentdone | jq --slurp --compact-output 'unique_by(.uuid)[]'
# Find stories that carry both of two tags: one tag on the server, the other with --wherestoryblok stories find --tag campaign --skip-content --where '$.tag_list[?(@ == "legacy")]'unique_by(.uuid) removes stories that more than one search returns.
Process results with other tools
Section titled “Process results with other tools”Piped output is JSON Lines. Line-based tools such as jq process it as it arrives.
# Print slugs, one per linestoryblok stories find --container-block product | jq --raw-output '.full_slug'
# Count the matchesstoryblok stories find --container-block product | wc -l
# Save selected fields as JSON Linesstoryblok stories find --container-block product | jq --compact-output '{id, name, component: .content.component}' > products.jsonl
# Save the results as a single JSON arraystoryblok stories find --starts-with lp --skip-content | jq --slurp '.' > lp.jsonjq --slurp reads the whole output before it prints anything. Results appear only after the run ends.
Review results before you write
Section titled “Review results before you write”Save the selection to a file and review the file before you pass it to a command that writes:
# Save the selectionstoryblok stories find --starts-with en/campaigns --updated ..2023 --skip-content > selection.jsonl
# Review the selectionwc -l selection.jsonljq --raw-output '.full_slug' selection.jsonl | lessBefore you write stories that --check-references returns, remove the annotations.
Troubleshoot
Section titled “Troubleshoot”The following sections list symptoms of stories find, their causes, and fixes. For the flags that these fixes use, refer to content flags.
The run is slow
Section titled “The run is slow”A search that reads content fetches every story in scope one at a time, at six requests per second by default. Narrow the scope first, then change how the run gets content:
- Move filters from
--whereto server-side flags such as--starts-with,--includes-block,--container-block,--tag, and--publish-status. - Replace a
--whereexpression on a date with--created,--updated,--last-published, or--first-published. These flags skip the per-story fetch for stories outside the range. - Run the same flags without
--where, plus--skip-content | wc -l, to count the stories in scope. When the count divided by six comes to minutes, add--capi-filter. - Add
--skip-contentwhen the output needs nocontent.
storyblok stories find --where '$..[?(@.component == "hero" && @.headline == "")]'storyblok stories find --includes-block hero --where '$..[?(@.component == "hero" && @.headline == "")]'storyblok stories find --includes-block hero --capi-filter --skip-content \ --where '$..[?(@.component == "hero" && @.headline == "")]'To sort or limit results, use --sort and --limit. A jq sort holds back the whole result set before it prints the first line.
An expression takes too long to test
Section titled “An expression takes too long to test”Add --limit 5 or pipe into head -n 5 while you develop an expression. The run stops after the first results, whatever the size of the space.
A --where expression matches nothing
Section titled “A --where expression matches nothing”Check the following causes:
--skip-contentwith an expression that readscontent: the story listing has no content. Remove--skip-contentor add--capi-filter.@where$belongs:$[?(@.updated_at > "2025-01-01")]tests each property value of the story. Use$[?($.updated_at > "2025-01-01")]to test the story.- Shell expansion of part of the expression: wrap the expression in single quotes.
A search with --capi-filter --skip-content misses matching stories
Section titled “A search with --capi-filter --skip-content misses matching stories”An expression on content excludes folders, unpublished stories, and stories in a failed batch. Run the search without --skip-content, or add --entry-type story to exclude folders. For details, refer to --capi-filter.
The command rejects an __i18n__ key under --capi-filter
Section titled “The command rejects an __i18n__ key under --capi-filter”The Content Delivery API drops field-level translations. Use --capi-params language=de to request the language and match the base field name, or run the search without --capi-filter.
The command rejects --capi-params version=published
Section titled “The command rejects --capi-params version=published”The Content Delivery API returns a published version only for stories that someone published. Add --publish-status published, --publish-status published-with-changes, or both.
A reference check reports no issues for some stories
Section titled “A reference check reports no issues for some stories”Run the check without --capi-filter to include references in field-level translations and stories without Content Delivery API content. For details, refer to --capi-filter.
A reference check returns too many results
Section titled “A reference check returns too many results”Targets that are drafts report as unpublished. A space with many drafts returns many results. Check broken first, because those references point to missing stories:
storyblok stories find --check-references brokenThen list published stories that reference unpublished targets.
The command exits with code 1
Section titled “The command exits with code 1”Part of the run failed, and the results may be incomplete. Check the summary on stderr and run the command again. For all codes, refer to exit codes.
The output differs in a terminal and in a script
Section titled “The output differs in a terminal and in a script”The command prints a table in a terminal and JSON Lines when you pipe the output. To get JSON Lines in a terminal, pipe the output into cat. For details, refer to output.
Was this page helpful?
This site uses reCAPTCHA and Google's Privacy Policy (opens in a new window).Terms of Service (opens in a new window) apply.
Get in touch with the Storyblok community