Skip to navigation

Python

The official Python SDK (spyglass-sdk on PyPI, spyglass-so/spyglass-python on GitHub) provides typed synchronous (Spyglass) and asynchronous (AsyncSpyglass) clients for the Spyglass API.

Installation

Install spyglass-sdk with pip or uv:

pip install spyglass-sdk

Usage

  1. Get an API key from Settings → API in the dashboard.
  2. Set the key as an environment variable named SPYGLASS_API_KEY or pass it directly to the Spyglass class as token.
import os
from spyglass import Spyglass
client = Spyglass(token=os.environ["SPYGLASS_API_KEY"])
# 1. Resolve a brand by name
result = client.search_brands(names=["Nike"], limit=1)
if result.brands:
brand = result.brands[0]
# 2. Search the creatives that brand is running
media_result = client.search_media(
account_id=brand.id,
query="marathon running shoes comfort",
limit=10,
)
for ad in media_result.media:
print(ad.id, ad.hook, ad.core_angle, ad.url)

Searching Brands & Creators

Search brands

Search brands by name (names), social handle (ig_username, tiktok_username), known IDs (brand_ids), or a natural-language brief (query):

# Fuzzy name lookup
by_name = client.search_brands(
names=["Athletic Greens", "AG1"],
limit=5,
)
# Natural-language discovery
by_brief = client.search_brands(
query="DTC electrolyte and hydration powder brands",
limit=10,
)

Search creators

Search creators by handle, name, or natural-language brief, and filter by demographic and profile attributes (gender, age_range, ethnicity, location, language):

creators = client.search_creators(
query="skincare routine and dermatology tips",
gender="female",
age_range="25-34",
location="United States",
language="English",
limit=20,
)

Searching Ad Creatives & Social Media

Pass account_id to inspect a specific brand or creator’s creatives, or omit account_id and pass query to search across the entire Spyglass index:

# Optional: look up valid product taxonomy paths first (free call)
taxonomies = client.search_taxonomies(query="running shoes")
media = client.search_media(
query="UGC founder story explaining product benefits",
channel=["META_AD", "TIKTOK"],
format=["VIDEO"],
status="ACTIVE",
taxonomy=taxonomies.paths[:2],
min_days_run=14,
limit=20,
offset=0,
)

Insights, Campaigns, Landing Pages & Comments

Creative insights, campaigns, and landing pages

Pull clustered creative themes (HOOK, USP, PERSONA, MEDIA_MIX), campaigns or offers, and ranked destination landing pages for any account:

# Ranked hooks + weekly volume breakdown over the last 90 days
insights = client.account.insights(
id=brand.id,
type="HOOK",
max_days_back=90,
limit=10,
)
# Top landing pages where the brand sends ad traffic
campaigns = client.account.campaigns(
id=brand.id,
type="CAMPAIGN",
date_preset="last-30-days",
limit=10,
)
landing_pages = client.account.landing_pages(
id=brand.id,
show_inactive="false",
limit=20,
)

Post comments

Fetch public comments from Instagram posts/reels (by shortcode or URL) and TikTok videos (by video ID or URL):

comments = client.get_comments(
instagram_ids=["https://www.instagram.com/reel/C2xY9abcD1e/"],
tiktok_ids=["https://www.tiktok.com/@nike/video/7234567890123456789"],
limit=1,
)

Indexing an Account

If Spyglass does not have a brand or creator yet, submit its website or social profile URL (POST /account/index is free) and poll until status is "ready":

import time
indexed = client.account.index(website_url="https://www.on.com")
status = indexed.status
while status == "indexing":
time.sleep(3)
check = client.account.index_status(id=indexed.brand_id)
status = check.status
if status in ("ready", "already_exists"):
account = client.account.get(id=indexed.brand_id)
print("Indexed account:", account.name)

Async Usage

Import AsyncSpyglass for non-blocking calls with asyncio:

import asyncio
import os
from spyglass import AsyncSpyglass
client = AsyncSpyglass(token=os.environ["SPYGLASS_API_KEY"])
async def main() -> None:
result = await client.search_brands(names=["Nike"], limit=3)
for brand in result.brands:
print(brand.id, brand.name)
asyncio.run(main())

Error Handling

When the API returns a non-2xx status code, the SDK raises an ApiError containing the HTTP status code and parsed error body:

from spyglass.core.api_error import ApiError
try:
client.search_brands(names=["Nike"], limit=5)
except ApiError as e:
print(e.status_code)
print(e.body)

Retries, Timeouts & Raw Responses

Requests default to a 60-second timeout and 2 automatic retries with exponential backoff on 408, 429, and 5xx responses. Override these on the client or per request:

client = Spyglass(
token=os.environ["SPYGLASS_API_KEY"],
timeout=30.0,
max_retries=3,
)
response = client.search_brands(
names=["Nike"],
limit=5,
request_options={"timeout_in_seconds": 15, "max_retries": 1},
)

Access HTTP headers and status codes via .with_raw_response:

raw = client.with_raw_response.search_brands(names=["Nike"], limit=1)
print(raw.status_code)
print(raw.headers.get("retry-after"))
print(raw.data.brands)