Spytrend
Server Details
Meta and TikTok advertising analytics with ads, creatives, advertisers, rankings and six workflows.
- Status
- Healthy
- OAuth
- Works in Glama
- Last Tested
- Transport
- Streamable HTTP
- URL
- Repository
- Spytrend/spytrend-mcp
- GitHub Stars
- 0
Available Tools
21 toolsadd_to_favoritesAdd to favouritesAIdempotentInspect
Save a SELECTION of entities to one of the caller's favourites folders so they can review them later on spytrend.com. entity_type is creo (creatives), webmaster, ad, hub (a hub profile from search_hubs) or shop (a shop domain from search_shops); ids is the list of entity ids from search_ads / search_webmasters / search_creatives / search_hubs / search_shops. For entity_type=hub you MUST also pass hub (the hub slug the ids came from, e.g. 'ecommerce'). folder is OPTIONAL — PREFER a short descriptive name from the user's search (e.g. 'Gambling ads US · Jun 2026'), created automatically if absent; if omitted, a stable dated folder is used. Idempotent — items already in the folder are reported as already_saved, never duplicated or re-charged. BILLING: saving an ad costs 1 token and saving a creative costs 10 tokens (you are taking the creative — same cost as get_media); saving a webmaster, a hub profile or a shop is FREE. Already-saved items cost nothing.
| Name | Required | Description | Default |
|---|---|---|---|
| hub | No | REQUIRED only when entity_type=hub: the hub slug the ids came from (the same 'hub' you passed to search_hubs, e.g. 'ecommerce', 'facebook', 'amazon') | |
| ids | Yes | the entity ids to save (from search_ads / search_webmasters / search_creatives / search_shops results; for entity_type=hub these are the hub profile ids/domains from search_hubs; for entity_type=shop these are the shop domains from search_shops) | |
| folder | No | OPTIONAL folder name; created if absent. PREFER a short descriptive name from the user's search context (e.g. 'Gambling ads US · Jun 2026'). If omitted, a stable dated folder ('AI saved ads · YYYY-MM-DD') is used | |
| entity_type | Yes | what kind of entity to save: creo (creative), webmaster, ad, hub (a hub profile from search_hubs) or shop (a shop domain from search_shops) |
Output Schema
| Name | Required | Description |
|---|---|---|
| added | Yes | |
| failed | No | |
| folder | Yes | |
| entity_type | Yes | |
| already_saved | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Going well beyond the idempotentHint/destructiveHint=false annotations, the description discloses concrete behavioral traits: idempotency semantics (already_saved reported, never duplicated or re-charged), per-type token costs (1 token per ad, 10 per creative, free for webmaster/hub/shop), automatic folder creation with a stable dated fallback, and the required hub parameter when entity_type=hub. This materially helps an agent predict side effects and cost before invoking.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose is front-loaded in the first sentence, and every sentence carries decision-relevant information: parameter mapping, conditional requirement, folder guidance, idempotency, and billing. It is a long single paragraph that could benefit from section breaks, but there is no waste.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema is present, so return values need no explanation, and the description covers purpose, all parameters, the conditional hub requirement, idempotency, and billing. Nothing an agent needs to call this tool correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already documents all 4 parameters with 100% coverage, so the baseline is 3. The description adds value by linking ids provenance to the exact sibling search tools, clarifying the hub/entity_type dependency with an example, and tying entity_type to billing behavior. It is somewhat redundant with the already-thorough schema, so not a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb ('Save'), a clear resource ('a SELECTION of entities to one of the caller's favourites folders'), and the purpose ('so they can review them later on spytrend.com'). It also disambiguates all five entity_type values and ties each to its source search tool, making it clearly distinct from the read/search siblings like list_favorites and the search_* tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states the intended use case — saving entities surfaced by the search_* tools for later review — and adds practical guidance such as preferring a folder name from the user's search context and the billing implications per entity type. It does not explicitly name alternatives or exclusions (e.g., 'use list_favorites to view saved items'), so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_similar_adsFind related adsARead-onlyInspect
Find ads related to one source ad by an explicit overlap basis: fanpage (same Facebook page_id) or domain (same exact landing_domain). This is entity overlap, NOT visual similarity of the material; use find_similar_creatives for that. The source ad is excluded from data, but pagination.total remains the upstream overlap-candidate count before that exclusion; source_excluded and total_semantics make this explicit instead of fabricating an adjusted exact total. Metadata only, cursor-paginated. QUOTA: 1 token per delivered row (default page 20).
| Name | Required | Description | Default |
|---|---|---|---|
| ad_id | Yes | source ad UUID from search_ads | |
| basis | Yes | overlap basis: fanpage (same Facebook page_id) or domain (same exact landing_domain) | |
| limit | No | maximum metadata rows (default 20, maximum 200; 1 token per delivered row) | |
| cursor | No | pagination cursor returned by the previous call |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | |
| pagination | Yes | |
| basis_value | Yes | |
| source_ad_id | Yes | |
| source_excluded | Yes | |
| total_semantics | Yes | |
| similarity_basis | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark this as readOnly and non-destructive, and the description adds important behavioral context beyond those: the source ad is excluded from results, pagination.total reflects the upstream count before exclusion, metadata-only returns, and cursor pagination. No contradictions with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence earns its place: it defines the operation, disambiguates from a sibling, clarifies pagination semantics, and states quota. The key exclusion and alternative are front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, annotations covering safety, full parameter schema coverage, and the description covering pagination, exclusion behavior, quota, and alternative routing, the agent has everything needed to select and invoke this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the parameters are already fully documented in the schema. The description reinforces the meaning of basis and the 1-token-per-row quota but does not add substantial semantics beyond what the schema already provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Find ads related to'), resource ('one source ad'), and the overlap basis ('fanpage' or 'domain'). It explicitly distinguishes itself from find_similar_creatives, so an agent can differentiate between entity overlap and visual similarity without inspecting siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit when-to-use guidance: entity overlap by fanpage or domain, and explicitly names the alternative ('use find_similar_creatives for that') for visual similarity. This makes the selection boundary unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_similar_creativesFind creatives that look the sameARead-onlyInspect
Find creatives that look the same as one creative, using the production similarity index. This is visual similarity, not exact creative membership. threshold_status distinguishes an explicitly requested threshold from the production default; result_status=bounded_native_cap means the upstream similarity endpoint exposes at most 20 candidates and has_more is conservative at that cap. Returns metadata without media URLs; use get_media for selected ids. QUOTA: 1 token per delivered row.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | maximum metadata rows to return (default 20, maximum 20) | |
| threshold | No | optional maximum visual difference between two creatives (0 uses the production default; maximum 128 — lower is stricter) | |
| creative_id | Yes | source creative UUID from search_creatives |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | |
| has_more | Yes | |
| threshold | Yes | |
| result_status | Yes | |
| similarity_basis | Yes | |
| threshold_status | Yes | |
| source_creative_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnly/openWorld annotations, it discloses the 20-candidate upstream cap, conservative has_more semantics, threshold_status vs result_status meaning, metadata-only output without media URLs, and per-row quota. This is substantial behavioral context that helps an agent predict side effects and limits. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four dense sentences that all earn their place: purpose, visual-similarity clarification, cap/has_more semantics, get_media routing, and quota. Front-loaded with the main verb and resource, with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only similarity search with 3 documented parameters and an output schema, the description covers the relevant edge cases (default vs explicit threshold, native cap, has_more behavior) and routing. Nothing an agent needs to call or interpret the result is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Input schema documentation covers all three parameters (100% coverage), including threshold's lower-is-stricter semantics and default. The description adds no substantive parameter-level detail beyond framing threshold as part of the production similarity index, so the baseline score of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific action: finding creatives visually similar to a source creative via the production similarity index. Explicitly differentiates visual similarity from exact creative membership, and the resource term 'creatives' distinguishes it from sibling tools for ads and webmasters.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides clear context that this is for visual look-alikes and explicitly says it is not exact membership, giving a when-not. It also points to get_media for media URLs. However, it does not explicitly compare against find_similar_ads or find_similar_webmasters, leaving some routing to the tool name.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_similar_webmastersFind similar webmastersARead-onlyInspect
Find other webmasters sharing normalized URL-parameter values with a source webmaster. First call get_webmaster_similarity_facets, then pass one returned param_key; optionally narrow to selected values. similarity_basis is url_parameter_overlap and rarity_count shows how many webmasters share the value. Results are deterministically ordered and bounded by the canonical source cap of 5000; result_status/result_cap/total_status state whether that bounded set is complete. QUOTA: 1 token per delivered row (default page 20).
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | maximum rows to return (default 20, maximum 200; 1 token per delivered row) | |
| offset | No | zero-based deterministic result offset | |
| values | No | optional subset of normalized source values for this param_key | |
| param_key | Yes | URL parameter key returned by get_webmaster_similarity_facets | |
| webmaster_id | Yes | source webmaster UUID from search_webmasters |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | |
| limit | Yes | |
| offset | Yes | |
| has_more | Yes | |
| param_key | Yes | |
| result_cap | Yes | |
| total_status | Yes | |
| result_status | Yes | |
| similarity_basis | Yes | |
| source_webmaster_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnly/openWorld/destructive annotations, the description discloses determinism, the canonical 5000-result cap, result completeness fields (result_status/result_cap/total_status), and the token quota per delivered row. This gives an agent substantial behavioral expectations without contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but efficiently organized: purpose first, workflow second, behavioral semantics third, quota last. Every sentence adds information and there is no filler or repetition of schema text.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the prerequisite call, parameter selection, result ordering, cap behavior, completeness signals, and quota. Since an output schema exists, return-value details are not necessary, and nothing essential is missing for an agent to invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds meaningful context: param_key must come from get_webmaster_similarity_facets, values are an optional subset of normalized source values, and webmaster_id is the source webmaster. It also clarifies pagination/quota behavior tied to limit.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Find other webmasters sharing normalized URL-parameter values with a source webmaster.' It also names the matching mechanism (url_parameter_overlap) and differentiates from the sibling find_similar_ads/find_similar_creatives tools by explicitly targeting webmasters.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives an explicit workflow: first call get_webmaster_similarity_facets, then pass one returned param_key, and optionally narrow by values. This is clear contextual guidance for when and how to use the tool, though it does not state when-not-to-use alternatives or explicitly exclude sibling tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_adGet ad by id (with media)ARead-onlyInspect
Fetch a single ad by its id with FULL details including its creative media: the full ad TEXT in body (this is the only surface that serves it), body_chars (its length in UTF-8 characters), title, creative URL, geos, status, advertiser. body is absent when the ad genuinely has no text; body_chars is absent when the length could not be read — never read an absent field as zero. Media urls on media.spytrend.com are permanent; a media entry with expiring=true is the original signed Facebook CDN link of a not-yet-mirrored fresh ad — it dies within hours/days, NEVER put it into reports or exports (re-fetch the ad later for the permanent link). QUOTA: 1 token (this delivers the ad's creative media). With source=tiktok: 100 tokens (the TikTok row is the billable resource).
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | the ad id to fetch (with source=tiktok: a TikTok ad id from search_ads source=tiktok results) | |
| source | No | ad corpus: meta (default, 1 token — delivers the ad's media) or tiktok (⚠️ 100 tokens per TikTok ad row; Pro plan required) |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | No | |
| source | No | |
| tiktok_data | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes far beyond the readOnly/openWorld/destructive annotations by disclosing absence semantics for body and body_chars, permanent vs. expiring media URLs, and the TikTok billable-resource behavior. It explicitly warns against treating absent fields as zero and against putting expiring media into reports or exports. This is rich behavioral disclosure that genuinely helps an agent avoid misuse.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence carries essential operational information: field availability, absent-field semantics, media URL permanence, expiring-link warnings, and quota distinctions. It front-loads the core purpose before diving into caveats, and no sentence is filler or redundant with the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given that output schema exists and annotations cover the read-only safety profile, the description supplies the remaining context an agent needs: exact response semantics, data-quality caveats, quota behavior, and how to handle the TikTok source variant. Nothing essential is missing for correctly selecting and invoking this tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already covers both parameters with 100% coverage, so the baseline is 3. The description adds value by clarifying the quota implications of source=tiktok and by tying the tool's purpose to the media-delivery expectation. It does not add new meaning to the id parameter itself, but the source parameter is meaningfully enriched.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a precise verb and resource: 'Fetch a single ad by its id with FULL details including its creative media,' then enumerates the specific fields returned. It also differentiates this tool from sibling media/search tools by stating that this is 'the only surface' that serves the full ad text, making its role unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context about when to use this tool: when fetching a single ad by id and when full creative media and text are needed. It also provides practical guidance around TikTok source selection and quota costs, and warns against using expiring media links in reports. It does not explicitly name sibling alternatives or state exclude conditions, so it falls just short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_ads_analyticsGet ads selection analyticsARead-onlyInspect
Get the bounded analytics overview for the current Meta /ads selection: total and display_total plus statuses, countries, AI categories, landing domains, advertisers and webmasters. This is a Pro-or-higher paid surface; every delivered breakdown row costs 1 token, pending/error responses refund the reservation. The default is 10 rows per section and the absolute maximum is 20; results are flattened in data with a dimension field and include section readiness/truncation metadata. Totals are nullable: null with pending/unavailable status is NOT zero; exact zero is numeric 0 with an exact status. Only the status section removes its own filter; every other section uses the full filtered universe. No pagination, sorting, source, scheduler, force-scrape, TikTok, raw SQL or storage coordinates are accepted. Auto-applied subscription categories are reported in auto_applied_verticals and scope_note. Re-issue the identical call after retry_after_seconds when pending; do not blindly retry paid calls.
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | free-text filter, at most 100 UTF-8 bytes | |
| saved | No | saved scope: all | |
| dedupe | No | collapse duplicate creative rows | |
| channel | No | messaging shortcut: whatsapp or telegram | |
| country | No | legacy single ISO-2 include country; prefer countries | |
| date_to | No | Facebook launch upper bound YYYY-MM-DD | |
| page_id | No | Facebook page id or bounded facebook.com page URL | |
| pixel_id | No | bounded Facebook pixel identifier | |
| countries | No | unique uppercase ISO-2 include countries, maximum 200 | |
| date_from | No | Facebook launch lower bound YYYY-MM-DD | |
| folder_id | No | non-zero favorite folder UUID | |
| languages | No | target language slugs, maximum 50 | |
| platforms | No | Meta publisher placements, maximum 6 | |
| search_in | No | query scope: all, title, advertiser or text | |
| categories | No | AI category slugs, maximum 50 | |
| cta_buttons | No | CTA labels, maximum 50 | |
| hub_domains | No | normalized destination hostnames, maximum 50 | |
| media_types | No | media types: image or video | |
| parsing_geo | No | one uppercase ISO-2 parser GEO | |
| resolved_ip | No | resolved IPv4/IPv6 address | |
| search_term | No | historical search term, at most 100 UTF-8 bytes | |
| domain_zones | No | lowercase landing TLD/zone labels, maximum 50 | |
| hub_category | No | closed top-level hub category | |
| status_today | No | current status: active, inactive or vanished | |
| webmaster_id | No | non-zero webmaster UUID | |
| advertiser_id | No | non-zero advertiser UUID | |
| country_match | No | country semantics: any or only | |
| first_seen_to | No | SpyTrend discovery upper bound YYYY-MM-DD | |
| max_countries | No | maximum additional/total GEOs, 0 disables, maximum 200 | |
| ai_subcategory | No | taxonomy-valid AI subcategory slugs | |
| days_active_to | No | non-negative active-days upper bound; null omits the filter, 0 is meaningful | |
| favorites_only | No | restrict to the token-derived user's favorite webmaster scope | |
| impressions_to | No | non-negative impressions bucket upper bound | |
| landing_domain | No | normalized landing hostname | |
| max_page_likes | No | non-negative page-like upper bound; null omits the filter, 0 is meaningful | |
| media_count_to | No | non-negative media-count upper bound | |
| min_page_likes | No | non-negative page-like lower bound | |
| platforms_mode | No | placement semantics: any or all | |
| first_seen_from | No | SpyTrend discovery lower bound YYYY-MM-DD | |
| min_days_active | No | non-negative active-days lower bound | |
| ai_enriched_only | No | only AI-enriched ads | |
| creative_formats | No | creative formats: video, carousel, single or dynamic | |
| impressions_from | No | non-negative impressions bucket lower bound | |
| media_count_from | No | non-negative media-count lower bound | |
| ai_confidence_min | No | minimum AI confidence: low, middle or high | |
| contains_in_links | No | tracking-link fragment, at most 255 UTF-8 bytes | |
| fan_page_categories | No | validated fan-page category groups | |
| landing_domain_exact | No | restrict landing domain to exact hostname | |
| max_items_per_section | No | rows per breakdown section, default 10, maximum 20 |
Output Schema
| Name | Required | Description |
|---|---|---|
| win | No | |
| data | Yes | |
| tier | Yes | |
| total | Yes | |
| domains | Yes | |
| pending | Yes | |
| statuses | Yes | |
| countries | Yes | |
| ai_labeled | Yes | |
| from_cache | No | |
| scope_note | No | |
| webmasters | Yes | |
| advertisers | Yes | |
| computed_at | No | |
| total_status | Yes | |
| ai_categories | Yes | |
| display_total | Yes | |
| result_status | Yes | |
| sections_ready | Yes | |
| sections_total | Yes | |
| display_total_basis | No | |
| retry_after_seconds | No | |
| display_total_status | Yes | |
| auto_applied_verticals | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only state readOnly/openWorld/non-destructive, leaving the description to carry behavioral detail, and it does so richly. It discloses token costs per delivered row, refund behavior on pending/error, row limits and defaults, flattened output shape with dimension field, section readiness/truncation metadata, nullable totals with non-zero semantics, status-section filter behavior, and retry guidance. This is far beyond what annotations provide and contains no contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but information-dense; every sentence conveys a distinct operational constraint or behavioral caveat. It front-loads the core purpose before layering cost, limits, null semantics, and exclusions. This is appropriate given the tool's 49-parameter surface and the many edge cases an agent must respect.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 49 parameters, 100% schema coverage, an existing output schema, and read-only annotations, the description covers the critical non-obvious context: pricing, retries, null semantics, section-scope behavior, unsupported features, and auto-applied vertical metadata. An agent has everything needed to decide whether and how to call this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3 and the description is not required to restate all parameters. It does add useful context around output semantics and limits, such as default 10 rows and maximum 20, but this largely mirrors the max_items_per_section schema description. There is no meaningful per-parameter enrichment beyond what the schema already states.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb and resource: "Get the bounded analytics overview for the current Meta /ads selection." It enumerates exactly which breakdowns are included (statuses, countries, AI categories, landing domains, advertisers, webmasters) and the key totals, making the tool's scope unmistakable. This clearly differentiates it from search or single-entity sibling tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides strong usage context: it targets the current Meta /ads selection, is a Pro-or-higher paid surface, and explicitly lists unsupported features such as pagination, sorting, source, scheduler, force-scrape, TikTok, raw SQL, and storage coordinates. It also instructs agents to re-issue identical calls after retry_after_seconds and not to blindly retry paid calls. It stops short of explicitly naming alternative tools and when to prefer them, so it does not fully earn a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_advertiserGet advertiser by idARead-onlyInspect
Fetch a single advertiser's FULL card by id — the same data as the spytrend.com/advertisers page: name, external_id (FB page id), avatar_url, page_url, total_ads and active_ads (EXACT precomputed counts — THIS is the cheap way to answer 'how many ads / how many are active does this advertiser run', NOT paginating search_ads, whose total is estimated for advertiser_id), launched_14d, top_countries (geo breakdown), platforms, top_landing (destination domains), page_like_count (followers), born (founded date), categories and recent creatives. The card also embeds top_creatives — the advertiser's MOST RE-UPLOADED creatives (a creative's total_ads = how many ads reuse the same material, ordered desc, media stripped/free); present these as the headline at the end and download with get_media (entity_type=creo). The id is an advertiser UUID (from an ad's advertiser_id / get_trends dimension=advertisers) OR a Facebook page id — a fanpage id from get_creative's fanpages, or an ad's page_id: a page id is resolved to its advertiser automatically, so a fanpage you found in a creative resolves straight to its card. A pasted facebook.com page link (any form — vanity name, profile 100…-id, profile.php, Ads Library) is likewise resolved to the page automatically. To list the advertiser's ads, call search_ads with advertiser_id. CATEGORY COUNTERS CONTRACT category_total_ads / subcategory_total_ads and every top_categories[].ads_count / top_subcategories[].ads_count count ONLY the advertiser's AI-LABELED ads - they are a share of the labeled subset, NOT of the page's ad universe. The labeling share is ai_coverage (labeled / total_ads). Read them as: 'N of total_ads ads are labeled (ai_coverage), and X% of THOSE are '. Never compare category_total_ads against total_ads/active_ads directly, and never report a category share as the advertiser's overall vertical mix when ai_coverage is low. QUOTA: 1 token (one entity card). TOP_CREATIVES CONTRACT each top_creatives entry's total_ads counts the creative's FULL family of matching material across the whole serving universe as the market-wide reuse of that creative; note the family may span MORE advertisers than this page — it is the creative's market-wide reuse, not this advertiser's share.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | the resource id to fetch |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, openWorldHint, and destructiveHint, and the description adds substantial behavioral detail beyond them: quota cost ('1 token'), exact vs. estimated count semantics, the AI-labeling caveat for category counters, and the top_creatives market-wide reuse contract. These are significant non-obvious behaviors that could change how an agent interprets results.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose and field list, then proceeds through essential contracts and caveats. It is quite long and dense, but nearly every sentence carries a necessary distinction or warning. Some redundancy and lack of paragraph breaks reduce readability, but there is minimal fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, the presence of an output schema, and the low parameter count, the description is extremely complete. It covers id formats, quota, when to use alternatives, caveats about counts and categories, and the meaning of top_creatives totals. There is no obvious gap an agent would need to guess about.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema only says 'the resource id to fetch', which is generic and unhelpful. The description compensates fully by explaining the id can be an advertiser UUID, a Facebook page id, a fanpage id, or a pasted facebook.com page link, and that page ids are auto-resolved to advertisers. This transforms a vague parameter into a precisely understood input.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Fetch a single advertiser's FULL card by id', and enumerates the exact fields included. It differentiates itself from search_ads by noting this is 'the cheap way to answer how many ads' and from get_media by directing creatives downloads there. This is a clear, distinguishing statement of purpose.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly tells when to use this tool versus alternatives: use it for exact counts instead of 'paginating search_ads', use search_ads to list an advertiser's ads, and use get_media for downloading creatives. It also explains what kinds of IDs can be passed and that Facebook page links resolve automatically, giving an agent actionable selection criteria.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_creativeGet creative details and breakdown by idARead-onlyInspect
Fetch ONE creative's metadata plus its canonical bounded breakdown by id: activity period/quality, max_days_active with max_days_active_source/max_days_active_quality, delivery_languages with explicit legacy provenance, aggregate counts, advertisers, fanpages, landing domains, webmasters and geos. The ambiguous legacy languages alias is intentionally omitted. When a speech transcript has already been generated for a video, copy_languages is returned separately with copy_language_source=cached_asr_transcript and copy_language_scope=spoken_audio; it describes spoken audio only, not title/body or visual OCR. copy_language_status is available for measured confidence >=0.8, available_low_confidence below that, available_unscored when the provider supplied no probability, and unavailable when there is no valid cached result. This read never starts transcription. Breakdown lists are sorted by ad count desc and capped at 1000 rows; each dimension has its own *_status, _returned and (where a snapshot cardinality exists) _count/_truncated/_count_status. breakdown_status is available_bounded only when every dimension query completed, partial when at least one dimension is unavailable, and unavailable when the breakdown endpoint itself failed. The id is a creative UUID from search_creatives. Media URLs are intentionally excluded; download with get_media (entity_type=creo, same id). Fanpage ids feed the search_creatives fanpages filter and search_ads page_id. QUOTA: 1 token (one entity card).
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | the resource id to fetch |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already indicate readOnlyHint=true and destructiveHint=false, and the description substantially extends this with behavioral detail: canonical bounded breakdown, omission of the ambiguous legacy languages alias, copy_languages provenance and scope, breakdown status semantics, sorting/capping at 1000 rows, per-dimension status fields, and the explicit statement that this read never starts transcription. No contradiction with annotations exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Although long, the description is front-loaded with the core purpose and every subsequent clause carries operational meaning: statuses, sorting, truncation, provenance, exclusions, and quota. There is no redundant filler or repetition of schema or annotation content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter read tool with a full output schema, the description covers the essential context: how to obtain the id, what data is included and excluded, how to route to get_media, breakdown status conditions, quota cost, and behavioral guarantees like no transcription. Nothing critical is left unresolved.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema only says id is 'the resource id to fetch,' while the description adds crucial meaning: it is a creative UUID from search_creatives, the same id used for get_media with entity_type=creo, and it selects a single creative. This is meaningful value beyond the schema's generic description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Fetch ONE creative's metadata plus its canonical bounded breakdown by id.' It clearly distinguishes this from search_creatives by emphasizing a single id lookup rather than search, and it names the id provenance as a creative UUID from search_creatives.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit routing guidance: it tells the agent to use get_media for media URLs with entity_type=creo and the same id, and states that fanpage ids feed the search_creatives fanpages filter and search_ads page_id. It also clarifies this tool never starts transcription, preventing misuse for transcript generation.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_mediaGet creative media (download URLs)ARead-onlyInspect
Fetch downloadable creative MEDIA (image/video URL + thumbnail) for 1-500 ad or creative UUIDs returned by search_ads / search_creatives. This is the paid delivery step. entity_type is 'ad' or 'creo'. Existing clients keep the {entity_type,items:[{id,media:[{media_type,url,thumbnail_url,fallback_urls}]}]} fields; additive counts, partial and failures explain malformed, quota-limited and unavailable items, plus interrupted items when at least one peer was delivered. Malformed/zero UUIDs never reach the API or consume quota; valid peers in a mixed batch are still processed. duplicate_count reports canonical duplicates, which are fetched and billed once. IMPORTANT: a blob coordinate can go stale after storage compaction, so if url returns an HTTP error (e.g. 416/404), retry fallback_urls in order before treating it as unavailable. Typical flow: search_ads (browse metadata, free) → pick ids → get_media. QUOTA: charges only for media actually delivered — 1 token per ad, 10 per creative (free starter: 500 tokens total; paid plans: 40,000/month). If your balance can't cover every id it delivers as many as fit. get_usage is free.
| Name | Required | Description | Default |
|---|---|---|---|
| ids | Yes | 1 to 500 ad or creative UUIDs to resolve (from search_ads / search_creatives results); malformed ids are reported per item and never consume quota | |
| entity_type | Yes | what kind of media to fetch: 'ad' (creative media of ads from search_ads) or 'creo' (media of creatives from search_creatives) |
Output Schema
| Name | Required | Description |
|---|---|---|
| items | Yes | |
| partial | Yes | |
| failures | No | |
| entity_type | Yes | |
| valid_count | Yes | |
| attempted_count | Yes | |
| delivered_count | Yes | |
| duplicate_count | Yes | |
| requested_count | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds substantial behavioral detail beyond the readOnly/destructive annotations: quota charges, retry of fallback_urls on stale blob coordinates, malformed UUIDs not consuming quota, duplicate_count billing behavior, and partial/interrupted item handling. This fully prepares the agent for real-world failures.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence carries operational information. It uses clear labels like QUOTA and IMPORTANT, front-loads the core purpose, and organizes edge cases logically without repetition or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with quota, paid delivery, fallback URLs, and partial-failure semantics, the description is remarkably complete. It covers success, malformed input, mixed batches, duplicates, quota exhaustion, stale URLs, and the recommended call flow, so an agent can invoke it correctly in nearly any scenario.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds meaningful semantics beyond the schema, especially for ids: malformed UUIDs never reach the API or consume quota, valid peers in a mixed batch are still processed, and duplicates are fetched/billed once.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Fetch downloadable creative MEDIA (image/video URL + thumbnail)' for 1-500 ad or creative UUIDs. It clearly distinguishes from siblings like get_ad or get_creative by emphasizing the media delivery use case and the search_ads/search_creatives source.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The typical flow 'search_ads → pick ids → get_media' and the explicit phrase 'This is the paid delivery step' give clear usage context. It does not explicitly name alternative tools to avoid, but the flow and media-specific wording effectively route an agent to the right tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_shopGet shop by domainARead-onlyInspect
Fetch one domain surface's FULL profile. Primary visits is the monthly estimate for traffic_as_of; fetched_at is ingestion time. traffic_sources is typed, while a separate legacy snapshot is kept alongside, and its similarly named traffic fields are not interchangeable with primary visits. the estimate's page title is not a canonical company/brand name; a missing rank on a subdomain is not evidence that the parent domain has no rank. domain_created is the best available registrable-root registration date, not product launch; its per-row RDAP/WHOIS-vs-legacy-fallback provenance is not retained in ClickHouse. ads_monthly/ads_burn use first_parsed_date and mark the open month is_complete=false. fb_signal is correlation_only. The domain/subdomain may be a funnel, runtime, blog or redirect rather than a company/product; inspect registrable_domain and surface_type. Includes AI-ad-category breakdown, recent ads and products. To save it use add_to_favorites (entity_type=shop, ids=[domain]). QUOTA: 1 token (one entity card).
| Name | Required | Description | Default |
|---|---|---|---|
| domain | Yes | the shop domain to fetch (e.g. 'example.com'), as returned by search_shops |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark the tool read-only and non-destructive, but the description adds substantial field-level caveats: legacy traffic snapshot fields are not interchangeable, domain_created is registration date not launch date, ads_monthly marks open months incomplete, and fb_signal is correlation-only. It also discloses the QUOTA and warns that domains may be funnels/runtime pages rather than companies.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The first sentence is front-loaded with the action and resource, and every subsequent caveat earns its place. However, the description is one sprawling paragraph with lowercase continuations and run-on transitions, so it could be better structured without losing information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-object fetch tool, this is remarkably complete: the output schema covers structure, and the description provides non-obvious field semantics, provenance limitations, quota information, and a save-to-favorites workflow. Nothing essential for calling the tool correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already covers the domain parameter with 100% description coverage and a concrete example, so the baseline is met. The description adds extra meaning by warning that the domain/subdomain may be a funnel, runtime, blog, or redirect rather than a company/product, which is not present in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Main clause 'Fetch one domain surface's FULL profile' pairs a specific verb with a clear resource and scope. It is easily distinguishable from the sibling get_* tools because 'shop'/'domain surface' is explicit in the title and description.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No explicit when-to-use or when-not-to-use guidance relative to siblings. It never mentions search_shops as the discovery step or get_advertiser/get_webmaster as alternatives; the only workflow hint is 'To save it use add_to_favorites', which is a next action, not a selection criterion.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_trendsGet trendsARead-onlyInspect
Get aggregated trend rankings for a dimension: geo, geo_timeline, advertisers, webmasters, apps, timeline or scaling. Common filters: date_from/date_to, date_basis (timeline/geo: fb_start or parsed), country (single ISO code or comma-separated codes for geo_timeline), platform, ai_category/ai_subcategory, media_type, status, format, min_days_active, sort_by/sort_order. Each dimension has its own row semantics — units, what a zero means, which counters are comparable with which. They are NOT repeated here: call the tool and the chosen dimension's full field semantics arrive with the data, in meta.dimension_semantics. One line each so you can pick: geo = point-in-time per-country stock; geo_timeline = batched per-country daily NEW-AD flow; timeline = one segment's daily flow; advertisers = fanpage ranking; webmasters = anonymous affiliate networks (ranking signals only); apps = store-app ranking; domains = landing-domain (brand) ranking, requires a category; scaling = winning/new creatives by launch acceleration, with preset signals or your own thresholds. Returns ranked rows with totals. On dimensions whose total is the real market size (advertisers, apps) the response carries has_more + offset so you know whether another page exists; on webmasters/domains total is returned_rows_only, so has_more is intentionally absent (use search_webmasters.pagination.total for the segment universe). Use for rankings/aggregates, NOT to list individual ads (use search_ads). Rows never carry media URLs on any plan — a trend row is metadata; fetch the asset with get_media (1 token per ad, 10 per creative), the same split every other listing already uses. dimension=scaling pages 50 rows at a time, matching the website panel; other dimensions keep the 200 ceiling. On the FREE plan dimensions scaling / advertisers / apps return a 6-row preview and do NOT paginate — the same slice the site shows before its paywall; passing offset is refused rather than silently answered with page 1. Narrow the filters to preview a different slice, or upgrade for the full ranking. FREE PLAN COUNTS: when the plan window narrows the request, pagination.total_status is "restricted" — the count describes the archive window actually searched, not the one asked for, so a zero means "nothing in the archive window", never "nothing exists". FREE PLAN COUNTS: when the plan's archive window narrows a request, pagination.total_status is "restricted" and meta.plan_window_cutoff names the boundary — the count then describes the window actually searched, NOT the one requested, so a zero means "nothing in the archive window" and never "nothing exists". QUOTA: 1 token per DELIVERED result from your plan balance (free starter: 500 tokens lifetime; paid plans: 40,000/month; short pages auto-refund — you pay only for results you receive). Default page is 20 results = 20 tokens; pass limit (1–200) to size it. get_usage is free. Autonomous agents must be linked to a spytrend account to access data — an unlinked agent gets an actionable connect-your-account refusal (create agent credentials at spytrend.com/settings?tab=ai, or a human claims it by client_id), NOT a server error. Calls are rate-limited per authenticated user (deployment defaults: 60/minute and 1000/hour); a rate-limit refusal is an MCP tool error with structured error.code=rate_limit_exceeded, scope, window and retry_after_seconds. CONCURRENCY: heavy analytical calls (get_trends, search_ads, search_creatives) are admitted ONE AT A TIME per account — fanning out 5-10 of them in parallel does not go faster, it returns admission refusals for all but one. Issue heavy calls sequentially; light lookups (get_ad, get_advertiser, get_usage) are not gated.
| Name | Required | Description | Default |
|---|---|---|---|
| hubs | No | advertisers: filter by hub/destination | |
| mode | No | scaling signal (dimension=scaling only): exploding, early_signal, revival, new or all. Omitted = all signals (the recommended base for custom threshold rules — run min_growth/min_new_ads/… across every signal); the SITE's default tab is exploding, so pass mode='exploding' to mirror it. | |
| limit | No | max rows to return (default 20; each delivered row costs 1 token). Ignored for geo_timeline: that dimension has no pagination and returns every populated date×country point in the accepted window while charging only the requested page size. | |
| format | No | creative format filter | |
| offset | No | scaling: pagination offset — skip the first N rows (use with limit to page through results). | |
| status | No | status filter: active or inactive. NOTE: 'vanished' is honored for ads/webmasters but NOT for dimension=advertisers (no advertiser-level vanished concept — use life_status for advertiser liveness). | |
| born_to | No | advertisers: page founded on or before this date (YYYY-MM-DD) | |
| country | No | single ISO country code filter (legacy form). Prefer countries[] for UI parity and multi-market requests; geo_timeline also accepts up to 200 comma-separated uppercase ISO-2 codes here, and omission means all geos | |
| date_to | No | end date YYYY-MM-DD; for geo_timeline supply together with date_from (both omitted = latest 30 days), max 31 inclusive days for all geos or 90 for a country subset | |
| signals | No | scaling tag classifier version: v2 = honest acceleration (base ≥3 ads over prior 3 days, growth vs 3-day average) and early_signal = fresh multi-account rollout ≤72h; default v1 (the current site behavior) | |
| sort_by | No | column to order the ranking by (dimension-specific, e.g. total_ads). NOTE on ranking: for dimension=advertisers, when a country/ai_category/hub filter is active the RELEVANT pages (smoothed concentration share >= 0.3 in the filtered vertical/geo) come first as a bucket, ordered INSIDE by the honest sort_by column desc — so the printed numbers descend within the relevant bucket and a metric top-N IS collectable; below the bucket sit low-share pages in the same column order. sort_by=active_ads orders by the geo-scoped active value the row displays; sort_by=coverage ranks purely by concentration. For dimension=scaling, sort_by accepts: growth (default), score (composite Spike-Score: a percentile blend of new-ad volume, base-honest growth multiplier, advertiser spread and freshness — each scaling row then also carries a spike_score 0-100), new_ads (most new ads yesterday — the today_ads metric), spread (widest advertiser spread — new fanpages), or freshness (newest creatives first by first_seen); sort_order is ignored (scaling always ranks DESC). | |
| baseline | No | scaling growth baseline: avg (default, window mean) or median (robust to a single outlier day). Affects the 3d/7d comparison window only; 1d is a no-op (single day). Default avg. | |
| platform | No | platform name filter (e.g. facebook, instagram) | |
| snapshot | No | scaling: snapshot source — v2 (default, 8-day detection window, the LIVE site behavior), v4 (37-day spike window, first_parsed_date axis; a PREVIEW contour) or long (Tier-2 Long Trend, 126-day WEEKLY window). ONLY snapshot=v4 unlocks spike_window, comparison=14d/30d, baseline=weekday, advertiser_ids, landing_changed, min_window_advertisers, min_active_now and the active_series/active_now/window_* fields; on v4 the active-history series is YOUNG (read days_of_active_history first). snapshot=long is a DIFFERENT tier — long-term Trend Growth (recent 30d new-ad rate vs the prior 90d baseline, per WEEK), NOT a spike: it accepts ONLY min_trend_growth / min_recent_new / min_advertisers / ai_category / ai_subcategory / media_types / sort=trend_growth|velocity, returns weekly_series[18] + trend_growth + recent_new_28d + prior_new_91d + prior_weekly_avg + window_advertisers with tag='trend_growth', and REFUSES (422) any spike-only param. On long the spike counters (today_ads, growth_multiplier, daily_history, currently_active, active_series, delta_*) are NOT populated — hydrate active/geo numbers per creative via get_creative (creative_id is the representative ad id). | |
| born_from | No | advertisers: page founded on or after this date (YYYY-MM-DD) | |
| countries | No | ISO country codes to INCLUDE. Mirrors the /trends geo include picker, which is multi-select: pass several markets in one call instead of one call per country. | |
| date_from | No | start date YYYY-MM-DD; for geo_timeline supply together with date_to (both omitted = latest 30 days), max 31 inclusive days for all geos or 90 for a country subset | |
| dimension | Yes | one of: geo, geo_timeline, advertisers, webmasters, apps, timeline, scaling, domains. geo_timeline is the batched per-country daily NEW-AD flow; use geo for current per-country stock. For creative trends use scaling (exploding/winning creatives). For BRANDS use domains — it ranks landing domains, the closest thing to a brand the data holds (advertisers are fanpages, webmasters are networks). | |
| same_text | No | scaling (snapshot=v4 ONLY): keep ONLY creatives whose title/hook is the SAME EXACT text — pass a title_norm_hash (a UInt64 as a decimal STRING, from a row's text cluster). This is same EXACT text, NOT 'same angle' (no embeddings). Template/boilerplate text (liquid {{…}}, bare URLs, confirmed CTA like 'Chat with us') is excluded. Ignored on snapshot=v2. | |
| verbosity | No | response size control: omit for the full payload, or compact to drop heavy per-row nested structures (geo: lifetime/geo_distribution; geo_timeline: is_complete, which meta already states for the whole window; timeline: lifetime; scaling: geo_distribution/active_series/daily_history/weekly_series). meta.omitted_row_fields lists exactly what was dropped. Use it when you only need the shape of a market and the full payload would not fit your context. | |
| comparison | No | scaling growth-comparison window: 1d, 3d or 7d. The numerator is always yesterday (d-1); the baseline is the average of that many prior COMPLETE days ending at d-2 — 1d = d-2, 3d = d-2..d-4, 7d = d-2..d-8 (a full seven days). Only these three presets exist on the default snapshot; 14d/30d require snapshot=v4. | |
| date_basis | No | timeline/geo date axis: fb_start (Facebook launch date — the honest axis for 'what the market launched', and the default here) or parsed (when SpyTrend discovered the ad). ⚠️ The spytrend.com /trends page sends parsed by default, so to REPRODUCE a number a user sees on the site pass date_basis=parsed explicitly; the two axes differ because SpyTrend also indexes older ads late. | |
| media_type | No | media type filter: image or video | |
| min_growth | No | scaling: minimum growth multiplier (today_ads ÷ prior-window average) — a FREE-FORM number, e.g. 2, 4.5 or 10 (not limited to x2/x5 presets). Omitted or 0 = NO growth threshold; the SITE's default is ×2, so pass min_growth=2 to mirror it. | |
| sort_order | No | sort direction: asc or desc | |
| store_type | No | apps ONLY: store type (e.g. android, ios). Has NO effect on dimension=advertisers. | |
| ai_category | No | AI category slug filter (e.g. gambling_and_betting) | |
| life_status | No | advertisers only: page life status — alive, deleted or banned | |
| max_new_ads | No | scaling: at most N new ads yesterday — combine with min_new_ads for a band, e.g. 10..50. 0 / omitted = no upper bound. | |
| min_new_ads | No | scaling: minimum NEW ads yesterday — the today_ads metric on each scaling row — free-form integer (e.g. 8). 0 / omitted = no threshold. | |
| creative_age | No | scaling: creative age bucket by first_seen — new (≤3 days), fresh (3-14 days), proven (14-60 days) or old (60+ days). A preset alternative to max_first_seen_hours; combine with it and both narrow. Omitted = no age filter. | |
| spike_window | No | scaling (snapshot=v4 ONLY): spike window in days — 1, 2, 3 or 7. The growth numerator becomes the per-day average of NEW launches over the last W complete days (1 = yesterday, the v2 form). Does NOT change the tag chips (they stay on the fixed yesterday-vs-3-day window). Ignored on snapshot=v2. | |
| webmaster_id | No | restrict the ranking to ads of this webmaster/affiliate id (from search_webmasters). Mirrors the visible /trends webmaster dropdown. Not honored on rollup-backed dimensions — the response then reports it in meta.unsupported_filter instead of silently ignoring it. | |
| first_seen_to | No | only ads spytrend first INDEXED on or before this date (YYYY-MM-DD) — internal discovery date, not the Facebook launch date; for 'new ads' questions use date_from/date_to. See first_seen_from for exact-total windows | |
| followers_max | No | advertisers: maximum page followers | |
| followers_min | No | advertisers: minimum page followers | |
| min_geo_share | No | advertisers only: dominant-geo gate (0-1) — keep only advertisers whose share of ads in the selected country is at least this fraction (e.g. 0.5 = the country is >=50% of the page's ads). Requires country. Without it the country filter is a mere present-in match: a page with 25% of its ads in GB still ranks in the GB top . Share is computed from the page's total per-country ad distribution; a per-country ACTIVE slice is not tracked. | |
| advertiser_ids | No | scaling (snapshot=v4 ONLY): competitor filter — comma-separated advertiser UUIDs; keep only creatives run by ANY of them (hasAny). Invalid UUIDs are dropped; capped at 100. Ignored on snapshot=v2. | |
| ai_subcategory | No | AI subcategory slug filter | |
| min_active_now | No | scaling (snapshot=v4 ONLY): keep creatives with ≥ N ads active RIGHT NOW (active_now). 0 / omitted = no threshold (no default preset). Ignored on snapshot=v2. | |
| min_recent_new | No | scaling (snapshot=long ONLY): minimum recent_new_28d — new ads first seen in the last 28 days (the velocity numerator). 0 / omitted = no threshold. Ignored unless snapshot=long. | |
| first_seen_from | No | only ads spytrend first INDEXED on or after this date (YYYY-MM-DD) — an internal discovery date, NOT the ad's Facebook launch date. Do NOT use it for 'new ads recently' questions: spytrend also indexes OLD ads late, so late-indexed old ads would pollute the answer — use date_from/date_to (Facebook launch) for market newness. Exact totals: any closed window up to 31 days; a single-country request with only AI-category filters can use the canonical daily cube for up to 90 days or from this date through today | |
| landing_changed | No | scaling (snapshot=v4 ONLY): landing-page-change filter — any (default), same (one landing domain over the window), new (a new landing appeared) or multiple (2+ landings — affiliate/cloaking distribution). Ignored on snapshot=v2. | |
| min_advertisers | No | scaling (snapshot=long ONLY): minimum advertisers_total — unique advertisers that ran the creative over the 126-day window (market spread). 0 / omitted = no threshold. Ignored unless snapshot=long. | |
| min_days_active | No | only rows whose entity has ads running at least this many days | |
| min_new_domains | No | scaling: minimum NEW webmasters/domains (new_webmasters) running the creative — the domain-spread signal, mirror of min_new_accounts — free-form integer (e.g. 3). 0 / omitted = no threshold. | |
| min_text_spread | No | scaling (snapshot=v4 ONLY): keep creatives whose EXACT text is shared by ≥ N families (the text-spread market signal — 'this script is being copied by N families', doc §16.5). Only non-template text counts. Each returned row also carries text_spread (families sharing its text) when ≥2 and non-boilerplate. 0 / omitted = no threshold. Ignored on snapshot=v2. | |
| min_new_accounts | No | scaling: minimum NEW accounts (new advertiser fanpages) running the creative — free-form integer (e.g. 6). 0 / omitted = no threshold. | |
| min_trend_growth | No | scaling (snapshot=long ONLY): minimum trend_growth — (new ads over the last 30d ÷ 4 weeks) ÷ (new ads over the prior 90d ÷ 13 weeks), i.e. the recent per-week new-ad rate vs the baseline per-week rate. Free-form float (e.g. 1.5, 2, 3); >1 = sustained growth. 0 / omitted = no threshold. Ignored unless snapshot=long. | |
| exclude_countries | No | ISO country codes to EXCLUDE — drops rows whose ads target ANY of them. Mirrors the /trends geo EXCLUDE column. Honored on timeline (and the webmaster-scoped path); NOT applicable to geo_timeline, whose rows are per-country cube aggregates rather than per-ad geo arrays — that response names it under meta.ignored_filters instead of pretending it ran. | |
| max_first_seen_hours | No | scaling: keep only creatives first seen within the last N hours — your own freshness window (the 'new' preset is fixed at 48h; this is free-form, e.g. 24 or 72). | |
| min_window_advertisers | No | scaling (snapshot=v4 ONLY): market-trend threshold — keep creatives with ≥ N unique advertisers over the 37-day window (the 'spreading across the market' signal, doc §16.5). 0 / omitted = no threshold. Ignored on snapshot=v2. |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | |
| meta | No | |
| total | Yes | |
| offset | No | |
| has_more | No | |
| snapshot | No | |
| total_status | No | |
| weeks_of_history | No | |
| days_of_active_history | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, openWorldHint=true, and destructiveHint=false, so the safety profile is clear. The description adds substantial value on top: quota and token accounting, free-plan preview restrictions, rate-limit error codes, one-at-a-time concurrency admission, auth requirements for autonomous agents, and which dimensions return has_more vs. intentionally omit it. There is no contradiction with the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the purpose and dimension selector, then logically organized into plan, quota, auth, rate-limit, and concurrency sections. It is extremely dense and useful for a 52-parameter tool, but it repeats the FREE PLAN COUNTS passage almost verbatim, which is an avoidable redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers dimension row semantics, pagination semantics, free-plan limitations, quota accounting, rate limiting, concurrency, authentication, and even tells the agent that full field semantics arrive in meta.dimension_semantics. With an output schema present, nothing material is missing for an agent to select and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is already high, but the description still adds meaningful parameter-level guidance: dimension-specific filter behavior, site-mirroring defaults (date_basis=parsed, mode='exploding', min_growth=2), snapshot=long restrictions, and free-plan refusal semantics. This goes well beyond simply restating the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource ('Get aggregated trend rankings for a dimension') and then enumerates every dimension, including a one-line definition for each. It also explicitly contrasts the tool with search_ads, making its purpose unmistakable. The first sentence omits 'domains', but the later per-dimension list resolves that ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says when to use the tool ('Use for rankings/aggregates') and when not to ('NOT to list individual ads (use search_ads)'). It also names sibling alternatives such as get_media and search_webmasters for specific follow-up actions, and explains free-plan versus paid-plan behavior with concrete pagination guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_usageGet usage / quotaARead-onlyIdempotentInspect
Return the caller's current MCP quota: plan, tokens used, remaining and limit (free starter = 500 tokens total forever; paid plans = 40,000/month ≈ 2,000 default-page searches; Enterprise = personal limit via support). Token costs: 1 per delivered result (an ad/webmaster/advertiser row, a trends row, an entity card), ad media = 1, creative media = 10, TikTok row = 100. You pay only for results actually delivered (short pages auto-refund). This call is FREE — it consumes no quota.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | |
| plan | Yes | |
| used | Yes | |
| limit | Yes | |
| window | Yes | |
| reset_at | No | |
| remaining | Yes | |
| unlimited | No | |
| server_version | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, the description discloses the key behavioral fact that the call consumes no quota, and it explains the broader cost model including auto-refund behavior for short pages. It also clarifies plan-specific limits, adding meaningful context not present in the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is information-dense but well organized, front-loading the core purpose before plan details and cost rules. Every sentence adds value: the plan breakdown, token costs, and the free-call note are all relevant to using the tool correctly.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no parameters and an output schema present, the description fully covers the situational and behavioral context an agent needs: what the quota is, how plans differ, how token costs are computed, refund behavior, and that the call itself is free.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters and the schema is an empty object, so there is nothing for the description to document. The baseline of 4 applies because no parameter explanation is needed and the description doesn't mislead about parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: "Return the caller's current MCP quota." It enumerates the included fields (plan, tokens used, remaining, limit), making the tool's function unambiguous and distinct from sibling data-fetching tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear context: it explains plan variants, token costs, and that "This call is FREE." It doesn't explicitly name alternatives or exclusion criteria, but the tool is unique among siblings and its purpose is so evident that no alternative-routing is needed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_webmasterGet webmaster by idARead-onlyInspect
Fetch a single webmaster (affiliate NETWORK) by UUID: aggregate stats PLUS top_creatives — the creatives this webmaster re-uploads the MOST, ordered by the webmaster's OWN in-slice ad count (slice.ads_in_slice desc — the deliberate slice order, matching what the profile page shows), media stripped/free. Each entry's total_ads is the creative's market-wide family size, folded best-effort from family redirects and floored at ads_in_slice — total_ads == ads_in_slice therefore often means 'family cards not yet built', not 'this webmaster owns the whole market'; it is context, not the sort key. analytics_pending=true means the canonical PostgreSQL identity exists but its ClickHouse aggregate is not published yet, so zero counters are not analytical zeroes. fanpages_status explicitly reports advertiser-aligned lifecycle enrichment as available, partial, unavailable or not_applicable; an omitted fanpages array is therefore never evidence that a page is alive. The id MUST be the canonical UUID returned by search_webmasters; domain names and display names are rejected instead of silently widening to the whole database. Present top_creatives as the headline final step; for the rest of the network use search_creatives?webmaster_id / search_ads?webmaster_id, and download media with get_media (entity_type=creo). identifier_inventory_semantics explains why historical page/domain/pixel inventory counts are not comparable to total_ads. attribution_link_stats.merge_edges answers WHY these identifiers sit in one network: each entry is a {page_id, page_name, domain, ads} pair — that fanpage ran exactly that many ads on that landing domain, and such a pair is what merges two networks into one (an ad whose domain belongs to network A and whose fanpage belongs to network B fuses them permanently). Weigh the edges before claiming a single operator: a 1-3 ad edge next to 100+ ad edges is a thin bridge, typically a catalog/feed ad, and is weak evidence of shared ownership; identifiers with no edge at all are inherited from earlier merges and prove nothing today. QUOTA: 1 token (one entity card). TIKTOK: with source=tiktok this fetches the TikTok cluster card instead (business-id rollup, same UUID space as search_webmasters source=tiktok results) — ⚠️ costs 100 tokens and requires a Pro-or-higher plan; the Meta-only embeds (top_creatives, attribution link-stats) are omitted on the TikTok card.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | the webmaster UUID (from search_webmasters — same id space for both corpora) | |
| source | No | webmaster corpus: meta (default, FREE) or tiktok (⚠️ 100 tokens for the TikTok cluster card; Pro plan required) |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds substantial behavioral context beyond the readOnly/openWorld/destructive hints: 1-token quota, 100-token TikTok cost, analytics_pending semantics, total_ads folding and flooring, fanpages_status lifecycle meaning, and merge-edge interpretation. These are exactly the non-obvious behaviors an agent needs to avoid misreading results.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long, but it is information-dense and front-loaded: the core fetch behavior appears first, followed by warnings, field semantics, alternatives, and quota/TikTok caveats. Every sentence carries meaningful guidance; none is filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description is complete given the call's complexity and the presence of an output schema. It covers input constraints, token costs, plan requirements, open-world caveats, sensitive field interpretations, source-specific behavior, and sibling endpoints for extending the query. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema coverage is already 100%, the description enriches both parameters: id must be the canonical UUID from search_webmasters and domain/display names are rejected, while source=tiktok is explained as the business-id cluster card with Meta-only embeds omitted. This goes well beyond the schema's enum and type descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Fetch a single webmaster (affiliate NETWORK) by UUID.' It clearly defines the return focus — aggregate stats plus top_creatives — and explicitly distinguishes the canonical-UUID requirement from silent widening, which separates it from search-style siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit routing guidance: for the rest of the network use search_creatives?webmaster_id / search_ads?webmaster_id, and download media with get_media. It also defines when the TikTok variant applies and that it requires Pro or higher, leaving no ambiguity about when this tool should be selected.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_webmaster_similarity_facetsGet webmaster similarity facetsARead-onlyIdempotentInspect
List the URL-parameter keys and normalized values that can be used to find related webmasters. Similarity here means shared affiliate/tracking parameter values, not name or domain similarity. Returns only facets that have at least one other webmaster match. FREE.
| Name | Required | Description | Default |
|---|---|---|---|
| webmaster_id | Yes | source webmaster UUID from search_webmasters |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | |
| webmaster_id | Yes | |
| similarity_basis | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, openWorld, and non-destructive behavior, so the description adds value by disclosing extra behaviors: only facets with at least one other webmaster match are returned, and the tool is FREE. It also clarifies the semantic scope of similarity, which is not present in annotations. No contradiction exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded, with the key verb and resource in the first sentence. Each subsequent sentence earns its place: the similarity definition, the match-filtering behavior, and the cost note. No filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With one fully documented parameter, strong annotations, and an output schema present, the description covers the important non-schema context: what facets are, how similarity is defined, and that only matched facets are returned. An agent has enough information to select and invoke this tool correctly without confusion.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the single parameter webmaster_id is already fully documented as 'source webmaster UUID from search_webmasters'. The description itself does not add significant parameter-level detail beyond the tool's overall purpose. This matches the baseline for high schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('List') and a clear resource ('URL-parameter keys and normalized values' / facets). It also actively disambiguates the meaning of similarity by stating it is about shared affiliate/tracking parameter values, not name or domain similarity. This clearly separates it from siblings like find_similar_webmasters and get_webmaster.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly frames when to use the tool: to discover URL-parameter facets that can be used for finding related webmasters. It gives helpful context about what similarity means and that only facets with at least one other match are returned. It does not explicitly name alternatives or say when not to use it, but the use case is clear enough for an agent.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_favoritesList favouritesARead-onlyIdempotentInspect
List the caller's favourites folders (name, entity_type, item_count). Pass entity_type (creo/webmaster/ad/hub/shop) to filter; omit it to list folders across all types. Pass folder (a folder name) to instead return the ITEMS inside that folder: entity ids for creo/webmaster/ad (usable with get_creative / get_webmaster / get_ad), resolved hub profiles (hub + id + label) for hub folders, and domains for shop folders. Use this to confirm what add_to_favorites saved and to read a folder back. FREE — does not consume quota.
| Name | Required | Description | Default |
|---|---|---|---|
| folder | No | OPTIONAL folder name; when set, returns the ITEMS inside that folder (ids for creo/webmaster/ad — usable with get_creative/get_webmaster; resolved hub profiles for hub folders; domains for shop folders) instead of the folder list. If a name matches folders of several types, pass entity_type to disambiguate | |
| entity_type | No | optional filter: creo, webmaster, ad, hub or shop; omit to list folders of all types |
Output Schema
| Name | Required | Description |
|---|---|---|
| folders | No | |
| folder_contents | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, and non-destructive behavior, and the description adds useful behavioral context beyond them: the operation is caller-scoped, consumes no quota, and the return content differs by folder type. No contradiction exists between the description and annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the primary purpose, then packs filtering, folder-item behavior, return shapes, a use-case, and quota information into a compact set of sentences. Every sentence earns its place and none are redundant with the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the tool's scope, all parameter behaviors, return-shape differences, the intended use case, and quota impact. With an output schema present and no required parameters, an agent has everything needed to select and invoke this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents both parameters in detail. The description adds some interpretive context such as using returned ids with get_creative/get_webmaster/get_ad, but it largely restates what the schema already provides, so a baseline score of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('List') with a clear resource ('the caller's favourites folders') and explicitly explains the two modes: listing folders or listing items inside a named folder. It also distinguishes itself from add_to_favorites, its write counterpart, making the tool's role unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit usage guidance: pass entity_type to filter, omit it to list across all types, and pass folder to read items. It also tells the agent when to use this tool — to confirm what add_to_favorites saved and to read a folder back — naming the sibling tool that performs the corresponding write.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_adsSearch adsARead-onlyInspect
Search the spytrend.com ad database and return ad METADATA: title, body_chars (the LENGTH of the ad text in UTF-8 characters), landing domain, status, geos, activity, advertiser, ai_enrichment (ai_category/media_type), media_count and creative-format metadata. The ad TEXT ITSELF is deliberately NOT in list rows — an average body is ~1.1k characters and would bloat a 20-row page 20-80x; fetch the text for a specific ad with get_ad, and use body_chars plus min_body_chars/max_body_chars to find the ads worth opening. The visible /ads Format filter targets creative_formats (video/carousel/single/dynamic); publisher placements are filtered separately via platforms. The downloadable creative media is NOT included here — fetch it with get_media using the returned ids. Filters: keyword (query), country/countries (multi-geo), platforms, categories (ai_category slug), ai_subcategory, status_today (active/inactive/vanished), advertiser_id, webmaster_id, landing_domain, media_type; languages, formats (creative_formats: video/carousel/single/dynamic), cta_buttons, domain_zones, search_in (which field query matches: all/title/advertiser/text), platforms_mode (platforms any/all); point lookups pixel_id / page_id / resolved_ip and contains_in_links (tracking fragments like pixel_id=/sub1=/utm); ranges min_days_active/days_active_to, min_page_likes/max_page_likes, min_body_chars/max_body_chars (ad-text length in characters; both directions cover only ads with known non-empty text), date_from/date_to (Facebook launch date — THE axis for 'new ads in a window': new = launched on Facebook then; first_seen_from/to is the date we first indexed the ad, which also catches late-indexed OLD ads — never use it for market newness); max_countries (with selected countries: additional GEOs outside them; without selected GEO: total GEOs), dedupe (unique creatives only), and saved/folder scopes (saved=all, folder_id). Sort with sort_by + sort_order. Use sort_by=most_reused_creative to rank the selected results by how many ads share the same creative; rows include same_creative_ads_in_selection and other_ads_with_same_creative for the selected filters, plus same_creative_ads_lifetime and advertisers_using_same_creative for the lifetime catalog. creative_match_type=exact identifies the matching contract and creative_reuse_status=exact|unavailable qualifies the counts. For most_reused_creative the absolute number of distinct creative groups is intentionally unavailable/null; page with has_more and next_cursor instead of retrying for a total. To find ads on a domain use landing_domain — NOT query. Cursor pagination via next_cursor; pagination always includes total and total_status (exact/estimated/unavailable), plus counted_at/count_cached for exact analytical totals. Any closed launch OR discovery window of at most 31 calendar days completes a raw exact unique-ad count in-call. A discovery request with exactly one country plus optional AI category/subcategory filters can instead use the canonical daily cube for an exact count across up to 90 inclusive days, or from first_seen_from through today; no other filters may be present on that cube path. Other wider/open combinations use the normal estimated/unavailable contract, and malformed/reversed dates are rejected. An exact count that includes today is exact for ads ingested so far, while analysis.includes_open_utc_day and data_complete_through make the incomplete tail explicit. status_today always means CURRENT state: with a date window, active is the currently-active survivors of that selected cohort, not historical activity on each date. Except for the intentional most_reused_creative group-total omission described above, unavailable means the async count did not settle inside this call: the number is PENDING, not zero and not a measurement; re-issue the identical call in a few seconds to obtain it (analysis.count_note repeats this warning in-band). NEVER present an unavailable count as 0. With categories set, matches and totals cover AI-LABELED ads only — a labeled SUBSET of the market (analysis.category_semantics=ai_labeled_ads_only_not_comparable_to_extrapolated_trends); get_trends aggregates for the same category×geo are extrapolated market estimates and will always be larger — never cross-compare the two. For an advertiser's or webmaster's EXACT ad count (total AND active), do NOT paginate search_ads — call get_advertiser / get_webmaster. To download the creatives, pass the ids to get_media; to SAVE them, pass the ids to add_to_favorites. Respects the caller's plan/verticals. For aggregate rankings use get_trends. QUOTA: 1 token per DELIVERED result (default page 20 = 20 tokens; short pages auto-refund — you pay only for results you receive). Results carry metadata only (no downloadable media) — the creative MEDIA is billed separately via get_media (1 token per ad, 10 per creative). Request small limits and narrow filters instead of paginating wide. TIKTOK SOURCE: pass source="tiktok" to query the TIKTOK ad corpus instead of Meta. (The platforms filter does NOT do this — platforms are Meta publisher placements.) ⚠️ TIKTOK IS PAID PER ROW at a premium: every DELIVERED TikTok ad row costs 100 tokens and every TikTok webmaster row/card costs 100 tokens (Meta rows cost 1), deducted from the same token balance (free starter: 500 tokens total = 5 TikTok rows EVER; paid plans: 40,000/month = up to 400 TikTok rows). Request small limits and narrow filters; you are charged only for rows actually delivered (short pages auto-refund; if the balance covers only part of the request, that part is delivered — a 3-row request on a 150-token balance returns 1 row and charges 100). TikTok requires a Pro-or-higher plan: free/starter callers and unlinked agents get an actionable upgrade refusal, never data and never a charge. TikTok rows have their OWN shape, returned under tiktok_data (verified live 2026-07-30): id/external_id (TikTok ad id), advertiser{id,external_id,name} plus advertiser_id (TikTok BUSINESS id), start_date/end_date (the ad's delivery window) and first_seen_date/last_seen_date (SpyTrend indexing), days_active, is_active + status_today, ai_category/ai_subcategory, countries + targeting_geos + targeting_details (OS/age/gender/regions), audience_size, objective, sponsor (the 'paid for by' funder) and registry_location, landing_domain + link_url, body/title/call_to_action, creative_format, creative_id, creative_ad_count (how many ads reuse that creative), is_blurred/is_cloaked, and media[] with media_type + thumbnail_url on media-tt.spytrend.com. Media comes WITH the row — do NOT call get_media for TikTok ids (it serves Meta entities only). There are no TikTok engagement counters (plays/likes/comments/shares) on this surface. The TikTok feed serves the ARCHIVED TikTok corpus — exactly what the spytrend.com /ads TikTok tab shows — and pagination carries has_more/next_cursor plus total with total_status (exact unless ttads marks the count estimated). With source=tiktok the honored filters are: query (+search_in), country, categories, landing_domain, advertiser_id (TikTok business id), contains_in_links, media_type, cta_buttons, domain_zones, status_today (active/inactive only), min_days_active/days_active_to, max_countries (with country: additional GEOs outside it; without country: total GEOs), dedupe, date_from/date_to, sort_by=date|days_active, limit (default 10 for TikTok — rows are paid), cursor. Meta-only params (platforms/formats/languages, pixel_id/page_id/resolved_ip, webmaster_id, first_seen_from/to, page-likes ranges, hub_category/channel) are REJECTED with an explanation rather than silently ignored.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | max rows to return (default 20; each delivered row costs 1 token). IGNORED on geo and geo_timeline — both return their complete set (geo: the whole per-country stock table, ~194 rows; geo_timeline: every populated date×country point) while charging only the page size you asked (a flat 20 by default). Those responses can be large: pass verbosity=compact to drop the heavy per-row nested structures. | |
| query | No | free-text keyword to match in ad title, body or link | |
| saved | No | saved scope selector. Use saved=all to restrict results to ads saved in ANY favorites folder, matching the saved-ads view on /ads. | |
| cursor | No | pagination cursor from a previous response's next_cursor | |
| dedupe | No | when true, collapse duplicates so each unique creative appears once | |
| source | No | ad corpus to search: meta (default — the Facebook/Meta library, 1 token per delivered row) or tiktok (the TikTok corpus — ⚠️ PAID: 100 tokens per DELIVERED TikTok ad row, embedded media links included; Pro plan required; default limit drops to 10). platforms=[tiktok] does NOT do this. | |
| channel | No | messaging channel the ad sends traffic to: whatsapp or telegram. Filters to ads landing on that channel's chat domains (whatsapp: api.whatsapp.com/wa.me/chat.whatsapp.com; telegram: t.me). Use for 'ads/bundles going to WhatsApp/Telegram'. | |
| country | No | single ISO country code filter (legacy form, e.g. US, BR, DE). Prefer countries[] for UI parity and multi-country requests. | |
| date_to | No | only ads whose Facebook launch date is on or before this date (YYYY-MM-DD); combine with date_from in a closed window of at most 31 calendar days for an exact in-call total | |
| formats | No | creative format filter from the visible /ads Format control: video, carousel, single or dynamic. Distinct from platforms, which filters publisher placements like facebook/instagram. | |
| page_id | No | point lookup: only ads from this Facebook page id; also accepts a pasted facebook.com page link in any form (vanity, profile id, profile.php, Ads Library view_all_page_id) — resolved to the canonical page id automatically | |
| sort_by | No | order results by: date (default, newest first), most_popular, folder_added or most_reused_creative. most_reused_creative ranks ads by how many ads share the same exact creative within the selected filters and dates; default/desc gives most reused first, asc gives least reused first. It requires a specific advertiser/page/pixel/webmaster/query/landing-domain/creative anchor or a launch/discovery window of at most 31 days. Rows include semantic same-creative reuse counts and creative_reuse_status. folder_added is only meaningful on folder-scoped or saved=all views; most_popular is the Trending preset ranking | |
| pixel_id | No | point lookup: only ads carrying this Facebook pixel id | |
| countries | No | ISO country codes to filter by. Mirrors the /ads UI multi-country picker. With country_match=any (default) an ad matches when at least one selected geo is present; with country_match=only every known geo must belong to this set. | |
| date_from | No | only ads whose Facebook launch/delivery start date is on or after this date (YYYY-MM-DD). THE canonical date axis for 'new ads in a window' questions — new means LAUNCHED ON FACEBOOK in that window, not indexed by spytrend. Combine with date_to in a closed window of at most 31 calendar days for an exact in-call total | |
| folder_id | No | restrict results to ads saved in this favorites folder UUID. Mirrors the /ads favorites page scope. | |
| languages | No | ISO language codes the ad targets (e.g. en, pt, es). The /ads Language filter. | |
| platforms | No | platform names to include (e.g. facebook, instagram) | |
| search_in | No | which field the free-text query matches: all (default), title, advertiser or text. The /ads search-scope toggle. | |
| categories | No | AI category slugs to include. Use the underscore slug form, e.g. gambling_and_betting, ecommerce_and_retail, finance_and_banking, dating_and_relationships, healthcare_and_medical, impersonation_funnels, crypto_and_trading (NOT short words like 'gambling') | |
| media_type | No | filter by creative media type: image or video | |
| sort_order | No | sort direction: asc or desc (default desc) | |
| cta_buttons | No | call-to-action button labels (e.g. Shop Now, Learn More, Sign Up, Download). The /ads CTA filter. | |
| hub_domains | No | exact hub/destination domains to filter landings by (e.g. linktr.ee, wa.me). The visible /ads Hub filter sends these; hub_category stays the coarse category form. When channel is also set, the explicit domains and channel domains are combined deterministically into one OR-list. | |
| resolved_ip | No | point lookup: only ads whose landing domain resolves to this IP | |
| domain_zones | No | landing-domain TLD/zone without the dot (e.g. com, shop, online, xyz). The /ads domain-zone filter. | |
| hub_category | No | filter to ads whose landing destination is in this TOP-LEVEL hub category. Valid slugs: social, app_stores, shortlinks, amazon, ecommerce, popular, platforms (NOT 'social_media' — the slug is 'social'). For a specific messaging channel use the channel param instead - hub_category does NOT accept whatsapp/telegram sub-slugs. | |
| status_today | No | current status: active, inactive or vanished | |
| webmaster_id | No | return only ads from this webmaster id (from search_webmasters) | |
| advertiser_id | No | return only ads from this advertiser id (from search_advertisers) | |
| country_match | No | how the countries filter matches: any (default, overlap semantics) or only (strict subset semantics). The /ads UI sends any explicitly when countries are selected. | |
| first_seen_to | No | only ads spytrend first INDEXED on or before this date (YYYY-MM-DD) — internal discovery date, not the Facebook launch date; for 'new ads' questions use date_from/date_to. See first_seen_from for exact-total windows | |
| max_countries | No | with countries or country selected, allow at most this many additional GEOs outside that selected set; without a selected GEO, allow at most this many total GEOs | |
| ai_subcategory | No | AI subcategory slugs to include under the chosen ai_category set. Mirrors the visible /ads subcategory drill-down. | |
| days_active_to | No | upper bound of the days-active range — only ads running at most this many days | |
| landing_domain | No | landing domain filter. By default it preserves the historical /ads behavior: match this hostname AND its subdomains. Set landing_domain_exact=true to restrict to the normalized hostname itself only. | |
| max_body_chars | No | only ads whose AD TEXT is at most this many characters long (UTF-8 characters, not bytes). Ads with no text are NOT returned as length 0 — the length filters cover only ads with known non-empty text. Combine with min_body_chars for a closed length range, e.g. 100..500 | |
| max_page_likes | No | only ads from pages with at most this many likes | |
| min_body_chars | No | only ads whose AD TEXT is at least this many characters long (UTF-8 characters, not bytes). Both length filters match only ads whose text length is KNOWN and non-zero: ads with no text at all are outside the length axis entirely and are returned by neither direction. Pairs with the body_chars field returned on every row; the ad text itself is served by get_ad, not by this list | |
| min_page_likes | No | only ads from pages with at least this many likes | |
| platforms_mode | No | how the platforms filter combines: any (OR, default) or all (AND — the ad must run on EVERY selected platform). The /ads platform match-mode toggle. | |
| first_seen_from | No | only ads spytrend first INDEXED on or after this date (YYYY-MM-DD) — an internal discovery date, NOT the ad's Facebook launch date. Do NOT use it for 'new ads recently' questions: spytrend also indexes OLD ads late, so late-indexed old ads would pollute the answer — use date_from/date_to (Facebook launch) for market newness. Exact totals: any closed window up to 31 days; a single-country request with only AI-category filters can use the canonical daily cube for up to 90 days or from this date through today | |
| min_days_active | No | only ads running at least this many days (e.g. 30 → long-running 'winning' ads). Lower bound of the days-active range | |
| contains_in_links | No | match a fragment inside the ad's tracking links - e.g. a pixel id, sub id or UTM fragment like 'pixel_id=123', 'sub1=', 'utm_campaign=xyz' | |
| landing_domain_exact | No | when true, restrict landing_domain to the normalized hostname itself and exclude subdomains. Mirrors the /ads UI exact-host toggle. |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | |
| meta | No | |
| source | No | |
| analysis | No | |
| pagination | Yes | |
| tiktok_data | No | |
| auto_applied_verticals | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only indicate readOnly/openWorld/non-destructive, so the description carries the behavioral burden and fulfills it richly. It discloses the token-cost model (1 per Meta row, 100 per TikTok row), exact/estimated/unavailable count semantics, the 'unavailable is pending, not zero' rule, status_today's current-state interpretation, and the AI-labeled-subset caveat. No contradiction with annotations exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single dense block with no section breaks, lists, or formatting, making it hard for an agent to scan quickly. It also repeats details already present in the input schema, such as formats, status_today values, and min/max body_chars semantics. While front-loaded with a clear purpose, its excessive length and lack of structure undermine its usability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex 45-parameter search tool, the description covers every operationally relevant area: filter semantics, pagination, exact-count windows, quota/token costs, status interpretations, TikTok-specific behavior, and unsupported-parameter rejections. Given that an output schema exists, not restating return shapes is acceptable; nothing critical for correct invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with detailed per-parameter descriptions, so the baseline is 3. The description adds cross-parameter value by grouping filters, explaining the date_from vs. first_seen_from distinction, max_countries behavior with and without a selected GEO, and which parameters are honored or rejected on the TikTok source. However, much of the parameter-level detail already appears in the schema, so the increment above baseline is moderate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Search the spytrend.com ad database' and explicitly enumerates the returned metadata fields (title, body_chars, landing domain, status, etc.). It also distinguishes itself from siblings by stating what it deliberately does not return (ad text, media) and pointing to get_ad and get_media for those needs.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit routing rules: fetch ad text with get_ad, media with get_media, exact advertiser/webmaster counts with get_advertiser/get_webmaster, aggregate rankings with get_trends, and saving with add_to_favorites. It also warns not to use query for domain lookups and not to use first_seen_from/to for market newness, steering instead to landing_domain and date_from/date_to.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_advertisersSearch advertisersARead-onlyInspect
Search advertisers by NAME, numeric Facebook page id, or a pasted facebook.com page link (typeahead; query ≥2 chars). A link in any form (vanity name, profile 100…-id, profile.php, Ads Library view_all_page_id) and either id namespace resolve to the canonical fanpage with the exact match first. Returns advertisers with external_id and ad_count. To fetch ONE advertiser's full card by id use get_advertiser; to see an advertiser's ads, call search_ads with advertiser_id. For advertiser RANKINGS, ordering and filters (category/country/followers/founded-date/life_status/hubs), use get_trends with dimension=advertisers. FREE PLAN COUNTS: when the plan's archive window narrows a request, pagination.total_status is "restricted" and meta.plan_window_cutoff names the boundary — the count then describes the window actually searched, NOT the one requested, so a zero means "nothing in the archive window" and never "nothing exists". QUOTA: 1 token per DELIVERED result from your plan balance (free starter: 500 tokens lifetime; paid plans: 40,000/month; short pages auto-refund — you pay only for results you receive). Default page is 20 results = 20 tokens; pass limit (1–200) to size it. get_usage is free. Autonomous agents must be linked to a spytrend account to access data — an unlinked agent gets an actionable connect-your-account refusal (create agent credentials at spytrend.com/settings?tab=ai, or a human claims it by client_id), NOT a server error. Calls are rate-limited per authenticated user (deployment defaults: 60/minute and 1000/hour); a rate-limit refusal is an MCP tool error with structured error.code=rate_limit_exceeded, scope, window and retry_after_seconds. CONCURRENCY: heavy analytical calls (get_trends, search_ads, search_creatives) are admitted ONE AT A TIME per account — fanning out 5-10 of them in parallel does not go faster, it returns admission refusals for all but one. Issue heavy calls sequentially; light lookups (get_ad, get_advertiser, get_usage) are not gated.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | max results, 1-50 (default 20 — this typeahead is capped at 50 upstream; a larger value is clamped, not rejected). Each delivered result costs 1 token. | |
| query | No | advertiser name (at least 2 characters), a numeric Facebook page id (either the Ads Library page id or the direct-page profile 100…-id), OR a pasted facebook.com page link in any form (vanity, profile.php, Ads Library) — links and ids resolve to the canonical fanpage with the exact match first |
Output Schema
| Name | Required | Description |
|---|---|---|
| meta | No | |
| items | Yes | |
| total | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark the call read-only and non-destructive, and the description goes much further: it discloses plan-window count restrictions, quota/token accounting, required account linking for autonomous agents, rate-limit refusal behavior, and one-at-a-time concurrency for heavy calls. These are exactly the behavioral traits an agent needs to avoid failed or expensive calls.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Long but well labeled and front-loaded: core semantics and alternatives come first, then quotas, auth, rate limits, and concurrency. Some sections are dense enough to be hard to scan, but almost every sentence earns its place; only minor trimming would improve it.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with quota, authentication, rate-limit, and concurrency constraints, the description covers all operationally critical areas while the output schema covers return structure. The only completeness issue is the limit-range contradiction, but that is better attributed to parameter semantics.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema covers both parameters at 100%, so the baseline is 3. The description adds useful cost semantics (1 token per delivered result; default 20) and query-form resolution, but it says limit is 1–200 while the schema says 1–50 with clamping, a conflicting instruction that makes the added semantic value partially unreliable.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific action and resource: 'Search advertisers by NAME, numeric Facebook page id, or a pasted facebook.com page link', and specifies the typeahead behavior (query ≥2 chars) and primary returns (external_id, ad_count). It also clearly contrasts with search_ads and get_advertiser, so an agent can tell which tool is intended.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly routes to alternatives: get_advertiser for one full card, search_ads for an advertiser's ads, and get_trends with dimension=advertisers for rankings, ordering, and filters. This is direct when-to-use vs when-not-to-use guidance with named sibling tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_creativesSearch creativesARead-onlyInspect
Browse and filter unique creative families and return metadata only. Filters include country/countries, exclude_countries, categories, ai_subcategory, media_type/media_types, domain_zone, advertiser_id/advertiser_ids, webmaster_id, fanpages, hub_domains/channel, active_ads_today_from, last_seen_from/to, period_from/to, max_countries, min_geo_share, folder_id and saved=all. last_seen_from/to is creative activity eligibility; period_from/to (date_from/to aliases) is the independent in-period window for ads_in_period. Sort by relevance, total_ads, ads_in_period/reuploads, active_ads, last_seen or fb_created. With an advertiser/webmaster slice, pagination.sort_semantics=slice_ordered_by_webmaster|slice_ordered_by_advertiser and slice.ads_in_slice is the ordering key. total_ads remains the lifetime whole-family count; ads_in_period is the count for the selected period on the backend-reported date axis. Always read pagination.period_axis: first_seen means first discovery by SpyTrend; facebook_start_date means launch on Facebook after the verified launch-generation cutover. Creatives have no free-text search; use search_ads for discovery. Cursor-paginated. Download with get_media and save with add_to_favorites. QUOTA: 1 token per DELIVERED result (default page 20 = 20 tokens; short pages auto-refund — you pay only for results you receive). Results carry metadata only (no downloadable media) — the creative MEDIA is billed separately via get_media (1 token per ad, 10 per creative). Request small limits and narrow filters instead of paginating wide. PERIOD PARITY CONTRACT: the /creo web filters ai_subcategory, hub_domains[] and active_ads_today_from are available here too. last_seen_from/to filters the creative's LAST recorded activity and does not change count semantics. period_from/period_to is the independent inclusive window used for ads_in_period; date_from/date_to are compatibility aliases. A closed explicit period enables ads_in_period automatically. sort_by=ads_in_period or reuploads ranks by that measured in-period count, while total_ads remains lifetime family reuse. The legacy period_metric=ad_debuts plus closed last_seen bounds remains accepted temporarily. Empty pages do not fabricate a count. Invalid/open/reversed periods are rejected before quota or upstream work, and a backend response missing ads_in_period is rejected rather than presented as zero. AXIS CONTRACT: read pagination.period_axis on every period response. "first_seen" means the ad was first discovered by SpyTrend; "facebook_start_date" means the ad was launched on Facebook, and is emitted only after the verified launch-generation cutover. Never assume an axis from the request or description.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | max metadata rows (default 20, max 200; each delivered row costs 1 token) — fetch media with get_media (entity_type=creo, 10 tokens per creative). | |
| query | No | free-text keyword search over the creative FAMILY: every word must appear in the texts of the creative's ads (all of them, not one representative ad). Combine with any other filter; use search_in to narrow the area. | |
| saved | No | saved scope selector. Use saved=all to restrict results to creatives saved in ANY favourites folder. | |
| cursor | No | pagination cursor | |
| channel | No | messaging channel the creative sends traffic to: whatsapp or telegram. Filters to creatives landing on that channel's chat domains. Use for 'bundles going to WhatsApp/Telegram'. | |
| country | No | ISO-2 country code to filter creatives shown in that geo (e.g. US, BR). Country may be combined with categories, ai_subcategory, media_type/media_types, max_countries, advertiser_id/advertiser_ids, webmaster_id, fanpages, hub_domains, domain_zone, active_ads_today_from, last_seen dates, min_geo_share and supported sorting filters. Do not combine a country scope with hub_category or channel. | |
| date_to | No | compatibility alias of period_to for the in-period window; read pagination.period_axis to learn which date axis was used | |
| sort_by | No | order by: relevance (needs country), total_ads (lifetime), ads_in_period/reuploads (count in a closed period; read pagination.period_axis for its date axis), active_ads, last_seen or fb_created | |
| fanpages | No | the /creo fanpages filter: keep only creatives that ran on ANY of these fanpage ids (advertiser external_ids — the Facebook page ids; OR-semantic). Entries may also be pasted facebook.com page links in any form (vanity, profile id, Ads Library) — each is resolved to its canonical page id. Get page ids from get_creative's fanpages, an ad's page_id (search_ads), or get_advertiser's external_id. Max 50. | |
| countries | No | ISO country codes to INCLUDE. The visible /creo geo picker is a multi-select — pass several markets in one call instead of one call per country. | |
| date_from | No | compatibility alias of period_from for the in-period window; read pagination.period_axis to learn which date axis was used | |
| folder_id | No | restrict results to creatives saved in this favourites folder UUID. Mirrors the /creo folder view. | |
| period_to | No | in-period window end (YYYY-MM-DD), independent from creative last activity; requires period_from; read pagination.period_axis to learn which date axis was used | |
| search_in | No | where to search when query is set: all (default - ad headline/link text/CTA, fan page names, landing domains AND ad body text), title (headline/link description/CTA), text (ad body text), advertiser (fan page names). Ignored without query. | |
| categories | No | AI category slugs to filter by (e.g. gambling_and_betting, impersonation_funnels, crypto_and_trading). Multiple = OR. | |
| media_type | No | media type filter: image or video | |
| sort_order | No | sort direction: asc or desc | |
| domain_zone | No | landing-domain zones/TLDs without the dot (e.g. com, shop, online). Mirrors the /creo domain-zone filter. | |
| hub_domains | No | exact destination hostnames to include (the web Hub filter). Use domains as a compatibility alias if needed; values are normalized hostnames, maximum 50. | |
| media_types | No | media types to include (image, video) — the visible /creo media multi-select. Prefer this over the singular media_type. | |
| period_from | No | in-period window start (YYYY-MM-DD), independent from creative last activity; requires period_to; read pagination.period_axis to learn which date axis was used | |
| hub_category | No | filter to creatives whose destination is in this TOP-LEVEL hub category. Valid slugs: social, app_stores, shortlinks, amazon, ecommerce, popular, platforms (NOT 'social_media' — the slug is 'social'). For a specific messaging channel use channel - hub_category does NOT accept whatsapp/telegram sub-slugs. | |
| last_seen_to | No | date range end (YYYY-MM-DD): only creatives whose LAST recorded activity is on or before this date. Combine with last_seen_from for a closed activity window. | |
| webmaster_id | No | only creatives used by this webmaster id (from search_webmasters) | |
| advertiser_id | No | only creatives run by this advertiser id (from an ad's advertiser_id in search_ads, or get_trends dimension=advertisers) | |
| max_countries | No | only creatives shown in at most this many countries | |
| min_geo_share | No | D1 geo-relevance FILTER (0-1): keep only creatives where 'country' is at least this share of the creative's ads — i.e. that geo is the creative's dominant / #1 country (e.g. 0.5 = the country is >=50% of its ads). Requires country. Combine with sort_by=relevance to rank by ad-volume IN that country. | |
| period_metric | No | derived in-period metric: ad_debuts counts ads in the inclusive period_from/period_to window on the backend-reported date axis; pagination.period_axis explains the result (first_seen = first discovery by SpyTrend, facebook_start_date = Facebook launch after the verified cutover) | |
| advertiser_ids | No | multi-select advertiser filter: creatives run by ANY of these advertiser UUIDs (OR). Each returned card then carries slice.ads_in_slice / slice.active_in_slice = the EXACT summed ad counts of exactly these advertisers inside the creative's family (additive — an ad belongs to one advertiser). Supersedes advertiser_id when both are sent. | |
| ai_subcategory | No | AI subcategory slugs to filter by. Multiple = OR; combine with categories when needed. | |
| last_seen_from | No | date range start (YYYY-MM-DD): only creatives whose LAST recorded activity/fixation is on or after this date. This is the ONLY date filter; it is not an interval-overlap or first-seen filter. | |
| exclude_countries | No | ISO country codes to EXCLUDE: a creative targeting ANY of them is hidden. Mirrors the /creo geo EXCLUDE column. | |
| active_ads_today_from | No | minimum number of ads active today for the creative family; 0 or omitted disables this filter |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | |
| meta | No | |
| pagination | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes far beyond the readOnly/destructive annotations by disclosing quota semantics (1 token per delivered result, short-page auto-refund), metadata-only behavior, separate media billing, the period_axis contract, empty-page behavior, and rejection of invalid periods before upstream work. These are behavioral details not inferable from annotations alone.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but densely packed with operationally important information, and it is front-loaded with purpose then quota then contracts. However, it repeats the period_axis contract and last_seen/period definitions in multiple places, so it is not maximally concise.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex 33-parameter tool with no required parameters and an output schema present, the description covers pricing, pagination, period semantics, aliases, sort behavior, slice semantics, error handling, and sibling routing. Nothing essential for correct invocation appears missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, so the baseline is 3, but the description adds substantial meaning: last_seen vs period date semantics, date_from/date_to aliases, period_axis interpretation, slice.ads_in_slice ordering, min_geo_share's country requirement, and the closed-period auto-enablement of ads_in_period. This clearly exceeds the baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb-resource pair: 'Browse and filter unique creative families and return metadata only.' It also explicitly distinguishes the tool from search_ads by stating that creatives have no free-text search and that search_ads is for discovery, which helps an agent tell siblings apart.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit routing guidance: use search_ads for free-text discovery, get_media for downloading media, and add_to_favorites for saving. It also advises requesting small limits and narrow filters instead of wide pagination, making the intended usage pattern clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_hubsSearch hubs (destinations)ARead-onlyInspect
Explore HUBS — where ads send traffic, grouped by destination (Social Media: Facebook/Instagram/Telegram/WhatsApp; App Stores; Amazon; E-commerce; Popular; Platforms). Call WITHOUT hub to get the catalog (the list of hub slugs + labels) — that is FREE. Call WITH hub set to a slug to drill into that hub's destination profiles, filtered by query (free-text profile name/id search), ai_category, min_total/min_active/min_sticky ads, date_from/date_to, window (7d/30d/90d; NOTE: lifetime counters on the default snapshot path — window is a no-op there, prefer date_from/date_to or sort=active_desc for recency), country (ISO alpha-2 — keep only profiles with ads in that geo) and exclude_cloaking (drop multi-geo redirect/cloaking domains), ordered by sort (ad_count_desc default = LIFETIME volume, ad_count_asc, last_seen_desc, sticky_desc, sticky_asc, active_desc/active_asc = CURRENTLY-active 'hot now', or relevance = ad volume IN the selected country — needs country). For a per-geo ranking of real storefronts (e.g. top e-commerce in BR), combine country + sort=relevance + exclude_cloaking. The catalog call (no hub) is FREE; profile rows cost 1 token each. FREE PLAN COUNTS: when the plan's archive window narrows a request, pagination.total_status is "restricted" and meta.plan_window_cutoff names the boundary — the count then describes the window actually searched, NOT the one requested, so a zero means "nothing in the archive window" and never "nothing exists". QUOTA: 1 token per DELIVERED result from your plan balance (free starter: 500 tokens lifetime; paid plans: 40,000/month; short pages auto-refund — you pay only for results you receive). Default page is 20 results = 20 tokens; pass limit (1–200) to size it. get_usage is free. Autonomous agents must be linked to a spytrend account to access data — an unlinked agent gets an actionable connect-your-account refusal (create agent credentials at spytrend.com/settings?tab=ai, or a human claims it by client_id), NOT a server error. Calls are rate-limited per authenticated user (deployment defaults: 60/minute and 1000/hour); a rate-limit refusal is an MCP tool error with structured error.code=rate_limit_exceeded, scope, window and retry_after_seconds. CONCURRENCY: heavy analytical calls (get_trends, search_ads, search_creatives) are admitted ONE AT A TIME per account — fanning out 5-10 of them in parallel does not go faster, it returns admission refusals for all but one. Issue heavy calls sequentially; light lookups (get_ad, get_advertiser, get_usage) are not gated.
| Name | Required | Description | Default |
|---|---|---|---|
| hub | No | hub slug to drill into. IT IS THE NESTED ONE: the catalog (returned when hub is omitted) is TWO levels — each row is a CATEGORY with its own slug (social_media, app_stores, …) and a hubs[] array whose entries carry the drill-in slugs (facebook, instagram, google_play, …). Pass hubs[].slug, NOT the category slug — a category slug is rejected. Leave empty to list the hub catalog (free). | |
| sort | No | profiles only: ad_count_desc (default, LIFETIME volume), ad_count_asc, last_seen_desc, sticky_desc, sticky_asc, active_desc/active_asc (CURRENTLY-active ads — 'what's hot now', avoids the dead-domain top), or relevance (rank by ad volume in the selected country — REQUIRES country) | |
| limit | No | profiles only: max results (default 20; each delivered profile costs 1 token). The catalog call (no hub) is free. | |
| query | No | profiles only: free-text search by profile id/name within the hub (the same box as the profile search on the /hubs page). Requires hub. | |
| window | No | profiles only: lookback window 7d, 30d (default) or 90d. NOTE: on the default snapshot read path counters are LIFETIME and window is a no-op — for recency use date_from/date_to (profile activity dates) or sort=active_desc instead | |
| country | No | profiles only: ISO-3166 alpha-2 country code (e.g. US, BR). Keeps only profiles with ads in that country and enables sort=relevance (rank by ad volume IN that country) — turns the global lifetime top into a real per-geo ranking. | |
| date_to | No | profiles only: active on or before this date (YYYY-MM-DD) | |
| date_from | No | profiles only: active on or after this date (YYYY-MM-DD) | |
| min_total | No | profiles only: minimum total ads | |
| min_active | No | profiles only: minimum active ads | |
| min_sticky | No | profiles only: minimum sticky (long-running) ads | |
| ai_category | No | profiles only: AI category slug filter | |
| exclude_cloaking | No | profiles only: drop multi-geo redirect / cloaking domains (a real storefront targets a few countries; a cloaking redirect runs in 90+). Use with country+relevance to surface genuine storefronts instead of infrastructure domains. |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | |
| meta | No | |
| pagination | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint/openWorldHint/destructiveHint; the description adds significant behavioral context: token costs, restricted archive counts, window no-op behavior, unlinked-agent refusal handling, rate-limit error codes, and concurrency admission limits. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose and call patterns, but it becomes a long rambling paragraph with repeated FREE/token reminders and extensive quota/rate-limit/concurrency details that could be summarized or bulleted. It is informative but not concise.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the 13 parameters and already-rich schema, the description covers everything an agent needs: invocation modes, filter semantics, sort behavior, cost model, auth requirements, rate limits, error shapes, and concurrency guidance. The output schema being present means return-value details are not required here.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds value beyond the schema by explaining the token cost per delivered profile, the 1–200 limit range, the semantic difference between lifetime and currently-active sorts, and how to combine country, relevance, and exclude_cloaking to get meaningful per-geo rankings.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states that search_hubs explores destination hubs where ads send traffic, and precisely distinguishes the two modes: without hub it returns the catalog, with hub it drills into destination profiles. This makes the tool's scope unambiguous relative to sibling search tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly explains when to call without hub vs with hub, and gives a concrete recipe for per-geo storefront rankings (country + relevance + exclude_cloaking). However, it does not explicitly contrast search_hubs with sibling search_* tools or state when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_shopsSearch domains and their trafficARead-onlyInspect
Search domain-level traffic surfaces with commerce and advertising intelligence. A row is a DOMAIN/SUBDOMAIN surface, not necessarily a company or product. visits is a MONTHLY estimate for traffic_as_of (not live traffic); fetched_at is ingestion time. Judge every estimate using traffic_quality, traffic_confidence, traffic_is_small, traffic_is_data_from_ga, traffic_data_status and history. traffic_paid_share is display + paid-search + paid-social share; trust it only when traffic_sources_available=true. visits_growth_pct is latest-vs-previous month PERCENT; min_growth and sort_by=growth only return growth_qualified rows (current cohort, contiguous 3-month window, previous month >=10k, non-small estimate, confidence >=0.60). category is an estimated SITE category and may be missing/wrong; ai_category is SpyTrend's AD category and is preferred for ad-market discovery. created_from/to filter domain_created: best available registration date of the REGISTRABLE ROOT, not product/subdomain launch or SpyTrend first-seen. The ClickHouse row does not retain whether that date came from the preferred registry lookup or the legacy fallback. traffic_start_max compares the current estimate with the MAXIMUM of both preceding complete months and excludes stale/low-quality windows; combine with traffic_end_min. ads_monthly counts ads first observed by SpyTrend (first_parsed_date), and each point has is_complete=false for the open current month. fb_signal is correlation_only, not causal attribution; honest statuses are correlated_growth, ad_growth_ahead, ad_growth_flat_traffic, ad_growth_declining_traffic, traffic_growth_without_fb_growth, stable_or_mixed and insufficient_signal. sort_by=relevance REQUIRES ai_category and is rejected otherwise. Sorts: visits/revenue/growth/ads/rank/backlinks/ai_traffic/products/fb_score/per_ad/relevance. a separate legacy snapshot is kept alongside; never substitute its similarly named traffic fields for the primary visits/traffic_as_of contract. Returns total, has_more and offset. Use get_shop for the full source payload. Saving shops is free. FREE PLAN COUNTS: when the plan's archive window narrows a request, pagination.total_status is "restricted" and meta.plan_window_cutoff names the boundary — the count then describes the window actually searched, NOT the one requested, so a zero means "nothing in the archive window" and never "nothing exists". QUOTA: 1 token per DELIVERED result from your plan balance (free starter: 500 tokens lifetime; paid plans: 40,000/month; short pages auto-refund — you pay only for results you receive). Default page is 20 results = 20 tokens; pass limit (1–200) to size it. get_usage is free. Autonomous agents must be linked to a spytrend account to access data — an unlinked agent gets an actionable connect-your-account refusal (create agent credentials at spytrend.com/settings?tab=ai, or a human claims it by client_id), NOT a server error. Calls are rate-limited per authenticated user (deployment defaults: 60/minute and 1000/hour); a rate-limit refusal is an MCP tool error with structured error.code=rate_limit_exceeded, scope, window and retry_after_seconds. CONCURRENCY: heavy analytical calls (get_trends, search_ads, search_creatives) are admitted ONE AT A TIME per account — fanning out 5-10 of them in parallel does not go faster, it returns admission refusals for all but one. Issue heavy calls sequentially; light lookups (get_ad, get_advertiser, get_usage) are not gated.
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | substring search on the normalized domain; URLs are normalized to their host | |
| limit | No | max results (default 20; each delivered result costs 1 token). | |
| saved | No | saved scope selector. Use saved=all to restrict results to shops saved in ANY favourites folder. | |
| offset | No | pagination offset (0-based) | |
| pixels | No | comma-separated pixel identifiers to filter by | |
| country | No | ISO-2 country code — keep only shops with traffic in this country | |
| has_ads | No | true = only shops with at least one ad in the database | |
| min_ads | No | minimum number of ads | |
| socials | No | comma-separated social-media handle filters | |
| sort_by | No | order by: visits (default), revenue, growth, ads, rank, backlinks, ai_traffic, products, fb_score, per_ad or relevance; relevance REQUIRES ai_category and is rejected without it | |
| category | No | estimated site-category filter; may be missing or misclassified, so prefer ai_category for ad-market discovery | |
| platform | No | commerce platform the site runs on: Shopify, WooCommerce, WordPress, Wix, Squarespace, Magento, PrestaShop or BigCommerce | |
| fb_status | No | observed ads/traffic relationship: correlated_growth, ad_growth_ahead, ad_growth_flat_traffic, ad_growth_declining_traffic, traffic_growth_without_fb_growth, stable_or_mixed or insufficient_signal; correlation only | |
| folder_id | No | restrict results to shops saved in this favourites folder UUID. Mirrors the /shops folder view. | |
| created_to | No | registrable-root registration date on/before YYYY-MM-DD; NOT product/subdomain launch; per-row RDAP/WHOIS-vs-legacy provenance is unavailable | |
| max_bounce | No | maximum bounce rate in PERCENT (0-100) — max_bounce=30 keeps only sticky sites, the reason this control exists. The server converts it to the fraction the column stores. | |
| max_growth | No | upper end of the traffic-growth range (percent). The /shops panel has BOTH ends; pair it with min_growth for a band such as 10..50. | |
| max_per_ad | No | maximum visits-per-ad efficiency | |
| max_visits | No | maximum monthly visits | |
| min_bounce | No | minimum bounce rate in PERCENT (0-100), matching the visible /shops bounce slider — min_bounce=70 keeps only high-bounce sites. The server converts it to the fraction the column stores. | |
| min_growth | No | minimum latest-vs-previous monthly visits growth PERCENT; only current-cohort growth_qualified rows pass (contiguous 3 months, previous >=10k, non-small, confidence >=0.60) | |
| min_per_ad | No | minimum visits-per-ad efficiency (traffic / active ads) | |
| min_visits | No | minimum monthly visits | |
| sort_order | No | sort direction: asc or desc (default desc) | |
| ai_category | No | AI category slug the shop's ads belong to (e.g. ecommerce_and_retail) | |
| min_revenue | No | minimum estimated monthly revenue (USD) | |
| shopify_app | No | Shopify app slug filter (shops using this app) | |
| created_from | No | registrable-root registration date on/after YYYY-MM-DD; NOT product/subdomain launch or SpyTrend first-seen; per-row RDAP/WHOIS-vs-legacy provenance is unavailable | |
| has_products | No | true = only shops with a product catalog in our database | |
| min_products | No | minimum number of catalogued products | |
| shopify_plan | No | Shopify plan name filter (e.g. Basic, Shopify, Advanced) | |
| min_backlinks | No | minimum total backlinks | |
| shopify_theme | No | Shopify theme slug filter | |
| has_ai_traffic | No | true = only shops receiving AI-referred traffic (ai_traffic_share > 0) | |
| has_trustpilot | No | true = only shops with Trustpilot reviews | |
| country_exclude | No | ISO-2 country code to exclude from results | |
| traffic_end_min | No | latest monthly estimate at traffic_as_of is at least this value; combine with traffic_start_max | |
| traffic_start_max | No | maximum allowed baseline traffic, where baseline=max(two complete months before traffic_as_of); stale, low-quality, incomplete and non-contiguous windows are excluded; combine with traffic_end_min | |
| min_trustpilot_rating | No | minimum Trustpilot rating (0-5) | |
| exclude_infrastructure | No | drop rows that are transit rather than an offer — link shorteners / link-in-bio, app stores and marketplaces, social networks and messengers, ad servers. Every row also carries surface_role (destination | redirect | store | social | adserver; absent for an ordinary merchant site) so you can filter yourself instead. NOTE: rows are dropped after the page is fetched, so an excluded page can return fewer than limit rows — page on has_more/offset, not on row count. |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | |
| meta | No | |
| total | Yes | |
| offset | Yes | |
| has_more | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnlyHint=true, openWorldHint=true, destructiveHint=false), so the bar is lower — yet the description goes far beyond: visits is a MONTHLY estimate, fetched_at is ingestion time, quality fields must gate judgment, paid_share trusts only when traffic_sources_available=true, growth rows require a qualifying cohort, category may be wrong, provenance of registration dates is not retained (ClickHouse row caveat), free-plan archive windows change what total reports, rate-limit refusal surfaces as structured error.code=rate_limit_exceeded, and concurrency gates heavy calls. Failure modes and data caveats are disclosed in unusual depth.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long, but every sentence carries a substantive caveat or behavior — no obvious padding. It front-loads the core row/visits contract before quota, rate-limit, and concurrency details. The length is justified by 40 params, a token-cost model, and complex data semantics; it loses a point only because a wall of unheadered prose is harder for an agent to scan selectively than structured sections.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with this complexity — 40 loosely-coupled filters, quota accounting, plan-window restrictions, rate limits, concurrency, and data-quality caveats — the description covers nearly everything an agent needs: return shape (total/has_more/offset), pagination restriction semantics, token cost per delivered row including auto-refund, the unlinked-agent refusal mode, and the exact rate-limit error contract. An output schema exists, so return-value details are appropriately left to it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, giving a baseline of 3. The description adds interpretave semantics beyond the schema: it ties visits_growth_pct to the growth_qualified cohort conditions, ranks ai_category over category for ad-market discovery, and explains traffic_start_max's baseline of both preceding complete months with stale windows excluded. It doesn't fully compensate for the sheer number of params on its own, but genuinely elevates understanding of the key filter semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource — 'Search domain-level traffic surfaces with commerce and advertising intelligence' — and immediately defines the row unit (DOMAIN/SUBDOMAIN) to prevent over-interpretation. This distinguishes it from siblings like search_ads, search_creatives, and search_advertisers, whose resources are different entities, and it explicitly points to get_shop for the full source payload.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit alternative guidance is present: 'Use get_shop for the full source payload.' The concurrency paragraph also implies search_shops is not a gated heavy call (unlike get_trends, search_ads, search_creatives), and the quota/plan section gives clear cost-based context for calling it. It stops short of an explicit when-not-to-use list against the other search_* siblings, so a 4 rather than a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_webmastersSearch webmastersARead-onlyInspect
Search webmasters (affiliates) — a webmaster is a whole ad NETWORK (landing domains, pixels, pages and geos grouped as one). Copy workflow: search by domain, take the canonical UUID, then search_creatives/search_ads with webmaster_id. Finish with the most reused material: get_webmaster.top_creatives or search_creatives sort_by=total_ads; total_ads is how many ads reuse the same material over its lifetime, not a weekly debut count. Returns total/active counts, top countries and geo counts. Find by name or pixel_id/domain/page_id/resolved_ip. Filter by category, AI category/subcategory, countries, languages, platforms, format, current status, dates and count ranges. For a geo ranking combine countries + min_geo_share + sort_by=relevance. Save returned UUIDs with add_to_favorites. Exact pixel_id/domain/page_id/resolved_ip lookups use the webmaster's CURRENT canonical identifier membership; historical identifiers removed from a webmaster do not count as matches. Exact page_id results report fanpages_status as available, partial, unavailable or not_applicable. When available, the selected page is included in fanpages[] with life_status from the same page-lifetime source as /advertisers (alive/deleted/banned, or unknown when lifetime coverage has no row); when unavailable, an omitted fanpages[] is not evidence that the page is alive. FREE PLAN COUNTS: when the plan's archive window narrows a request, pagination.total_status is "restricted" and meta.plan_window_cutoff names the boundary — the count then describes the window actually searched, NOT the one requested, so a zero means "nothing in the archive window" and never "nothing exists". QUOTA: 1 token per DELIVERED result from your plan balance (free starter: 500 tokens lifetime; paid plans: 40,000/month; short pages auto-refund — you pay only for results you receive). Default page is 20 results = 20 tokens; pass limit (1–200) to size it. get_usage is free. Autonomous agents must be linked to a spytrend account to access data — an unlinked agent gets an actionable connect-your-account refusal (create agent credentials at spytrend.com/settings?tab=ai, or a human claims it by client_id), NOT a server error. Calls are rate-limited per authenticated user (deployment defaults: 60/minute and 1000/hour); a rate-limit refusal is an MCP tool error with structured error.code=rate_limit_exceeded, scope, window and retry_after_seconds. CONCURRENCY: heavy analytical calls (get_trends, search_ads, search_creatives) are admitted ONE AT A TIME per account — fanning out 5-10 of them in parallel does not go faster, it returns admission refusals for all but one. Issue heavy calls sequentially; light lookups (get_ad, get_advertiser, get_usage) are not gated. TIKTOK SOURCE: pass source="tiktok" to query the TIKTOK ad corpus instead of Meta. (The platforms filter does NOT do this — platforms are Meta publisher placements.) ⚠️ TIKTOK IS PAID PER ROW at a premium: every DELIVERED TikTok ad row costs 100 tokens and every TikTok webmaster row/card costs 100 tokens (Meta rows cost 1), deducted from the same token balance (free starter: 500 tokens total = 5 TikTok rows EVER; paid plans: 40,000/month = up to 400 TikTok rows). Request small limits and narrow filters; you are charged only for rows actually delivered (short pages auto-refund; if the balance covers only part of the request, that part is delivered — a 3-row request on a 150-token balance returns 1 row and charges 100). TikTok requires a Pro-or-higher plan: free/starter callers and unlinked agents get an actionable upgrade refusal, never data and never a charge. TikTok rows have their OWN shape, returned under tiktok_data (verified live 2026-07-30): id/external_id (TikTok ad id), advertiser{id,external_id,name} plus advertiser_id (TikTok BUSINESS id), start_date/end_date (the ad's delivery window) and first_seen_date/last_seen_date (SpyTrend indexing), days_active, is_active + status_today, ai_category/ai_subcategory, countries + targeting_geos + targeting_details (OS/age/gender/regions), audience_size, objective, sponsor (the 'paid for by' funder) and registry_location, landing_domain + link_url, body/title/call_to_action, creative_format, creative_id, creative_ad_count (how many ads reuse that creative), is_blurred/is_cloaked, and media[] with media_type + thumbnail_url on media-tt.spytrend.com. Media comes WITH the row — do NOT call get_media for TikTok ids (it serves Meta entities only). There are no TikTok engagement counters (plays/likes/comments/shares) on this surface. The TikTok feed serves the ARCHIVED TikTok corpus — exactly what the spytrend.com /ads TikTok tab shows — and pagination carries has_more/next_cursor plus total with total_status (exact unless ttads marks the count estimated). With source=tiktok rows are TikTok webmaster CLUSTERS (business-id rollup — the same entity the spytrend.com /webmaster TikTok tab shows; a different id space from ttads domain clusters). Honored filters: query (name/domain/cabinet legal names), domain, countries (+min_geo_share), ai_category (dominant category), min_total_ads, min_active_ads, date_from/date_to, sort_by=total_ads|active_ads|created_at|relevance, limit (default 10, server caps at 100 for TikTok). TikTok-only filter has_funder=true keeps only clusters with an EXTERNAL 'paid for by' sponsor. Meta-only lookups (pixel_id/page_id/resolved_ip, languages/platforms/creative_format, status_today, ai_subcategory, min_geo_share without countries) are REJECTED, not ignored. TikTok has no status-checker: active means 'shown within the last 30 days'.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | max results (default 20; each delivered result costs 1 token). | |
| query | No | free-text webmaster (affiliate) name to search for | |
| saved | No | saved scope selector. Use saved=all to restrict results to webmasters saved in ANY favourites folder. | |
| domain | No | point lookup: webmasters on this landing domain | |
| offset | No | pagination offset — skip the first N rows. The /webmasters ranking pages by offset (not a cursor), so this is how a caller reaches page two and beyond. Max 5000. | |
| source | No | webmaster corpus: meta (default — Facebook/Meta networks, 1 token per delivered row) or tiktok (TikTok business-id clusters — ⚠️ PAID: 100 tokens per DELIVERED row; Pro plan required; default limit drops to 10, server caps TikTok at 100) | |
| date_to | No | only webmasters active on or before this date (YYYY-MM-DD) | |
| page_id | No | exact current-ownership lookup by Facebook page id (a pasted facebook.com page link in any form is also accepted and resolved to the canonical page id); fanpages_status reports lifecycle coverage and available results include alive/deleted/banned/unknown lifecycle | |
| sort_by | No | order by: total_ads (default), active_ads or relevance (relevance ranks by ad-VOLUME in the selected countries — geo-relevance; needs countries) | |
| category | No | vertical filter: gambling or other | |
| pixel_id | No | point lookup: webmasters using this Facebook pixel id | |
| countries | No | ISO country codes the webmaster's ads ran in | |
| date_from | No | only webmasters active on or after this date (YYYY-MM-DD) | |
| folder_id | No | restrict results to webmasters saved in this favourites folder UUID. Mirrors the /webmasters folder view. | |
| languages | No | language codes filter | |
| platforms | No | platform names filter (e.g. facebook, instagram) | |
| has_funder | No | TikTok only (source=tiktok): keep only clusters with an EXTERNAL 'paid for by' sponsor (the funded-by filter). Rejected for the Meta corpus. | |
| sort_order | No | sort direction: asc or desc (default desc) | |
| ai_category | No | AI category slug filter (e.g. gambling_and_betting) | |
| resolved_ip | No | point lookup: webmasters whose domain resolves to this IP | |
| status_today | No | current status: active, inactive or vanished | |
| min_geo_share | No | GEO-RELEVANCE gate (0-1, the relevant-only toggle): keep only webmasters where the 'countries' you pass are at least this fraction of their ads — i.e. that geo is their DOMINANT geo (e.g. 0.5 = country >=50% of their ads). Without it, countries is a mere 'present-in' match and a webmaster with 0.5% of ads in BR ranks as a 'BR webmaster'. Requires countries. Each returned row's geo field carries the per-country ad-count breakdown so you can read the real exposure. | |
| min_total_ads | No | only webmasters with at least this many total ads | |
| ai_subcategory | No | AI subcategory slug filter | |
| min_active_ads | No | only webmasters with at least this many ACTIVE ads | |
| creative_format | No | single creative format filter (legacy form). Prefer creative_formats[] — the visible /webmasters control is a multi-select. | |
| creative_formats | No | creative formats to include (video, carousel, single, dynamic). Mirrors the visible /webmasters format multi-select. | |
| exclude_countries | No | ISO country codes to EXCLUDE — drops webmasters whose ads run in ANY of them. Mirrors the /webmasters geo EXCLUDE column; combine with countries to keep one market while removing noise markets. |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | |
| meta | No | |
| source | No | |
| pagination | Yes | |
| tiktok_data | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark the tool read-only and non-destructive, and the description goes far beyond that: it discloses token quotas and per-row costs, free-plan archive-window restrictions, per-user rate limits, one-at-a-time concurrency gating for heavy calls, current-membership matching semantics for exact lookups, and TikTok-specific row/charge behavior. No contradiction with the annotations exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-organized with clear section markers (FREE PLAN COUNTS, QUOTA, CONCURRENCY, TIKTOK) and front-loads the core search workflow before edge cases. It is very long, though, and parts of the TikTok row-shape enumeration overlap what the output schema should already provide, so not every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 28-parameter tool with quota, rate-limit, concurrency, and dual-corpus behavior, the description is exceptionally complete: it covers cost per delivered row, archive-window count semantics, error shapes, upgrade refusals, TikTok-only filters, and how to paginate. An agent has everything needed to invoke this tool correctly without guessing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents every parameter, but the description adds meaning the schema cannot convey: min_geo_share is explained as a geo-relevance dominance gate, sort_by=relevance is tied to country ad-volume, source is priced and plan-gated, and boolean/count filters get interpretation warnings. This materially helps an agent choose correct parameter values.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Search webmasters (affiliates)', then defines a webmaster as an ad NETWORK. It also embeds the tool in an explicit downstream workflow ('search by domain, take the canonical UUID, then search_creatives/search_ads with webmaster_id'), which clearly distinguishes this search tool from sibling search/retrieval tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives concrete when-to-use guidance: search first, capture the canonical UUID, then pass webmaster_id to search_creatives/search_ads or get_webmaster.top_creatives. It also states when alternatives apply, distinguishes Meta vs TikTok corpus selection via source, and warns which filters are rejected for TikTok — explicit usage routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
1 tool update
- Changed
search_ads2 fields changed- changed
Input schema / properties / max_body_chars / descriptionPrevious value: -"only ads whose AD TEXT is at most this many characters long (UTF-8 characters, not bytes). Combine with min_body_chars for a closed length range, e.g. 100..500"New value: +"only ads whose AD TEXT is at most this many characters long (UTF-8 characters, not bytes). Ads with no text are NOT returned as length 0 — the length filters cover only ads with known non-empty text. Combine with min_body_chars for a closed length range, e.g. 100..500" - changed
Input schema / properties / min_body_chars / descriptionPrevious value: -"only ads whose AD TEXT is at least this many characters long (UTF-8 characters, not bytes). Pairs with the body_chars field returned on every row; the ad text itself is served by get_ad, not by this list"New value: +"only ads whose AD TEXT is at least this many characters long (UTF-8 characters, not bytes). Both length filters match only ads whose text length is KNOWN and non-zero: ads with no text at all are outside the length axis entirely and are returned by neither direction. Pairs with the body_chars field returned on every row; the ad text itself is served by get_ad, not by this list"
21 tool updates
- First observed
add_to_favorites - First observed
find_similar_ads - First observed
find_similar_creatives - First observed
find_similar_webmasters - First observed
get_ad - First observed
get_ads_analytics - First observed
get_advertiser - First observed
get_creative - First observed
get_media - First observed
get_shop - First observed
get_trends - First observed
get_usage - First observed
get_webmaster - First observed
get_webmaster_similarity_facets - First observed
list_favorites - First observed
search_ads - First observed
search_advertisers - First observed
search_creatives - First observed
search_hubs - First observed
search_shops - First observed
search_webmasters
Frequently Asked Questions
Claiming proves that you control a remote MCP connector. It does not move, proxy, or interrupt the server.
Open the connector listing, choose Claim ownership, and sign in to Glama.
Complete one verification method:
GitHub identity — fastest for official registry listings. For a namespace such as
io.github.alice/server, link the matching GitHub user, then choose Claim with GitHub. An organization namespace such asio.github.acme/serveralso needs that organization to have installed the Glama AI GitHub App and approved its permissions, because GitHub discloses organization membership only to apps it has installed. Use HTTP or DNS when it has not.HTTP challenge — works when you can deploy a public file. Generate a token, publish the exact JSON Glama shows at
/.well-known/glama.jsonon the same origin as the connector, then choose Check HTTP challenge.DNS challenge — works when you control DNS but cannot change the server. Generate a token, create the exact TXT record Glama shows, wait for it to propagate, then choose Check DNS challenge.
After verification, Glama sends a confirmation email and gives you access to listing details, thumbnails, health checks, and analytics. Keep the HTTP file or DNS record in place: Glama periodically checks it and ownership remains verified while the token is discoverable.
The HTTP ownership file has this structure:
{
"$schema": "https://glama.ai/mcp/schemas/connector.json",
"claim": "glama_claim_..."
}Claim tokens are opaque, stable, and bound to the signed-in Glama account. They contain no email address or other personal information. If Glama can no longer discover a verified HTTP or DNS token, it starts a seven-day grace period before removing claim-based access. Restore the same token during that period to keep ownership verified. Never publish an email address, Glama session token, GitHub token, or connector credential as ownership proof.
If verification fails, confirm that you copied the current token exactly. The HTTP file must be public, return valid JSON with a successful HTTP response, and stay on the connector's origin. DNS changes may need more time to propagate. A claim cannot transfer to a different origin or hostname: if the connector target changes, Glama starts the grace period and the new target must be claimed separately after the previous claim is released.
For a connector linked to the official MCP Registry, registry updates continue to replace its name, description, and URL by default. After claiming, open Manage connector and enable Use Glama listing details as the source of truth if edits made on Glama should be preserved. Categories and thumbnails are always managed on Glama; registry linkage and technical connection settings continue to sync.
Control your server's listing on Glama, including description and metadata
Access analytics and receive server usage reports
Get monitoring and health status updates for your server
Feature your server to boost visibility and reach more users
To improve your MCP server's ranking:
Claim ownership of the server listing
Complete the server profile with an accurate description and thumbnail
Provide a test profile so Glama can connect to and evaluate the server
Keep tool definitions clear and complete to earn a high Tool Definition Quality Score (TDQS)
Route real usage through the Glama Gateway; more recorded successful server uses also improve the ranking
For users:
Full audit trail – every tool call is logged with inputs and outputs for compliance and debugging
Granular tool control – enable or disable individual tools per connector to limit what your AI agents can do
Centralized credential management – store and rotate API keys and OAuth tokens in one place
Change alerts – get notified when a connector changes its schema, adds or removes tools, or updates tool definitions, so nothing breaks silently
For server owners:
Proven adoption – public usage metrics on your listing show real-world traction and build trust with prospective users
Tool-level analytics – see which tools are being used most, helping you prioritize development and documentation
Direct user feedback – users can report issues and suggest improvements through the listing, giving you a channel you would not have otherwise
The connector status is unhealthy when Glama is unable to successfully connect to the server. This can happen for several reasons:
The server is experiencing an outage
The URL of the server is wrong
Credentials required to access the server are missing or invalid
If you are the owner of this MCP connector and would like to make modifications to the listing, including providing test credentials for accessing the server, please contact support@glama.ai.
Discussions
No comments yet. Be the first to start the discussion!
Related MCP Connectors
Search Meta, Google Ads, LinkedIn, and TikTok ad libraries plus creative analysis via MCP.
Create, launch & analyze ad campaigns, spy on competitors - Google, Meta, TikTok, LinkedIn & more.
TikTok Ads automation with AI: analyze performance, manage campaigns, optimize creatives.
Facebook / Meta Ads automation with AI: analyze performance, test creatives, optimize spend.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceManage ad campaigns across Meta, Google, and TikTok, create campaigns, analyze performance, spy on competitors, and generate AI creatives.12MIT
- FlicenseNot gradedqualityDmaintenanceSearches and analyzes competitor ads and content across Meta, Google, Instagram, TikTok, and YouTube with AI-powered creative analysis and cross-platform brand discovery.1-
- AlicenseNot gradedqualityCmaintenanceEnables competitive intelligence, market analysis, and advertising insights by providing 15+ advanced tools to search, analyze, monitor, and predict Facebook ad performance.227MIT
- AlicenseNot gradedqualityDmaintenanceProvides access to Meta Marketing API for comprehensive ad analytics and AI-powered video creative analysis.1,6591MIT
Glama MCP Gateway
Add one secure layer between your agents and this server.
TDQS
Each tool pairs a distinct action verb with a distinct entity type: search_* queries databases, get_* fetches single-entity cards, find_similar_* performs similarity lookups, and add_to_favorites/list_favorites handle saved items. The three find_similar tools explicitly cross-reference their differences (visual vs entity overlap vs URL-parameter), and get_adversus search_advertisers are clearly single-card vs typeahead.
All 21 tools follow a consistent verb_noun snake_case pattern: six search_* tools, ten get_* tools, three find_similar_* tools, plus add_to_favorites and list_favorites. No camelCase, no mixed verb styles, and the similarity trio shares an identical prefix, making the set highly predictable.
At 21 tools this is above the typical 3-15 sweet spot, but the server spans six entity domains (ads, creatives, advertisers, webmasters, hubs, shops) plus cross-cutting media delivery, favorites, trends, analytics, similarity and usage. Nearly every tool maps to a distinct workflow step, so the set feels dense rather than bloted.
The core lifecycle is fully covered: search every entity type, fetch detail cards, download media, save to favorites, list favorites back, plus trends, analytics, similarity and TikTok surfaces. The main gaps are a remove_from_favorites operation and a standalone hub detail card, neither of which blocks the primary ad-intelligence workflows.