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.
storyblok stories find [arguments] [flags]Arguments
Section titled “Arguments”| 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. |
Examples
Section titled “Examples”The following examples assume you defined a space in the configuration file.
# Find published stories that mention "pricing"storyblok stories find "pricing" --publish-status published
# Find stories whose root block is productstoryblok stories find --container-block product
# Find stories that contain both a hero and a pricing_table blockstoryblok stories find --includes-block hero,pricing_table
# Find stories updated in the last seven daysstoryblok stories find --updated 7d
# List every folder, without fetching contentstoryblok stories find --entry-type folder --skip-contentFilters
Section titled “Filters”The following sections describe the syntax of each filter. To learn which filters need story content, refer to content flags.
Content field queries
Section titled “Content field queries”--query filters by root-level content fields with the filter query syntax [field][operation]=value.
# Match a valuestoryblok stories find --query "[category][in]=electronics"
# Match a case-insensitive wildcardstoryblok stories find --query "[title][like]=*Headphones*"
# Combine several conditions with ANDstoryblok stories find --query "[featured][is]=true&[price][gt_int]=100"
# Pass a JSON object, which suits generated queriesstoryblok 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
JSONPath expressions
Section titled “JSONPath expressions”--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.
# Find stories with an asset that has empty alt textstoryblok stories find --where '$..[?(@.fieldtype == "asset" && @.alt == "")]'
# Match a nested field with a regular expressionstoryblok stories find --where '$..[?match(@.sku, "SB-[0-9]+")]'
# Narrow with a server-side filter first, then refine with --wherestoryblok stories find --includes-block hero --where '$..[?(@.component == "hero" && @.active == false)]'
# Combine several expressions with ANDstoryblok stories find \ --where '$..[?(@.component == "hero" && @.headline != "")]' \ --where '$..[?(@.component == "pricing_table" && @.currency == "EUR")]'Publish status
Section titled “Publish status”--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.
# Find every published story, with or without unpublished changesstoryblok stories find --publish-status published,published-with-changesDate filters
Section titled “Date filters”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.
# Find stories created in 2024 below a path prefixstoryblok stories find --starts-with en/blog --created 2024
# Find stories first published in the third quarter of 2024storyblok stories find --first-published 2024-07..2024-09
# Find published stories whose unpublished changes are older than 90 daysstoryblok stories find --publish-status published-with-changes --updated ..90d
# Find stories scheduled to publish in the next seven daysstoryblok stories find --scheduled 7dA 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 order and limits
Section titled “Sort order and limits”--sort sorts results on the server. --limit returns the top results of the sort order.
# Sort by most recent updatestoryblok stories find --sort updated_at:desc
# Sort by publish date, oldest first, then by slugstoryblok stories find --sort published_at:asc,slug:asc
# Sort by a content field as a number instead of textstoryblok stories find --sort content.price:desc:float
# Print the 10 most recently updated storiesstoryblok stories find --sort updated_at:desc --limit 10The 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.
Reference search
Section titled “Reference search”--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.
# Find stories that reference a storystoryblok stories find --references "0c4f0f4a-9b5e-4c3a-8e7d-2f1a6b8c9d0e"
# Combine a reference search with other filtersstoryblok stories find --references "0c4f0f4a-9b5e-4c3a-8e7d-2f1a6b8c9d0e" --container-block product --publish-status publishedReference checks
Section titled “Reference checks”--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:
# Check every reference in the spacestoryblok stories find --check-references
# Report issues in link fields onlystoryblok 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.
Content flags
Section titled “Content flags”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-contentskips the per-story fetch.--capi-filterreads 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.
--skip-content
Section titled “--skip-content”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.
--wherereads story properties only, such asfull_slug,tag_list, orstages. An expression oncontentmatches nothing.--check-referencesis unavailable, and the command rejects the combination.- The
contentkey is absent. A script such asjq '.content.component'printsnull.
--capi-filter
Section titled “--capi-filter”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-paramsand match the base field name. - Draft content includes
_editable. An expression on_editablematches 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
Section titled “--capi-params”--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-paramsrequires--capi-filter.version=publishedrequires--publish-status published,--publish-status published-with-changes, or both.--check-referencesaccepts every parameter exceptlanguage.
Output
Section titled “Output”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.
Piped output
Section titled “Piped output”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.
Terminal output
Section titled “Terminal output”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 SLUG123456789 Running shoes en/products/running-shoes123456790 Trail shoes en/products/trail-shoesA 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.
Annotations
Section titled “Annotations”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:
storyblok stories find --check-references | jq --compact-output 'with_entries(select(.key | startswith("_") | not))'Exit codes
Section titled “Exit codes”| 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?
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