Skip to content

stories find

stories find searches a Storyblok space for stories that match a set of filters. The command only reads data and leaves the space unchanged. It prints each match as a line of JSON. In a terminal, it prints a table.

Use stories find to query a space and pass the matches to scripts. For example, list stories that use a block or reference another story.

The command combines all filters with AND.

For examples of common audits and fixes for slow runs and missing results, refer to stories find: Examples and Troubleshoot.

Terminal window
storyblok stories find [arguments] [flags]
Argument Type Description
Search text string A case-insensitive full-text search of story names, slugs, and content, through the Management API text_search parameter.

All flags are optional, except --space.

The command lists stories first. The story listing holds story metadata without content. The per-story fetch retrieves one story with its content through one Management API request.

Flag Type Description
--space, -s integer Required. The ID of the Storyblok space to search.
--entry-type string Restrict results to story, folder, or all. Defaults to all.
--starts-with string Restrict results to stories below a path prefix. For example, en/blog. Omit the leading slash.
--container-block string Match stories whose root block is this block. A shorthand for --query.
--includes-block string Match stories that contain all these blocks at any depth. Comma-separated.
--query, -q string Filter by root-level content fields with filter query syntax.
--tag string Match stories with any of these tags. Comma-separated.
--workflow-stage string Match stories at any of these workflow stage IDs. Comma-separated.
--publish-status string Match stories with any of these publish statuses. Comma-separated.
--created string Match stories created in a date range. For example, 2024 or 7d.
--updated string Match stories last updated in a date range.
--last-published string Match stories last published in a date range.
--first-published string Match stories first published in a date range.
--scheduled string Match stories with a pending scheduled publish. Accepts an optional date range.
--where string Match stories with a JSONPath expression. Repeatable. Expressions combine with AND.
--references string Match stories that reference all these story UUIDs. Comma-separated.
--check-references string Report stories with reference issues. Accepts a list of issue types.
--sort string Sort results on the server. For example, updated_at:desc.
--limit integer Stop the run after this many results.
--skip-content boolean Skip the per-story fetch and print story listing metadata only.
--capi-filter boolean Evaluate --where on content from bulk Content Delivery API reads.
--capi-params string Add query parameters to --capi-filter reads. For example, version=published.

The following examples assume you defined a space in the configuration file.

Terminal window
# Find published stories that mention "pricing"
storyblok stories find "pricing" --publish-status published
# Find stories whose root block is product
storyblok stories find --container-block product
# Find stories that contain both a hero and a pricing_table block
storyblok stories find --includes-block hero,pricing_table
# Find stories updated in the last seven days
storyblok stories find --updated 7d
# List every folder, without fetching content
storyblok stories find --entry-type folder --skip-content

The following sections describe the syntax of each filter. To learn which filters need story content, refer to content flags.

--query filters by root-level content fields with the filter query syntax [field][operation]=value.

Terminal window
# Match a value
storyblok stories find --query "[category][in]=electronics"
# Match a case-insensitive wildcard
storyblok stories find --query "[title][like]=*Headphones*"
# Combine several conditions with AND
storyblok stories find --query "[featured][is]=true&[price][gt_int]=100"
# Pass a JSON object, which suits generated queries
storyblok stories find --query '{"component":{"in":"product"}}'

--container-block product is shorthand for --query "[component][in]=product". The command merges both flags into one query.

The Management API drops a filter that contains an unknown operator, so the search returns every story that the other filters match. To prevent this, the command exits with a usage error when --query has any of the following problems:

  • An unknown operator, such as [eq]
  • The same field and operator as --container-block
  • No readable clause

--where covers cases beyond --query: nested blocks, any-depth search, regular expressions, >= and <= comparisons, and story properties. Expressions follow JSONPath (RFC 9535), and a story matches when the expression selects at least one node.

Two rules cover most expressions:

  • @ is the node under test. $..[?(@.component == "hero")] tests every block in the story.
  • $ is the story. Use $ for story properties: $[?($.updated_at > "2025-01-01")] tests the story itself. $[?(@.updated_at > …)] tests each property value of the story instead and matches nothing.

A $.. expression runs without errors over any content, including strings, numbers, and null.

Expressions support the function extensions match() and search() for regular expressions, as well as length(), count(), and value().

Wrap each expression in single quotes, and use double quotes for strings inside the expression. Inside double quotes, shells such as bash and zsh expand $[ before the command receives the expression.

Terminal window
# Find stories with an asset that has empty alt text
storyblok stories find --where '$..[?(@.fieldtype == "asset" && @.alt == "")]'
# Match a nested field with a regular expression
storyblok stories find --where '$..[?match(@.sku, "SB-[0-9]+")]'
# Narrow with a server-side filter first, then refine with --where
storyblok stories find --includes-block hero --where '$..[?(@.component == "hero" && @.active == false)]'
# Combine several expressions with AND
storyblok stories find \
--where '$..[?(@.component == "hero" && @.headline != "")]' \
--where '$..[?(@.component == "pricing_table" && @.currency == "EUR")]'

--publish-status matches stories by publish status:

Value Matches
published Published stories without unpublished changes
published-with-changes Published stories with unpublished changes
draft Stories that were never published or that someone unpublished

Separate values with commas to match any listed status. Folders have a publish status as well. Add --entry-type story to exclude them.

Terminal window
# Find every published story, with or without unpublished changes
storyblok stories find --publish-status published,published-with-changes

Five flags match stories by date. Each flag takes a date range.

Flag Story property
--created created_at
--updated updated_at
--last-published published_at, the latest publish
--first-published first_published_at
--scheduled The date of a pending scheduled publish

A date range takes one of the following forms:

Value Matches
30m, 12h, 7d, 2w Within the last 30 minutes, 12 hours, 7 days, or 2 weeks
3mo, 1y Within the last three months or the last year
2024, 2024-06 That whole year or month
2024-06-15 That whole day
2024-06-15T09:00+02:00 That minute, as an ISO 8601 date and time with an optional offset
2024-06-01..2024-06-30 Between two dates, both included
2024-06-01.. On or after a date
..2024-06-30 On or before a date, including that whole day
30d..7d Between 30 and 7 days ago, in either order
7d..now Between seven days ago and now, in either order

Each end of a range covers the whole period it names. 2024-01..2024-03 runs from the start of January to the end of March. Durations count back from the start of the run, and now stands for that moment. Duration units are lowercase: m means minutes, and mo means months. A date or time without an offset is in Coordinated Universal Time, like the dates in the output.

Terminal window
# Find stories created in 2024 below a path prefix
storyblok stories find --starts-with en/blog --created 2024
# Find stories first published in the third quarter of 2024
storyblok stories find --first-published 2024-07..2024-09
# Find published stories whose unpublished changes are older than 90 days
storyblok stories find --publish-status published-with-changes --updated ..90d
# Find stories scheduled to publish in the next seven days
storyblok stories find --scheduled 7d

A story matches only if it has the date. For example, a story that was never published has no published_at.

Pending publishes are always in the future. Durations in --scheduled count forward: --scheduled 7d matches publishes within the next seven days and also matches overdue publishes: stories whose scheduled time has passed but that are still unpublished. Without a range, --scheduled matches every pending scheduled publish. In that case, place the search text before --scheduled, or --scheduled takes the text as its range. A story matches when any of its languages has a pending scheduled publish in the range.

--sort sorts results on the server. --limit returns the top results of the sort order.

Terminal window
# Sort by most recent update
storyblok stories find --sort updated_at:desc
# Sort by publish date, oldest first, then by slug
storyblok stories find --sort published_at:asc,slug:asc
# Sort by a content field as a number instead of text
storyblok stories find --sort content.price:desc:float
# Print the 10 most recently updated stories
storyblok stories find --sort updated_at:desc --limit 10

The format is field[:direction[:cast[:nulls]]], with commas between multiple fields:

Part Description
field A story property, such as updated_at, slug, or name, or a content field with a content. prefix, such as content.price.
direction asc or desc. Defaults to asc.
cast int or float. Without a cast, values sort as text.
nulls nulls_first or nulls_last. Defaults to nulls_last.

--limit accepts a positive integer and stops the run once the command prints that many results. A run that stops at the limit exits with code 0.

--references matches every story that references a story UUID. The search covers reference fields, link fields, and links in rich text. The search ignores references from a story to itself. Comma-separated UUIDs combine with AND. A story matches only if it references every one.

Terminal window
# Find stories that reference a story
storyblok stories find --references "0c4f0f4a-9b5e-4c3a-8e7d-2f1a6b8c9d0e"
# Combine a reference search with other filters
storyblok stories find --references "0c4f0f4a-9b5e-4c3a-8e7d-2f1a6b8c9d0e" --container-block product --publish-status published

--check-references checks the references in each story that the other filters return, and prints only the stories with problems. The check includes field-level translations of reference fields, such as author__i18n__de.

The check reports three issue types:

Type Description
broken The target UUID is missing from the space.
unpublished The target exists but has no published version. Folders are exempt.
stale_url The cached URL of a link field differs from the current path of the target.

A reference can be both unpublished and stale_url, and the command reports both. To report only some types, pass a comma-separated list. For example, --check-references broken.

In a terminal, an issues column summarizes the issues of each story. For example, 2 broken, 1 stale_url. In JSON Lines output, each reported story carries a _ref_issues array:

{
"id": 123,
"full_slug": "blog/my-post",
"_ref_issues": [
{
"type": "stale_url",
"ref_type": "multilink",
"target_uuid": "def-456",
"cached_url": "/about/old-slug",
"actual_url": "about/new-slug",
"field_path": "content.hero.cta_link"
},
{
"type": "broken",
"ref_type": "relation",
"target_uuid": "ghi-789",
"field_path": "content.author"
}
]
}

ref_type is multilink, richtext, or relation. actual_url appears only when the command finds the target, and cached_url appears only for reference types that store one.

The command applies --where after the check. An expression can filter by _ref_issues:

Terminal window
# Check every reference in the space
storyblok stories find --check-references
# Report issues in link fields only
storyblok stories find --check-references --where '$._ref_issues[?(@.ref_type == "multilink")]'

The check reads the content of every story it checks. For a space-wide check, add --capi-filter to read that content in bulk. The command warns about the gaps that this creates.

A filter that must check content costs one per-story fetch, which is the slowest part of a run. Every filter runs without story content, except --check-references and a --where expression that reads content.

Two flags change how a run gets content:

  • --skip-content skips the per-story fetch.
  • --capi-filter reads content in bulk and fetches only the matching stories.

The flags are independent. A run follows one of four pipelines:

Flags --where can read content Output includes content Per-story fetches
None Yes Yes One per story in scope
--skip-content No No None
--capi-filter Yes Yes One per match
--capi-filter --skip-content Yes No None

If the search doesn’t read content, use --skip-content. If it does, use --capi-filter, and add --skip-content when the output doesn’t need content. Searches that need data that exists only in the Management API story, such as field-level translations or translated_stories, use neither flag.

The output is the story object as the listing returns it, without content. Use --skip-content for inventories, slug and ID lists, and publish status counts.

  • --where reads story properties only, such as full_slug, tag_list, or stages. An expression on content matches nothing.
  • --check-references is unavailable, and the command rejects the combination.
  • The content key is absent. A script such as jq '.content.component' prints null.

With --capi-filter, the command evaluates --where on content from bulk Content Delivery API (CAPI) reads and fetches only the matching stories from the Management API (MAPI). The output is always the MAPI story. A selective filter turns minutes into seconds. A filter that keeps almost every story gains nothing.

--capi-filter requires at least one --where expression. With --check-references, --capi-filter needs no expression and serves as a bulk content source for the check. To exclude folders, which have no CAPI content and cost a per-story fetch each, add --entry-type story.

CAPI content differs from MAPI content in two ways:

  • Field-level translations are absent. The command rejects an expression with an __i18n__ key. To filter on a translation, request the language with --capi-params and match the base field name.
  • Draft content includes _editable. An expression on _editable matches only with --capi-filter.

Folders, unpublished stories (with version=published), and stories in a failed batch have no CAPI content. The command fetches these stories from the MAPI and tests them as usual. With --skip-content, it tests them on story properties only. An expression on content excludes them. The command warns with their count when the run ends.

--check-references --capi-filter skips references in field-level translations. For stories without CAPI content, it runs the check without their content and reports no issues for them. The command warns about both gaps.

--capi-params adds query parameters to the bulk content reads. For example, version=published decides matches against the published version, and language=de decides them against the German version. Pass key-value pairs separated by commas: version=published,language=de.

  • --capi-params requires --capi-filter.
  • version=published requires --publish-status published, --publish-status published-with-changes, or both.
  • --check-references accepts every parameter except language.

The format depends on the destination of stdout. When stdout is a pipe or a file, the command prints JSON Lines. When stdout is a terminal, the command prints a table. In both cases, the command writes warnings and the run summary to stderr.

The command prints each match as a complete story object on a single line. JSON Lines has one self-contained JSON document per line. Every line includes id, uuid, and full_slug.

The command prints each result as it finds it. You can process the first line before the run ends. If the program that reads the output stops, as head does, the run stops. A reference check is the exception: it prints its results at the end of the run.

For ways to process results, refer to process results with other tools.

In a terminal, the command shows its progress during the run and prints a table when the run ends. The table has ID, NAME, and FULL SLUG columns, and adds an issues column with --check-references. The full slug of a folder ends in /.

ID NAME FULL SLUG
123456789 Running shoes en/products/running-shoes
123456790 Trail shoes en/products/trail-shoes

A table taller than the terminal opens in less. To turn the pager off, set PAGER=cat. Scripts read the JSON Lines output, which is the stable format. To get JSON Lines in a terminal, pipe the output into another command, such as | jq . or | cat.

Some flags add keys that start with an underscore to each line. These annotations belong to the command: --check-references adds _ref_issues. Underscored keys inside content, such as _uid, belong to the story.

Remove annotations before you send a story back to the Management API:

Terminal window
storyblok stories find --check-references | jq --compact-output 'with_entries(select(.key | startswith("_") | not))'
Code Meaning
0 The run completed. This includes runs that stop at --limit, runs that stop when the receiving program closes the pipe, and --capi-filter runs that recover from a failed batch.
1 Part of the run failed, such as a listing page or a per-story fetch. The summary on stderr names the failure, and the results may be incomplete.
2 A usage error, such as an invalid flag value. The command fetched nothing.

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.