Search
Search across visual scenes, speech, and on-screen text in one call. Returns ranked video segments with relevance scores, timestamps, and source links.
Try it
Copy and run. Replace YOUR_CERUL_API_KEY with your actual key.
search.sh
curl "https://api.cerul.ai/v1/search" \
-H "Authorization: Bearer YOUR_CERUL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "Sam Altman views on AI video generation"
}'search.py
import requests
response = requests.post(
"https://api.cerul.ai/v1/search",
headers={
"Authorization": "Bearer YOUR_CERUL_API_KEY",
"Content-Type": "application/json",
},
json={"query": "Sam Altman views on AI video generation"},
)
print(response.json())search.js
const response = await fetch("https://api.cerul.ai/v1/search", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_CERUL_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
query: "Sam Altman views on AI video generation",
}),
});
const data = await response.json();
console.log(data);search.ts
const response = await fetch("https://api.cerul.ai/v1/search", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_CERUL_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
query: "Sam Altman views on AI video generation",
}),
});
type SearchResult = {
id: string;
score: number;
rerank_score?: number | null;
url: string;
title: string;
snippet: string;
transcript?: string | null;
thumbnail_url?: string | null;
keyframe_url?: string | null;
duration: number;
source: string;
speaker?: string | null;
timestamp_start?: number | null;
timestamp_end?: number | null;
};
type SearchResponse = {
results: SearchResult[];
answer?: string | null;
credits_used: number;
credits_remaining: number;
request_id: string;
};
const data: SearchResponse = await response.json();
console.log(data);With reranking: Add "ranking_mode": "rerank" to use LLM-based reranking for higher relevance. Uses the Jina Reranker by default.
Parameters
Only query is required. All other fields are optional.
| Name | Type | Required | Description |
|---|---|---|---|
| query | string | Yes | Natural-language search query. Max 400 characters. |
| max_results | integer | No | Number of results to return. 1–50, default 10. |
| include_answer | boolean | No | Generate an AI summary grounded in the matched evidence. Default false. Costs 2 credits instead of 1. |
| ranking_mode | string | No | "embedding" (default) for vector similarity, or "rerank" for LLM-based reranking with higher relevance. |
| filters | object | No | Narrow results by speaker, source, date, or duration. See Filters below. |
Filters
Pass these inside the filters object. All are optional.
| Name | Type | Description |
|---|---|---|
| speaker | string | Filter by speaker name. |
| source | string | Filter by content source (e.g. "youtube"). |
| published_after | string | ISO date (YYYY-MM-DD). Only return content published after this date. |
| min_duration | integer | Minimum video duration in seconds. |
| max_duration | integer | Maximum video duration in seconds. |
with-filters.sh
curl "https://api.cerul.ai/v1/search" \
-H "Authorization: Bearer YOUR_CERUL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "AGI timeline discussion",
"max_results": 5,
"include_answer": true,
"ranking_mode": "rerank",
"filters": {
"speaker": "Sam Altman",
"published_after": "2024-01-01",
"min_duration": 60,
"max_duration": 7200,
"source": "youtube"
}
}'Response
| Field | Type | Description |
|---|---|---|
| results | array | Array of matched video segments. |
| results[].id | string | Unique result identifier. |
| results[].score | number | Relevance score, 0.0 to 1.0. |
| results[].rerank_score | number | null | Reranking score when ranking_mode is set to rerank. |
| results[].url | string | Cerul tracking URL — redirects to the source video. |
| results[].title | string | Video title. |
| results[].snippet | string | Matched transcript or visual description. |
| results[].transcript | string | null | Full ASR transcript text for the matched segment. Null for visual-only units. |
| results[].thumbnail_url | string | null | Preview image URL. |
| results[].keyframe_url | string | null | Representative keyframe image when available. |
| results[].duration | integer | Video duration in seconds. |
| results[].source | string | Content source (e.g. "youtube"). |
| results[].speaker | string | null | Speaker name, if detected. |
| results[].timestamp_start | number | null | Start time in seconds. |
| results[].timestamp_end | number | null | End time in seconds. |
| answer | string | null | AI-generated summary. Only present when include_answer is true. |
| credits_used | integer | Credits consumed by this request. |
| credits_remaining | integer | Remaining spendable credits after this request. |
| request_id | string | Unique request identifier in the form req_<24-hex-chars>. |
response.json
{
"results": [
{
"id": "unit_hmtuvNfytjM_1223",
"score": 0.93,
"rerank_score": 0.97,
"url": "https://cerul.ai/v/a8f3k2x",
"title": "Sam Altman on AI video generation",
"snippet": "Current AI video generation tools are improving quickly but still constrained by controllability.",
"transcript": "Current AI video generation tools are improving quickly but still constrained by controllability, production reliability, and the ability to steer outputs precisely.",
"thumbnail_url": "https://i.ytimg.com/vi/hmtuvNfytjM/hqdefault.jpg",
"keyframe_url": "https://cdn.cerul.ai/frames/hmtuvNfytjM/f0123.jpg",
"duration": 7200,
"source": "youtube",
"speaker": "Sam Altman",
"timestamp_start": 1223.0,
"timestamp_end": 1345.0
}
],
"answer": "Sam Altman frames current AI video generation tools as improving quickly but still constrained by controllability.",
"credits_used": 2,
"credits_remaining": 998,
"request_id": "req_9f8c1d5b2a9f7d1a8c4e6b02"
}Daily free
Today: 10 / 10 free searches reset every UTC day
Sign in to see your live remaining count here. The first 10 searches each day still cost 0 credits.
Errors
Every /v1 error response carries an error.code (coarse category) plus an optional error.subcode (fine-grained reason), a human-readable error.message, and a request_id that matches the x-request-id response header. Clients and agents should branch on error.subcode ?? error.code — this preserves backward compatibility for callers that only understand the coarse code.
error-envelope.json
{
"error": {
"code": "forbidden",
"subcode": "insufficient_credits",
"message": "Insufficient credits for this request",
"request_id": "req_9f8c1d5b2a9f7d1a8c4e6b02"
}
}| Status | Code | Subcode | Description |
|---|---|---|---|
| 400 | invalid_request | — | Generic schema or request validation failure — fix the body and do not retry unchanged. |
| 401 | unauthorized | missing_authorization | No Authorization header on /v1/*. Add Authorization: Bearer cerul_... and retry. |
| 401 | unauthorized | invalid_authorization_header | Authorization header is not a Bearer token (wrong scheme or empty Bearer). |
| 401 | unauthorized | malformed_api_key | API key fails format validation. Check copy/paste and the cerul_ prefix. |
| 401 | unauthorized | invalid_api_key | Key format is valid but the key is unknown. Regenerate it in the dashboard. |
| 403 | forbidden | api_key_inactive | API key was revoked or deactivated. Regenerate or reactivate the key. |
| 403 | forbidden | billing_hold | Account is under billing review. Contact support; do not auto-retry. |
| 403 | forbidden | insufficient_credits | Wallet balance is below request cost. Check GET /v1/usage and prompt for top-up. |
| 404 | not_found | — | Unknown /v1/* path or resource. |
| 422 | invalid_request | invalid_image | Search image could not be decoded, downloaded, or is unsupported. Text-only search still works. |
| 429 | rate_limited | rate_limit_exceeded | Per-key rate limit exceeded. Honor Retry-After plus jitter before retrying. |
| 500 | internal_error | — | Unhandled internal failure. Retry with bounded exponential backoff and include request_id if it repeats. |
Only auto-retry rate_limit_exceeded, rate_limited, and internal_error. For auth, billing, and invalid-request failures, stop retrying and surface the problem. 429 responses include a Retry-After header in seconds.