Get a view
CURL *hnd = curl_easy_init();
curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "GET");curl_easy_setopt(hnd, CURLOPT_URL, "https://api.focal.inc/v1/library-views/ac5a4a25-8554-4c96-8ade-cb57a1c43b93");
struct curl_slist *headers = NULL;headers = curl_slist_append(headers, "Authorization: Bearer <token>");curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);
CURLcode ret = curl_easy_perform(hnd);using System.Net.Http.Headers;var client = new HttpClient();var request = new HttpRequestMessage{ Method = HttpMethod.Get, RequestUri = new Uri("https://api.focal.inc/v1/library-views/ac5a4a25-8554-4c96-8ade-cb57a1c43b93"), Headers = { { "Authorization", "Bearer <token>" }, },};using (var response = await client.SendAsync(request)){ response.EnsureSuccessStatusCode(); var body = await response.Content.ReadAsStringAsync(); Console.WriteLine(body);}package main
import ( "fmt" "net/http" "io")
func main() {
url := "https://api.focal.inc/v1/library-views/ac5a4a25-8554-4c96-8ade-cb57a1c43b93"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close() body, _ := io.ReadAll(res.Body)
fmt.Println(res) fmt.Println(string(body))
}HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.focal.inc/v1/library-views/ac5a4a25-8554-4c96-8ade-cb57a1c43b93")) .header("Authorization", "Bearer <token>") .method("GET", HttpRequest.BodyPublishers.noBody()) .build();HttpResponse<String> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());System.out.println(response.body());OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder() .url("https://api.focal.inc/v1/library-views/ac5a4a25-8554-4c96-8ade-cb57a1c43b93") .get() .addHeader("Authorization", "Bearer <token>") .build();
Response response = client.newCall(request).execute();import axios from 'axios';
const options = { method: 'GET', url: 'https://api.focal.inc/v1/library-views/ac5a4a25-8554-4c96-8ade-cb57a1c43b93', headers: {Authorization: 'Bearer <token>'}};
try { const { data } = await axios.request(options); console.log(data);} catch (error) { console.error(error);}const url = 'https://api.focal.inc/v1/library-views/ac5a4a25-8554-4c96-8ade-cb57a1c43b93';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}val client = OkHttpClient()
val request = Request.Builder() .url("https://api.focal.inc/v1/library-views/ac5a4a25-8554-4c96-8ade-cb57a1c43b93") .get() .addHeader("Authorization", "Bearer <token>") .build()
val response = client.newCall(request).execute()use reqwest;
#[tokio::main]pub async fn main() { let url = "https://api.focal.inc/v1/library-views/ac5a4a25-8554-4c96-8ade-cb57a1c43b93";
let mut headers = reqwest::header::HeaderMap::new(); headers.insert("Authorization", "Bearer <token>".parse().unwrap());
let client = reqwest::Client::new(); let response = client.get(url) .headers(headers) .send() .await;
let results = response.unwrap() .json::<serde_json::Value>() .await .unwrap();
dbg!(results);}curl --request GET \ --url https://api.focal.inc/v1/library-views/ac5a4a25-8554-4c96-8ade-cb57a1c43b93 \ --header 'Authorization: Bearer <token>'wget --quiet \ --method GET \ --header 'Authorization: Bearer <token>' \ --output-document \ - https://api.focal.inc/v1/library-views/ac5a4a25-8554-4c96-8ade-cb57a1c43b93Fetch one saved library view. The view id pins the space, so no space_id query param is needed.
Returns 404 when the view doesn’t exist, is archived, belongs to another organization, or sits outside the token’s space allow-list — the cases share a response so a restricted token can’t probe for out-of-scope views.
Pass fields to trim the response to the properties you actually need — e.g. ?fields=name,html_url returns just those (plus id, always). Unknown names are rejected rather than ignored.
Required scope: media:read. Rate-limit cost: 1.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Example
ac5a4a25-8554-4c96-8ade-cb57a1c43b93Query Parameters
Section titled “Query Parameters”Return only these properties of each library view, instead of the full object. id is always included. Omit for the complete object. Selectable: id, name, space_id, organization_id, view_mode, filter, filter_complete, sort, group_by, visible_fields, order, created_at, updated_at, html_url.
Responses
Section titled “Responses”OK
object
object
Display name. null on a view the app created without one — it renders as “Untitled view” until a name is set.
How the view renders its media: gallery (cards) or grid (table).
The saved search this view runs, in the same vocabulary as POST /v1/media/search — every entry is ANDed. null means the view shows the whole library unfiltered.
include_subfolders, sort and order are absent on purpose: a view doesn’t store folder recursion (the app settles that when the view runs), and result ordering lives in the view’s own sort.
object
Free-text query, ranked by relevance (≤200 chars). Matches names, descriptions, folder names, tags and attribute values; in spaces with Focal AI enabled it also matches text detected in the asset (OCR), speech transcripts, and the visual content itself. A media id here matches that asset exactly.
Restrict to one file type.
Restrict to a folder in the searched space. Subfolders are included unless include_subfolders is false. Unknown folders are rejected rather than ignored.
Restrict to media with this status — an id or name from the space’s catalog (the Space resource’s media_statuses), the same id-or-name contract as a status write.
Restrict to media uploaded by this user (a GET /v1/users id).
Filter on media attribute (custom field) values. Media must satisfy every entry; within an entry, values is any-of. number and date attributes aren’t searchable yet and are rejected.
object
The media attribute (custom field) to filter on — an id from GET /v1/media-attributes.
Values to match, interpreted per the attribute’s type: option ids or names for a tags field, user ids for a user field, "true" / "false" for a checkbox field (exactly one), and the stored text for text / link. Several values match any of them. An option value matching none of the attribute’s options — and a user value that isn’t a user id — is rejected rather than ignored, so a filter never silently widens the result set.
any (default) keeps media matching at least one value; none excludes media matching any of them.
Narrow to media whose ad-platform metrics satisfy every one of these (ANDed, like the other filters). Compares against lifetime totals — { metric: "spend", operator: "greater_than", value: 1000 } means “has spent over 1000 in total”, not “spent over 1000 in some window”. Media with no data for the metric never match.
object
Which metric to compare — one of the keys that appear in a media’s metrics.insights.
How to compare — greater_than, less_than, or equals.
The bound to compare the metric’s lifetime total against.
Whether filter describes the view’s saved search in full. false means the stored search uses a condition this API can’t express (the app’s filter builder is wider than the public vocabulary), so filter under-describes the view — sending it back in a PATCH would drop the rest. Omit filter from a PATCH to leave the saved search untouched.
Result ordering; null means the library default.
object
The column to sort by — a built-in library column key (name, status, type, size, uploadedById, uploadedAt, …) or a media attribute id from GET /v1/media-attributes.
Column the view groups its media by, or null for no grouping. Same vocabulary as sort.field.
Columns shown, in display order. Same vocabulary as sort.field. null means the view never pinned a set and follows the library default.
Position in the space’s view list, ascending. Not guaranteed unique or gap-free.
Browser URL that opens this view in the Focal web app. Hand this to users as a clickable link.
Example
{ "data": { "id": "ac5a4a25-8554-4c96-8ade-cb57a1c43b93", "name": "Approved for Meta", "space_id": "6916bc3d-e1ab-425b-ac25-eefada01a9a2", "organization_id": "b46d9033-24b2-4441-87ad-2151088fde01", "view_mode": "gallery", "filter": { "type": "video", "status": "Approved", "created_after": "2027-03-01" }, "filter_complete": true, "sort": { "field": "uploadedAt", "order": "desc" }, "group_by": "status", "visible_fields": [ "name", "status", "type", "uploadedAt", "uploadedById" ], "order": 1, "created_at": "2027-03-04T10:15:00Z", "updated_at": "2027-03-06T14:42:00Z", "html_url": "https://app.focal.inc/spaces/6916bc3d-e1ab-425b-ac25-eefada01a9a2/library/views/ac5a4a25-8554-4c96-8ade-cb57a1c43b93" }}The request was malformed (invalid cursor, unknown type, validation failure, etc.). error.code is bad_request.
Canonical error envelope. code is stable across releases and intended for programmatic handling; message is human-readable. details only appears for validation failures and lists the offending fields.
object
object
object
Example
{ "error": { "code": "bad_request", "message": "Invalid request body", "details": [ { "field": "space_id", "issue": "Invalid UUID" } ] }}Missing, invalid, or disabled token. error.code is unauthenticated.
Canonical error envelope. code is stable across releases and intended for programmatic handling; message is human-readable. details only appears for validation failures and lists the offending fields.
object
object
object
Example
{ "error": { "code": "unauthenticated", "message": "Bearer token is missing or invalid" }}The token doesn’t have the scope this endpoint requires. error.code is forbidden.
Canonical error envelope. code is stable across releases and intended for programmatic handling; message is human-readable. details only appears for validation failures and lists the offending fields.
object
object
object
Example
{ "error": { "code": "forbidden", "message": "Token doesn't have the required scope" }}Resource doesn’t exist or is outside the token’s space allow-list. The two cases share a response shape so a restricted token can’t probe for the existence of out-of-scope spaces. error.code is not_found.
Canonical error envelope. code is stable across releases and intended for programmatic handling; message is human-readable. details only appears for validation failures and lists the offending fields.
object
object
object
Example
{ "error": { "code": "not_found", "message": "Not found" }}Rate limit exceeded. error.code is rate_limited. Wait the number of seconds in the Retry-After header before retrying. Quotas are per-token; see the “Rate limiting” section at the top of this document.
Canonical error envelope. code is stable across releases and intended for programmatic handling; message is human-readable. details only appears for validation failures and lists the offending fields.
object
object
object
Example
{ "error": { "code": "rate_limited", "message": "Rate limit of 1000 units per 60s exceeded (this request cost 5)" }}Headers
Section titled “Headers”Example
12Seconds to wait before retrying.