CMS API: Video Search v2

This topic explains the syntax for using version 2 of video search, supported by the CMS API.

Introduction

Version 2 of the video search used by the CMS API is the recommended syntax for new integrations. It supports the features that most v1 searches rely on, including date ranges (both the .. and [a TO b] forms), NOW, relative offsets, comma-separated values, and reference ID prefix matching. If you have an existing q integration, see Preparing to migrate. To see Version 1, go to CMS/Playback API: Videos Search v1.

Choosing which syntax to use is a simple matter of choosing the appropriate URL parameter:

  • To use the new v2 search:
        .../videos?query={search_string}
  • To use the original search:
        .../videos?q={search_string}

Basics

The basic element of a search string is a search term, which may be prefixed by a field name. If the field name is included, only that metadata field will be searched. Otherwise, several fields (listed below) will be searched.

For example:

Basic Search
Search string What will be returned
bird Videos that that the word "bird" in the fields listed below
name:bird Videos that have the word "bird" in the name (title) will be returned.

When you provide no field name to search, the request searches for that value in these eight fields:

  • id
  • name
  • description
  • long_description
  • tags
  • reference_id
  • custom_fields (all custom fields together)
  • variants

The supported fields to search are:

Supported Search Fields
Field Legal values
name strings or quoted strings
text strings or quoted strings (searches the name, description, and long_description)
tags strings or quoted strings. A comma-separated list matches videos having any of the values. Read Tag lists before combining a comma with a + modifier.
custom_fields strings or quoted strings (searches all custom fields - you can also use a specific custom field internal name). Values may be comma-separated: see Custom field lists.
reference_id string or quoted string. An unquoted value matches reference IDs that equal or begin with the value; a quoted value matches exactly. See Searching by reference ID.
playable true only
playable:true
See Playable videos.
clip_source_video_id string
clip_source_video_id:video_id
state ACTIVE, INACTIVE, PENDING, DELETED (only videos deleted within the past 10 days will be returned)
updated_at datetime or range (details below)
created_at datetime or range (details below)
schedule.starts_at datetime or range (details below)
schedule.ends_at datetime or range (details below)
published_at datetime or range (details below)
complete true or false
ad_keys quoted strings
ad_keys:"sport=swimming&category=aquatics"
economics string
economics:AD_SUPPORTED
economics:FREE
original_filename string or quoted strings
original_filename:oystercatcher.mp4
projection string
projection:equirectangular
cue_points string
has_cue_points:true
has_cue_points:false
geo string or quoted strings
geo.{property}:{value}
geo.countries:fr
images string
has_images:true
has_images:false
link string
has_link:true
has_link:false
sharing string or quoted string
sharing.{property}:{value}
sharing.by_external_acct:true
sharing.source_id:"parent_video_id"
sharing.by_id:"shared_from_account_id"
sharing.to_external_acct:"shared_to_account_id"
variants string or quoted string
variants.{language}.{property}:{value}
variants.en-US.name:eng
labels quoted string
labels:"/root/level1"
delivery_type string
delivery_type:dynamic_origin
delivery_type:live_origin
folder_id string
folder_id:6633bce70a73585a5816b82a
playback_rights_id string or quoted string
playback_rights_id:635011821
ingestion_profile_id string
ingestion_profile_id:583efe11e4b0a3fb4b6f0b1e
has_digital_master string
has_digital_master:true
has_digital_master:false
has_transcripts string
has_transcripts:true
has_transcripts:false
has_text_tracks string
has_text_tracks:true
has_text_tracks:false
drm_disabled string
drm_disabled:true
drm_disabled:false
offline_enabled string
offline_enabled:true
offline_enabled:false
created_by string
created_by.{property}:{value}
created_by.type:user
updated_by string
updated_by.{property}:{value}
updated_by.type:user
duration datetime or range (details below)
duration:[* TO 1450000]
plays_total range (details below)
plays_total:[0 TO 100000]
plays_trailing_week range (details below)
plays_trailing_week:[0 TO 100000]
shared_at date or range (details below)
shared_at:[2023-11-02T16:21:34.905Z TO 2024-05-02T16:21:34.908Z]

In both the examples shown above, videos that do not have the word "bird" in any relevant field might still be returned. The next section explains how to limit search results to only videos that have the specified terms.

Ignored words

Certain words are ignored in search strings because they are so common that they are likely to return many results unrelated to what you are actually searching for. Below is a list of words that are ignored by search:

"a", "an", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"

In addition, non-alphanumeric characters such as hyphens, underscores, line-breaks, "$", "&", "*", etc. are treated as word delimiters. For example, a search string like small-town will be treated as small town.

What is stemming?

Video fields that support stemming return words that have the stem of the search word in common. In addition, stemming supports entering whole words only, not partial words:

  • Example 1: Searching on running will return results containing: running, run, runs
  • Example 2: Searching on vid will not return results containing: video

Search with stemming works in the following fields:

  • custom_fields
  • description
  • name
  • long_description
  • tags
  • labels

There are some modifiers that help you limit search results to exactly the videos you want.

Search Modifiers
Modifier Description Examples
+ Prefixing a search term with the plus (+) sign signifies that the returned videos must have the specified term
  • +bird (returns only videos with "bird" in the fields listed above)
  • +tags:bird (returns only videos with "bird" in the tags)
- or NOT Prefixing a search term with the minus (-) sign or NOT signifies that the returned videos must not have the specified term
  • -birds or NOT birds (returns only videos that do not have "bird" in the fields listed above)
  • -name:birds or NOT name:birds (returns only videos that do not have "bird" in the name)
(term) AND (term)
or
(term) OR (term)
The logical AND and OR operators allow you to combine multiple search terms for complex queries
  • (economics:FREE) AND (has_text_tracks:true) (would return videos that have both "FREE" in economics and has_text_tracks)
  • (original_filename:oystercatcher.mp4) OR (duration:[* TO 14500]) (would return videos that have either "oystercatcher.mp4" in the original_filename and duration up to 14500 seconds)
  • (variants.en-US.name:"My Variant") AND (has_images:true) AND (NOT labels:"/root/label5") (would return videos that have both "My Variant" in the Variant name and has images, but not the label "/root/label5")

Phrase search

You can search for a phrase (rather than a single word) by placing in quotation marks:

  • "blue heron"
  • name:"blue heron"

Quoting values

Quotation marks are not only for phrases. Put double quotes around any field value that contains anything other than letters, digits, underscores, and periods. In particular, quote a value that contains a space or any of these characters:

:   /   |   =   $   %   &   !   *

The list is illustrative rather than exhaustive. The rule above is the reliable one: if the value is not made up only of letters, digits, underscores, and periods, quote it. An apostrophe, for example, is not in the list but is covered by the rule.

Quote the value
Instead of Write
tags:video approved tags:"video approved"
tags:modelid:414 tags:"modelid:414"
tags:/pgatour/tours/champions-tour tags:"/pgatour/tours/champions-tour"
tags:lang=fr tags:"lang=fr"
tags:on_air|noticias|clips tags:"on_air" tags:"noticias" tags:"clips", if the pipe was meant to separate three tags; tags:"on_air|noticias|clips" if it is one tag containing pipes

Leaving the quotes off has one of two outcomes, depending on the character.

Some characters split the value

A space, or one of / | = $, splits the value. The first piece stays attached to the field you named, and every remaining piece becomes a free text search across the eight default fields. This returns no error, so an incorrect result can go unnoticed.

tags:women's health asks for the tag women's plus the free text health, and can return far more videos than intended. tags:"women's health" asks for the one tag. In the other direction, tags:$gaeilge searches for a different tag (gaeilge) than the one you asked for.

Some characters make the query unreadable

A : or a * inside a value cannot be interpreted at all, so the whole request is rejected with HTTP 400 and a message naming the character and its position. tags:modelid:414 is refused, while tags:"modelid:414" returns the videos you asked for. See When a query cannot be parsed.

The remaining characters in the list above, % & !, should be quoted for the same reason even though the exact outcome varies. Quoting is the one action that is correct in every case.

Wildcards are not supported

The asterisk is not a wildcard in a text value. Outside a date or numeric range, where * means an open bound, an asterisk in a value cannot be interpreted, so tags:spring* is rejected with HTTP 400 (unexpected input '*' at position 11) rather than matching every tag starting with "spring".

What to do depends on what you meant by it:

  • If the asterisk is literally part of the value you are looking for, quote it so the query stays valid: tags:"spring*". Punctuation is treated as a word delimiter in the text being matched (see Ignored words), so the asterisk will not narrow the match by itself.
  • If you meant it as a wildcard, remove it. To match the beginning of a value, use reference_id, which prefix-matches by design (see below). No other field supports prefix matching.

Tag lists

A comma-separated list of tags matches videos that have any of the values:

    tags:sea,mammal

returns videos tagged sea or mammal.

See Quoting values for which characters need the quotes.

Custom field lists

Custom field values may also be comma-separated, with the same "any of these" meaning:

    +product_number:H517497,H517498

matches videos whose product_number is either value.

Searching by reference ID

reference_id does not behave like the other string fields. An unquoted value matches reference IDs that equal or begin with that value, with exact matches ranked first. Matching is case-insensitive.

reference_id
Search string Matches
reference_id:abc abc and abc123
reference_id:"abc" abc only

Do not append an asterisk to get a prefix search. reference_id:abc already does it, and reference_id:abc* is not supported.

Playable videos

There are two ways to limit results to videos that a viewer could play right now.

The playable_only parameter

    .../videos?query=name:bird&playable_only=true

The playable search term

    .../videos?query=%2Bplayable:true%20name:bird

Both apply the same definition. A video is playable when all four of these hold:

  • complete is true, meaning at least one rendition exists
  • state is ACTIVE (so INACTIVE, PENDING and DELETED videos are excluded)
  • schedule.starts_at is in the past, or is not set
  • schedule.ends_at is in the future, or is not set

A video with no schedule at all counts as playable, provided the other conditions hold.

Date/Times and Ranges

You can express a date range in either of two forms:

  • Bracket syntax: updated_at:[2020-01-01T00:00:00.000Z TO 2020-12-31T00:00:00.000Z]
  • Shorthand syntax: updated_at:2020-01-01..2020-12-31

Both bounds are inclusive. Both forms accept dates, date-times and NOW. Relative offsets are the one value type the two forms do not treat alike, so they have their own section below.

Date/Time Formats
Value Format Example
Date yyyy-MM-dd 2019-09-30
Date-Time yyyy-MM-ddThh:mm:ssZ or yyyy-MM-ddThh:mm:ss.sssZ 2019-09-30T14:24:33Z, 2019-09-30T14:24:33.512Z
Relative offset a sign, a number, and a unit -7days in the shorthand syntax, -7d in either. See Relative offsets.
NOW (case-insensitive) NOW schedule.ends_at:NOW..
Wildcard, for an open bound in the bracket syntax *
  • [2019-09-30T14:24:33.512Z TO *]
  • [* TO 2019-09-30T14:24:33.512Z]
  • [500 TO *]
  • [* TO 340000]

Milliseconds and time zones

Milliseconds are optional. When you include them, three digits is the usual form (2019-09-30T14:24:33.512Z); six digits are also accepted.

The two syntaxes differ in one respect, the time zone designator:

  • In the bracket syntax the Z suffix is required, so times are always UTC.
  • In the shorthand syntax a date-time may carry either Z or a numeric UTC offset: updated_at:2026-08-07T14:30:47-07:00..NOW. The offset must be the full ±HH:MM form. ±HH and ±HHMM are not accepted.

Dates without a time

A bare yyyy-MM-dd used as the lower bound means the start of that day. Used as the upper bound, it covers the whole of that day, in both syntaxes. So both of these match everything created on 31 December:

created_at:2020-12-31..2020-12-31
created_at:[2020-12-31 TO 2020-12-31]

Open ended ranges

In the shorthand syntax, leave off either end:

  • created_at:2020-01-01.. from a date onward
  • created_at:..2020-01-01 up to a date

In the bracket syntax, use * for the open end: created_at:[2020-01-01 TO *].

Relative offsets

A relative offset is a sign, a number, and a unit, measured from the current time: updated_at:-7days..NOW.

The units are YEAR(S), MONTH(S), DAY(S) or D, HOUR(S) or H, and MINUTE(S). Case does not matter, with one exception: in the shorthand syntax the bare letter M is rejected as ambiguous, while a bare lowercase m means minutes. The bracket syntax does not reject it: there M means months. Spell out MINUTE(S) or MONTH(S) rather than relying on a single letter for either.

Below are some sample date/time search strings.

Sample Data/Time Searches
Search String Description
+updated_at:[2019-09-30T00:00:00.000Z TO 2019-10-07T00:00:00.000Z] Videos updated between 30 Sep 2019 and 7 Oct 2019
+created_at:2019-09-30..2019-09-30 Videos added on 30 Sep 2019
+created_at:[2019-09-30T14:00:00.000Z TO 2019-09-30T16:30:00.000Z] Videos added between 2:00PM and 4:30PM (UTC) on 30 Sep 2019
+created_at:[* TO 2019-09-30T00:00:00.000Z] Videos added before 30 Sep 2019
+updated_at:-7days..NOW Videos updated in the past 7 days
+schedule.starts_at:..NOW +schedule.ends_at:NOW.. Videos whose availability window includes this moment

Searching for clips by source video

The clip_source_video_id field identifies the original source video from which a clip was created.

When a clip is generated from a live job (for example, via the Brightcove Studio Live module), the resulting clip includes a metadata property called clip_source_video_id. This property stores the video ID of the original live job that the clip was derived from.

This field can be used in CMS API search queries to retrieve all clips created from a specific live video. For instance, when viewing a live job in Brightcove Studio and opening the Saved Clips list, the Studio interface calls the Get Videos API with a query that filters by clip_source_video_id, returning all clips associated with that live job.

Example Query

GET https://cms.api.brightcove.com/v1/accounts/{account_id}/counts/videos?query=+clip_source_video_id:{source_video_id}

Use Cases

  • Retrieve all clips generated from a particular live job or source video.
  • Organise and manage clips programmatically using the CMS API.

When a query cannot be parsed

If the search string does not follow the syntax on this page, the request is rejected with HTTP 400, and the response body carries a message that says what could not be read and where:

unexpected input ':' at position 12

If you need to recognize these rejections in code, match on the message text: the JSON structure around it is not part of the search contract and may change. The count endpoints reject the same search strings in the same way.

Reading the message

  • The position is zero-based, counted in characters from the start of the decoded search string. In tags:modelid:414, position 12 is the second colon.
  • Two message forms cover most cases: unexpected input '<text>' at position <n> when something appears where it cannot, and unexpected end of input at position <n> when the string stops early, for example with an unclosed parenthesis.
  • A few checks produce their own message instead. The clearest example is the ambiguous relative-time unit described under Relative offsets.
Rejected search strings
Search string Message What to change
tags:modelid:414 unexpected input ':' at position 12 Quote the value: tags:"modelid:414"
tags:spring* unexpected input '*' at position 11 See Wildcards are not supported
updated_at:[not-a-date TO now] unexpected input 'not-a-date' at position 12 Use one of the accepted date formats
updated_at:[2020-01-01 TO] unexpected input ']' at position 25 Supply the upper bound, or use * for an open one
name:(foo unexpected end of input at position 9 Close the parenthesis

Known issues

  • Always send a sort parameter when paging. If you do not, results are ordered by relevance, and relevance order is not stable enough to page through: the same video can appear on two pages, or on none. This is the cause of the duplicate results that are sometimes reported.

    Any sort field gives you a stable order. Note that sorting on updated_at means a video that changes while you are paging can move behind a page you already fetched, so an integration that cannot tolerate missed videos should sort on created_at.

  • A search response does not carry a total. When you need to know how many videos match a query, send the same query to the count endpoint (/counts/videos), which returns an exact count. Do not try to derive a total from a page of search results.

For general search instructions in Video Cloud, see Media Module Search.