Skip to content

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.

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.

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.

Terminal window
storyblok stories find --references "0c4f0f4a-9b5e-4c3a-8e7d-2f1a6b8c9d0e" --skip-content | jq --raw-output '.full_slug'

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.

Terminal window
# List the stories that use a block
storyblok stories find --includes-block legacy_hero --skip-content | jq --raw-output '.full_slug'
# Count the stories that use each of several blocks
for block in legacy_hero old_teaser promo_banner; do
echo "$block: $(storyblok stories find --includes-block "$block" --skip-content | wc -l)"
done

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.

Terminal window
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.txt

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.

Terminal window
# Save every reference issue in the space as a repair list
storyblok 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 restructure
storyblok stories find --check-references stale_url --capi-filter | jq --raw-output '.full_slug'
# List published stories that reference unpublished targets
storyblok stories find --check-references unpublished --publish-status published | jq --raw-output '.full_slug'

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.

Terminal window
# List published stories with unpublished changes, most recent first
storyblok 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 first
storyblok 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 42
storyblok stories find --workflow-stage 42 --skip-content | jq --raw-output '.full_slug'
# List stories scheduled to publish in the next two weeks
storyblok stories find --scheduled 2w --skip-content | jq --raw-output '.full_slug'

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.

Terminal window
# List the stories updated in the last 24 hours, most recent first
storyblok 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 year
storyblok 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 2025
storyblok stories find --first-published 2025 --skip-content \
| jq --raw-output '.first_published_at[0:7]' | sort | uniq -c
Terminal window
# Count the stories per content_type
storyblok stories find --skip-content | jq --raw-output '.content_type' | sort | uniq -c | sort -rn
# Count the stories below a path prefix
storyblok stories find --starts-with en/blog --skip-content | wc -l
# Save an inventory as CSV for a spreadsheet
storyblok stories find --skip-content | jq --raw-output '[.id, .full_slug, .content_type, .published] | @csv' > inventory.csv

Each internationalization approach shows translation state in a different way, and --where covers every approach.

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.

Terminal window
# Find stories with a missing or empty German title
storyblok stories find --where '$[?(!$.content.title__i18n__de || $.content.title__i18n__de == "")]'
# Find stories with an empty German description at any depth
storyblok stories find --container-block product --where '$..[?(@.description__i18n__de == "")]'

Folder-level translation stores each language under its own path. Use --starts-with to restrict a search to one language.

Terminal window
# Find German draft stories
storyblok stories find --starts-with "de/" --publish-status draft --skip-content
# Count the published stories per language
for lang in en de fr; do
echo "$lang: $(storyblok stories find --starts-with "$lang/" --publish-status published --skip-content | wc -l)"
done

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.

Terminal window
# Find stories whose German version was never published
storyblok stories find --where '$.translated_stories[?(@.lang == "de" && @.published_at == null)]'
# Find stories whose French version has unpublished changes
storyblok stories find --where '$.translated_stories[?(@.lang == "fr" && @.unpublished_changes == true)]'

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.

Terminal window
# Find stories with any language at the stage with the ID 42
storyblok stories find --workflow-stage 42 --skip-content
# Find stories whose German version is at the stage with the ID 42
storyblok stories find --skip-content --where '$.stages[?(@.language == "de" && @.workflow_stage_id == 42)]'

The following patterns match any block or reference, and all tags:

Terminal window
# Find stories that use either of two blocks, with one search per block
for block in hero hero_v2; do
storyblok stories find --includes-block "$block" --skip-content
done | jq --slurp --compact-output 'unique_by(.uuid)[]'
# Find stories that reference either of two stories, with one search per UUID
for uuid in 0c4f0f4a-9b5e-4c3a-8e7d-2f1a6b8c9d0e 7a2b3c4d-1e2f-4a5b-9c8d-0e1f2a3b4c5d; do
storyblok stories find --references "$uuid" --skip-content
done | jq --slurp --compact-output 'unique_by(.uuid)[]'
# Find stories that carry both of two tags: one tag on the server, the other with --where
storyblok stories find --tag campaign --skip-content --where '$.tag_list[?(@ == "legacy")]'

unique_by(.uuid) removes stories that more than one search returns.

Piped output is JSON Lines. Line-based tools such as jq process it as it arrives.

Terminal window
# Print slugs, one per line
storyblok stories find --container-block product | jq --raw-output '.full_slug'
# Count the matches
storyblok stories find --container-block product | wc -l
# Save selected fields as JSON Lines
storyblok stories find --container-block product | jq --compact-output '{id, name, component: .content.component}' > products.jsonl
# Save the results as a single JSON array
storyblok stories find --starts-with lp --skip-content | jq --slurp '.' > lp.json

jq --slurp reads the whole output before it prints anything. Results appear only after the run ends.

Save the selection to a file and review the file before you pass it to a command that writes:

Terminal window
# Save the selection
storyblok stories find --starts-with en/campaigns --updated ..2023 --skip-content > selection.jsonl
# Review the selection
wc -l selection.jsonl
jq --raw-output '.full_slug' selection.jsonl | less

Before you write stories that --check-references returns, remove the annotations.

The following sections list symptoms of stories find, their causes, and fixes. For the flags that these fixes use, refer to content flags.

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:

  1. Move filters from --where to server-side flags such as --starts-with, --includes-block, --container-block, --tag, and --publish-status.
  2. Replace a --where expression on a date with --created, --updated, --last-published, or --first-published. These flags skip the per-story fetch for stories outside the range.
  3. 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.
  4. Add --skip-content when the output needs no content.
Fetches every story in the space
storyblok stories find --where '$..[?(@.component == "hero" && @.headline == "")]'
Fetches only the stories that contain a hero block
storyblok stories find --includes-block hero --where '$..[?(@.component == "hero" && @.headline == "")]'
Reads content in bulk and fetches no story one at a time
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.

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.

Check the following causes:

  • --skip-content with an expression that reads content: the story listing has no content. Remove --skip-content or 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:

Terminal window
storyblok stories find --check-references broken

Then list published stories that reference unpublished targets.

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?

What went wrong?

This site uses reCAPTCHA and Google's Privacy Policy (opens in a new window).Terms of Service (opens in a new window) apply.