Docs
Ctrl+K Search Alt+[Alt+] Guides
Get API key

API · Brands

Brands & Brand tracker

GET /api/v1/brands is the API-key tracked-brands list (metered). Brand detail tabs use /api/brands/* in a logged-in browser session — they are not available over an API key. MCP equivalent: list_tracked_brands.

GET/api/v1/brands

List the brands tracked by the API key owner.

Send X-API-Key on every request.

Query parameters

  • sortstringoptional

    Order of the returned list.

    Default date_added·Values date_added name new_ads growth
  • searchstringoptional

    Case-insensitive match on brand name or page id.

  • board_idstringoptional

    Only brands on that saved board (must belong to the user).

Request
curl -sS \
  -H "X-API-Key: $WH_API_KEY" \
  "{origin}/api/v1/brands?sort=date_added&search=string&board_id=string"
const res = await fetch(`${ORIGIN}/api/v1/brands?sort=date_added&search=string&board_id=string`, {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.WH_API_KEY,
  },
});
const data = await res.json();
import os, requests

res = requests.get(
    f"{ORIGIN}/api/v1/brands?sort=date_added&search=string&board_id=string",
    headers={"X-API-Key": os.environ["WH_API_KEY"]},
)
data = res.json()
Response
[
    {
        "id": "108897621643793",
        "name": "Allbirds",
        "logo_url": "https://cdn.example.com/brands/allbirds.png",
        "page_url": "https://www.facebook.com/allbirds",
        "status": "active",
        "total_ads": 412,
        "active_ads_on_page": 142,
        "new_ads_count": 18,
        "new_ads_change": 0.14,
        "added_at": "2024-04-12"
    }
]
{
    "success": false,
    "error": "Unauthorized"
}
{
    "success": false,
    "error": "WinningHunter Standard subscription required"
}

The tracked-brands list is returned as a JSON array (not wrapped in { success, data }).

Brand detail (session only)

These power the Brand tracker UI and require a logged-in browser session, not an API key. The brand identifier (id) is the Facebook page_id from the list above.

Method Path Body / params Purpose
POST /api/brands/follow url (ad library URL) Track a brand.
POST /api/brands/unfollow page_id or page_ids[] Remove one or more tracked brands.
GET /api/brands/amount-tracked Tracked count + plan cap.
GET /api/brands/ads id, page (0-based, 12/page), date_range Paginated ads for one brand.
GET /api/brands/notifications/prefs Brand digest notification prefs + per-brand enabled flags.
POST /api/brands/notifications/prefs/save JSON prefs (auto_enable_new_brands, digest, events, brands) Save notification preferences.
POST /api/brands/notifications/preview JSON optional frequency Build digest HTML for a frequency (no send).
GET /brands/notifications/digest-preview mode=sample|live, frequency=daily|weekly|monthly, raw=1 Dev HTML preview of the digest email (logged-in).
POST /api/brands/notifications/send-test JSON optional frequency Email a test digest to the logged-in user.

Brand digest prefs shape

digest holds a shared timezone plus independent schedules:

{
  "timezone": "America/New_York",
  "daily": { "enabled": true, "time": "09:00" },
  "weekly": { "enabled": true, "time": "09:00", "weekday": 1 },
  "monthly": { "enabled": false, "time": "09:00", "monthday": 1 }
}

Legacy single-frequency prefs (enabled + frequency + time) are migrated on read/save.

Brand digest cron (ops)

Digest emails are sent by scripts/send-brand-digest.php (run every 15 minutes). Each enabled schedule (daily / weekly / monthly) can send its own email when local time matches (time within a 15-minute window; weekly also checks weekday 1–7 ISO; monthly checks monthday 1–28). Deduped per userid + send_date + timezone + frequency. Flags: --userid=, --frequency=, --force, --dry-run.

Date range filter

date_range applies to /api/brands/ads and other insight tabs:

Value Meaning
(absent) / all No date filter
live Last seen in the last 3 days
7d, 30d, 3m, 6m Ad start date on or after the rolling cutoff
custom Requires date_from and date_to (Y-m-d)