> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.spyglass.so/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.spyglass.so/_mcp/server.

# Search media

POST https://api.spyglass.so/v2/media
Content-Type: application/json

Return creatives. Pass `accountId` to scope to one brand or creator,
or omit it to search the whole corpus. Without `accountId`,
`query` is required.

Limited to 120 requests per minute per user.
Costs $0.0005 per creative returned, charged against your plan's monthly credits. The count field is what you were charged for.
The call bills a minimum of one row even when nothing matches. In that
case `count` is 0 but you are charged for one row.

Reference: https://docs.spyglass.so/api-reference/spyglass-api/search/search-media

## Authentication

- `Authorization` header (bearer token, required) — An API key from Settings → API, prefixed sg_live_.

## Request

### Body (application/json)

This endpoint expects an object.

- `accountId` (MediaPostRequestBodyContentApplicationJsonSchemaAccountId, optional) — Account id given as a platform string or uuid
- `query` (string, optional)
- `channel` (list of enum, optional)
  - Allowed values: `META_AD`, `INSTAGRAM`, `TIKTOK`
- `format` (list of enum, optional)
  - Allowed values: `DPA`, `VIDEO`, `CAROUSEL`, `IMAGE`, `DCO`
- `duration` (list of enum, optional)
  - Allowed values: `0-10`, `10-30`, `30-60`, `60+`
- `status` (enum, optional)
  - Allowed values: `ACTIVE`, `INACTIVE`, `ALL`
- `categories` (list of enum, optional)
  - Allowed values: `Announcement`, `Before and After`, `Brand Story`, `Case Study`, `Comparison`, `Discount or Offer`, `Explainer`, `Founder Story`, `Interface`, `Meme`, `Negative Review`, `Press`, `Question`, `Quiz`, `Quote`, `Social Proof`, `Testimonial`, `Fake Interface`, `Street Interview`, `Unboxing`, `GRWM`, `Day in the life`, `Storytime`, `Skit`, `Reply`, `Text Overlay`, `Greenscreen`
- `taxonomy` (list of string, optional)
- `funnelStage` (list of enum, optional)
  - Allowed values: `UNAWARE`, `PROBLEM_AWARE`, `SOLUTION_AWARE`, `PRODUCT_AWARE`, `MOST_AWARE`
- `language` (list of enum, optional)
  - Allowed values: `English`, `Spanish`, `French`, `German`, `Italian`, `Portuguese`, `Russian`, `Chinese`, `Cantonese`, `Japanese`, `Korean`, `Arabic`, `Hindi`, `Dutch`, `Polish`, `Turkish`, `Vietnamese`, `Thai`, `Burmese`, `Khmer`, `Lao`, `Indonesian`, `Hebrew`, `Swedish`, `Norwegian`, `Danish`, `Finnish`, `Farsi`, `Fijian`, `Greek`, `Czech`, `Hungarian`, `Romanian`, `Ukrainian`, `Malay`, `Tagalog`, `Filipino`, `Bengali`, `Serbian`, `Croatian`, `Bulgarian`, `Slovak`, `Slovenian`, `Estonian`, `Latvian`, `Lithuanian`, `Persian`, `Swahili`, `Amharic`, `Assamese`, `Urdu`, `Afrikaans`, `Basque`, `Catalan`, `Icelandic`, `Georgian`, `Albanian`, `Armenian`, `Azerbaijani`, `Bosnian`, `Galician`, `Gujarati`, `Haitian Creole`, `Kannada`, `Kazakh`, `Kyrgyz`, `Latin`, `Macedonian`, `Malayalam`, `Marathi`, `Mizo`, `Montenegrin`, `Nepali`, `Sinhala`, `Tamil`, `Telugu`, `Tulu`, `Punjabi`, `Welsh`, `Irish`, `Maltese`, `Yoruba`, `Zulu`, `ASL`, `Asturian`, `Bambara`, `Belarusian`, `Bhojpuri`, `Bisaya`, `Bodo`, `Cebuano`, `Chechen`, `Cherokee`, `Chichewa`, `Coptic`, `Cornish`, `Dari`, `Dhivehi`, `Esperanto`, `Ewe`, `Faroese`, `Frisian`, `Guarani`, `Hausa`, `Hawaiian`, `Hiligaynon`, `Hmong`, `Igbo`, `Ilocano`, `Jamaican Patois`, `Javanese`, `Kashmiri`, `Kikuyu`, `Kinyarwanda`, `Konkani`, `Kurdish`, `Lingala`, `Luxembourgish`, `Luganda`, `Malagasy`, `Manipuri`, `Maori`, `Mongolian`, `Nigerian Pidgin`, `Nyanja`, `Odia`, `Oromo`, `Papiamento`, `Pashto`, `Quechua`, `Samoan`, `Sanskrit`, `Scottish Gaelic`, `Sepedi`, `Shona`, `Sindhi`, `Somali`, `Sotho`, `Sundanese`, `Tajik`, `Tamazight`, `Tetum`, `Tibetan`, `Tongan`, `Tsonga`, `Tswana`, `Twi`, `Uzbek`, `Wolof`, `Xhosa`, `Khasi`
- `location` (list of string, optional)
- `datePreset` (enum, optional)
  - Allowed values: `today`, `yesterday`, `last-3-days`, `last-7-days`, `this-week`, `last-week`, `last-14-days`, `last-30-days`, `this-month`, `last-month`, `last-3-months`, `last-6-months`, `year-to-date`, `last-year`
- `startDate` (string, optional)
- `endDate` (string, optional)
- `minDaysRun` (double, optional)
- `maxDaysRun` (double, optional)
- `minViewCount` (double, optional)
- `maxViewCount` (double, optional)
- `landingPage` (string, optional)
- `landingPageMatch` (enum, optional)
  - Allowed values: `contains`, `equals`
- `lowImpressions` (boolean, optional)
- `partnershipAd` (boolean, optional)
- `crossposted` (boolean, optional)
- `sortBy` (enum, optional)
  - Allowed values: `MOST_RECENT`, `OLDEST`, `LONGEST_RUN`, `MOST_IMPRESSIONS`, `MOST_LIKES`, `MOST_COMMENTS`, `MOST_VIEWS`, `HIGHEST_ENGAGEMENT`, `HIGHEST_OUTLIER`
- `limit` (integer, optional, default: 100)
- `offset` (integer, optional)

## Response

### 200

Matching creatives.

- `count` (integer, required)
- `media` (list of MediaPostResponsesContentApplicationJsonSchemaMediaItems, required)

## Errors

### 400 Bad Request Error

The request body or query string failed validation. `details` lists the offending fields.

- `error` (ErrorError, required)

### 401 Unauthorized Error

The API key is missing, malformed, unknown, or revoked.

- `error` (ErrorError, required)

### 402 Payment Required Error

The team's monthly credits are exhausted.

- `error` (ErrorError, required)

### 403 Forbidden Error

The team's subscription is inactive, or your membership of it has ended.

- `error` (ErrorError, required)

### 404 Not Found Error

No brand matches that id.

- `error` (ErrorError, required)

### 405 Method Not Allowed Error

The HTTP method is not supported on this path. The Allow header lists what is. HEAD is refused on metered GET endpoints because it would be billed without returning data.

- `error` (ErrorError, required)

### 413 Content Too Large Error

The request body exceeds the 100 KB limit.

- `error` (ErrorError, required)

### 429 Too Many Requests Error

Too many requests for this user on this endpoint. Carries a Retry-After header. The per-minute limit is stated on the operation.

- `error` (ErrorError, required)

### 500 Internal Server Error

Something went wrong on our side.

- `error` (ErrorError, required)

## Types

### MediaPostRequestBodyContentApplicationJsonSchemaAccountId

Account id given as a platform string or uuid

### MediaPostResponsesContentApplicationJsonSchemaMediaItems

- `id` (string, required)
- `platformId` (string, required)
- `brand` (MediaPostResponsesContentApplicationJsonSchemaMediaItemsBrand, required, nullable)
- `title` (string, required, nullable)
- `type` (enum, required, nullable)
  - Allowed values: `DPA`, `VIDEO`, `CAROUSEL`, `IMAGE`, `DCO`
- `mediaType` (enum, required, nullable)
  - Allowed values: `VIDEO`, `IMAGE`
- `url` (string, required, nullable)
- `urls` (list of string, required)
- `thumbnailUrl` (string, required, nullable)
- `width` (double, required, nullable)
- `height` (double, required, nullable)
- `durationInSeconds` (double, required, nullable)
- `platforms` (list of string, required)
- `hook` (string, required, nullable)
- `summary` (string, required, nullable)
- `coreAngle` (string, required, nullable)
- `targetPersona` (string, required, nullable)
- `ugcCharacter` (MediaPostResponsesContentApplicationJsonSchemaMediaItemsUgcCharacter, required, nullable)
- `startAt` (string, required, nullable)
- `endAt` (string, required, nullable)
- `daysRun` (double, required, nullable)
- `categories` (list of string, required)
- `carouselCount` (double, required, nullable)
- `viewCount` (double, required, nullable)
- `likeCount` (double, required, nullable)
- `commentCount` (double, required, nullable)
- `impressionPercentile` (double, required, nullable)
- `impressionsText` (string, required, nullable)
- `painPoints` (list of string, optional)
- `sellingPoints` (list of string, optional)

### ErrorError

- `code` (enum, required)
  - Allowed values: `invalid_request`, `invalid_api_key`, `payload_too_large`, `out_of_credits`, `membership_ended`, `subscription_inactive`, `brand_not_found`, `not_found`, `method_not_allowed`, `rate_limited`, `internal_error`
- `message` (string, required)
- `details` (list of ErrorErrorDetailsItems, optional)

### MediaPostResponsesContentApplicationJsonSchemaMediaItemsBrand

- `id` (string, required)
- `facebookAdLibraryId` (string, required, nullable)
- `name` (string, required)
- `type` (enum, required, nullable)
  - Allowed values: `BRAND`, `CREATOR`
- `igUsername` (string, required, nullable)
- `igFollowers` (double, required, nullable)
- `tiktokUsername` (string, required, nullable)
- `tiktokFollowers` (double, required, nullable)
- `facebookUsername` (string, required, nullable)
- `thumbnailUrl` (string, required, nullable)
- `category` (string, required, nullable)

### MediaPostResponsesContentApplicationJsonSchemaMediaItemsUgcCharacter

- `ageRange` (enum, required, nullable)
  - Allowed values: `0-18`, `18-24`, `25-34`, `35-44`, `45-54`, `55-64`, `65+`
- `gender` (enum, required, nullable)
  - Allowed values: `male`, `female`, `other`
- `ethnicity` (enum, required, nullable)
  - Allowed values: `White`, `Black`, `Asian`, `Hispanic`, `Middle Eastern`, `Indigenous`, `Pacific Islander`
- `description` (string, required, nullable)

### ErrorErrorDetailsItems

- `path` (string, required)
- `message` (string, required)

## Examples

**Request**

```json
{
  "query": "comfort-focused video ads",
  "limit": 20
}
```

**Response**

```json
{
  "count": 1,
  "media": [
    {
      "id": "string",
      "platformId": "string",
      "brand": {
        "id": "string",
        "facebookAdLibraryId": "string",
        "name": "string",
        "type": "BRAND",
        "igUsername": "string",
        "igFollowers": 1.1,
        "tiktokUsername": "string",
        "tiktokFollowers": 1.1,
        "facebookUsername": "string",
        "thumbnailUrl": "string",
        "category": "string"
      },
      "title": "string",
      "type": "DPA",
      "mediaType": "VIDEO",
      "url": "string",
      "urls": [
        "string"
      ],
      "thumbnailUrl": "string",
      "width": 1.1,
      "height": 1.1,
      "durationInSeconds": 1.1,
      "platforms": [
        "string"
      ],
      "hook": "string",
      "summary": "string",
      "coreAngle": "string",
      "targetPersona": "string",
      "ugcCharacter": {
        "ageRange": "0-18",
        "gender": "male",
        "ethnicity": "White",
        "description": "string"
      },
      "startAt": "string",
      "endAt": "string",
      "daysRun": 1.1,
      "categories": [
        "string"
      ],
      "carouselCount": 1.1,
      "viewCount": 1.1,
      "likeCount": 1.1,
      "commentCount": 1.1,
      "impressionPercentile": 1.1,
      "impressionsText": "string",
      "painPoints": [
        "string"
      ],
      "sellingPoints": [
        "string"
      ]
    }
  ]
}
```

**SDK Code**

```python searchMedia_example
import requests

url = "https://api.spyglass.so/v2/media"

payload = {
    "query": "comfort-focused video ads",
    "limit": 20
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript searchMedia_example
const url = 'https://api.spyglass.so/v2/media';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"query":"comfort-focused video ads","limit":20}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go searchMedia_example
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.spyglass.so/v2/media"

	payload := strings.NewReader("{\n  \"query\": \"comfort-focused video ads\",\n  \"limit\": 20\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby searchMedia_example
require 'uri'
require 'net/http'

url = URI("https://api.spyglass.so/v2/media")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"query\": \"comfort-focused video ads\",\n  \"limit\": 20\n}"

response = http.request(request)
puts response.read_body
```

```java searchMedia_example
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.spyglass.so/v2/media")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"query\": \"comfort-focused video ads\",\n  \"limit\": 20\n}")
  .asString();
```

```php searchMedia_example
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.spyglass.so/v2/media', [
  'body' => '{
  "query": "comfort-focused video ads",
  "limit": 20
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp searchMedia_example
using RestSharp;

var client = new RestClient("https://api.spyglass.so/v2/media");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"query\": \"comfort-focused video ads\",\n  \"limit\": 20\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift searchMedia_example
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "query": "comfort-focused video ads",
  "limit": 20
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.spyglass.so/v2/media")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```