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
TDQS
Scored across 21 tools
Tools are cleanly separated by resource and action: search_* browses/list metadata, get_* fetches full entity detail, find_similar_* handles relationship discovery, and get_media/add_to_favorites/list_favorites cover delivery and saved items. Overlaps are explicitly disambiguated in descriptions (e.g., find_similar_ads vs find_similar_creatives, search_ads vs get_ad).
All names use consistent snake_case verb_noun patterns across predictable families: search_*, get_*, find_similar_*, add_to_*/list_*, and get_usage. Minor length variance does not break the convention.
21 tools are slightly above the typical 3-15 band but justified by the breadth of entities (ads, creatives, webmasters, advertisers, shops, hubs) and supporting operations (analytics, trends, favorites, usage). No redundant tool clearly could be dropped.
Read/query coverage is strong across ads, creatives, webmasters, advertisers, shops, hubs, trends, and media. Minor gap: favorites support add and list but no removal/delete or update operation, which agents may need for lifecycle management.
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 (including multilang) costs 1 token and saving a creative costs 10 tokens; saving does not include another ad media opening; 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?
Annotations already declare idempotentHint=true and destructiveHint=false, so the description's idempotency statement aligns but adds value by explaining already_saved behavior and billing details. It also discloses that saving incurs costs for ads and creatives but is free for others, which is crucial for agent decisions. However, it does not detail output schema, but since idempotency and cost are covered, a 4 is appropriate.
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 and well-structured, with key information front-loaded (what it saves and what entity types are supported), followed by specific requirements and billing. Every sentence serves a purpose, and the use of bold for critical notes (MUST, OPTIONAL, BILLING) enhances scannability without 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?
Despite having an output schema, the description still explains return behavior (already_saved) and billing, which are not fully captured in the schema. It covers all necessary details for correct invocation: entity type mapping, source of IDs, special hub requirement, folder naming guidance, and cost implications. This is complete for an agent to use safely.
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 description largely repeats parameter info. It adds minimal extra meaning, like clarifying that 'ids' must come from search results and that 'hub' is only required for hub entity type, but this is already in the schema descriptions. The billing details per entity_type add some value but not beyond what the schema implies.
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 the tool saves a selection of entities to favorites folders, naming the valid entity types and how IDs are sourced. It distinguishes itself from sibling search and get tools by focusing on the save action, and the detailed breakdown of entity types and required parameters makes the purpose 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 explicitly explains when to use this tool (to save entities for later review) and provides guidance on prerequisite sources (search_* tools), the required hub parameter for hub entities, and folder naming preferences. It also indicates when it is not applicable (e.g., no duplication) and implies alternatives like list_favorites for reviewing saved items.
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?
Beyond the readOnlyHint annotation, it reveals the source ad exclusion, the pagination.total semantics, the existence of source_excluded and total_semantics fields, and the quota cost per row. This added context explains edge cases an agent might otherwise misinterpret.
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 and information-rich, with each sentence serving a purpose (purpose, distinction, output semantics, quota). It is slightly verbose but well-structured and front-loaded with the core action.
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 (pagination, output schema, annotations), the description covers behavioral quirks, quota, and alternative tools. It fully compensates for anything not obvious from the schema or annotations, making it complete for an agent.
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% and the description adds little beyond the schema for parameters. It does reinforce the meaning of basis, but the schema already describes all parameters adequately; no new parameter-level insight is provided.
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 the verb (find) and resource (ads related to one source ad) with an explicit overlap basis (fanpage or domain). It distinguishes itself from visual similarity tools by directly naming find_similar_creatives, making the purpose 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?
It explicitly states when to use (entity overlap on fanpage/domain) and when not to (not visual similarity) along with the alternative tool. This gives clear selection guidance relative to siblings without any ambiguity.
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?
While readOnlyHint and destructiveHint already indicate a safe read operation, the description still adds meaningful behavior beyond annotations: threshold_status vs production default, result_status=bounded_native_cap meaning the upstream faces at most 20 candidates and has_more is conservative, the absence of media URLs, and QUOTA per delivered row. This is valuable operational context for an agent.
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 opens with the core behavior, then adds disambiguation, boundary behavior, output-usage guidance, and quota in a compact set of sentences. Each sentence carries information an agent needs; there is no filler or omitted critical nuance.
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 lightweight read-oriented tool with a strong schema, an output schema present, and helpful annotations, this description fills in exactly the gaps an agent cannot get from structured data: quota cost, upstream candidate cap, default threshold behavior, and which parameter to use. No critical external context 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 has 100% description coverage for the three params, so the baseline is 3. The description improves on that by explaining threshold semantics more concretely and by documenting that result cap is 20 and conservative, which directly makes limit and threshold more useful. It does not belabor the already-documented parameter types/defaults.
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 action and resource: 'Find creatives that look the same as one creative' using a named mechanism, the production similarity index. It also adds an explicit disambiguation—'This is visual similarity, not exact creative membership'—which helps an agent distinguish this tool from exact-match or metadata-based 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?
The description clearly establishes the intended context: given one creative, retrieve visually similar creatives. It also gives follow-up guidance ('Returns metadata without media URLs; use get_media for selected ids') and clarifies semantics around threshold and result caps. It stops short of naming sibling tools like search_creatives or find_similar_ads for explicit when-not-to-use conditions, so it is not quite 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_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 annotations (readOnlyHint, openWorldHint, destructiveHint), the description discloses deterministic ordering, a bounded result cap of 5000, status fields (result_status/result_cap/total_status) for completeness, a quota of 1 token per row, and a dependency on a prior facets call. This is rich behavioral context that the annotations alone do not provide.
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 well-organized, leading with the core purpose, then the workflow, then behavioral details, and finally quota. Every sentence adds value with no filler. The structure supports quick comprehension by an agent.
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 5 parameters, 2 required, and an output schema present, the description covers the necessary invocation steps, behavioral constraints, quota, and dependency on another tool. The output schema handles return structure, so nothing critical 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?
With 100% schema description coverage, the baseline is 3. The description adds meaning by explaining that param_key comes from the facets call, webmaster_id comes from search_webmasters, and values is an optional subset of normalized source values. It also clarifies the role of similarity_basis and rarity_count as output concepts, which helps the agent understand the data flow beyond 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 clearly states the specific function: finding webmasters that share normalized URL-parameter values with a source webmaster. It explicitly names the mechanism (url_parameter_overlap) and the required dependency on get_webmaster_similarity_facets, distinguishing it from sibling tools like find_similar_ads or search_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 provides a clear workflow: first call get_webmaster_similarity_facets, then pass one returned param_key, optionally narrowing to selected values. It gives explicit prerequisites and ordering but does not explicitly compare against alternative tools or state when not to use it, which would make it a 5.
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, 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: 50 tokens for a multilang-upload ad opening; 1 token for an ordinary ad. The price includes the available media returned with this card. Each new opening call is charged separately; no subscription to an individual ad is created. A blurred card or a response without usable media is not charged. With source=tiktok: 100 tokens per delivered TikTok row. MULTILANG CONTENT: get_ad and get_media(ad) use the same full detail carousel. primary_creative identifies its selected first creative; primary_creative_selection.status describes selected, fallback or unavailable. carousel_position is the original 1-based carousel position, not the returned array index. Missing/unconfirmed selected primary media is not charged or disclosed; both tools report primary_creative_unavailable. Per-slide title/body/link_url describe that slide; when the API omits a slide override, the ad-level value is supplied as its fallback, consistently in both tools. original_landing_domain describes the original ad, while creative_destination_url/domain describe the primary creative's destination. cloaking_detected is a detected domain/copy mismatch, not a verified redirect or a guarantee that a creative is black. Actual per-call debit is reported in _meta.billing.tokens_charged, in Spytrend quota tokens (not model tokens); admin may be exempt.
| 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 adds substantial behavioral context beyond the readOnlyHint/openWorldHint/negative-destructive annotations: it details quota semantics (50/1/100 tokens), the live status of unsigned expiring CDN links (never put in reports), billing debits via `_meta.billing.tokens_charged`, no-charge edge cases (blurred cards, no usable media), no subscription behavior, field-absence semantics (body_index), and primary_creative selection status. This is rich, actionable behavior that far exceeds 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 thorough but long and dense, with many paragraphs and repeated references to the shared carousel used by both tools. It front-loads 'Fetch a single ad by its id' and then layers a large amount of billing and media edge-case detail. Every sentence does add value, yet the volume of information could overwhelm the average agent; there is no pruning or prioritization.
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?
Backed by a high coverage schema and output schema, the description still adds edge cases that are not present in structured data: body-field absence logic, the `expiring` CDN flag and its consequences, billing charge rules, `_meta.billing.tokens_charged`, and also the special behavior when source=tiktok. Considering the complexity of the tool and the provided schemas, this description is highly complete, although it could leave a few niche topics (e.g., error codes for non-existent ad IDs) to inference.
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 has 100% description coverage for both parameters: `id` and `source` already have robust descriptions including source default and pricing. The tool description adds pricing side-effects and source-specific price warnings (`source=tiktok` in provenance), but does not add any meaning about parameter syntax, types, or constraints beyond what the schema defines. Baseline 3 is appropriate because the schema carries the weight.
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 ad by its id with FULL details including its creative media'—clearly distinguishing it from siblings like get_creative or get_media. It also enumerates exact fields returned (text, body_chars, title, creative URL, geo, status, advertiser), making the tool's scope 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 context of use is implied: call this when you need a single ad's full details by ID. However, it does not explicitly state when to choose this tool over get_media or get_creative, aside from a note that get_media(ad) has the same detail carousel. The source=tiktok switch is described, but no alternative pathway or 'do not use when' guidance is given, so the description carries only implied usage direction.
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 already declare readOnlyHint=true and destructiveHint=false, and the description adds substantial behavioral context beyond that: it is a paid surface with token costs per row, pending/error responses refund the reservation, totals are nullable with null meaning pending/unavailable (not zero), only the status section removes its own filter, and results are flattened with dimension field and truncation metadata. This is rich, non-obvious behavior that an agent needs to know.
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 well-structured: it front-loads the core purpose, then covers cost, limits, return shape, null semantics, filter behavior, exclusions, and retry guidance. Every sentence carries information. It is long, but the tool is complex (49 parameters, paid surface, nuanced null semantics), so the length is justified. It loses one point for being a wall of text without paragraph breaks, which slightly reduces scannability.
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 (49 parameters, paid surface, output schema present), the description is remarkably complete. It covers cost, limits, null semantics, filter behavior, exclusions, retry guidance, and subscription category reporting. The output schema exists, so return values need not be described in detail. An agent has everything needed to call this tool correctly and avoid costly mistakes.
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. The description adds meaningful parameter-level context beyond the schema: it explains the default and maximum rows per section (10/20), which maps to max_items_per_section; it clarifies that totals are nullable and the distinction between null and numeric 0; and it notes that auto-applied subscription categories are reported in auto_applied_verticals and scope_note. It doesn't enumerate every parameter, but the schema already covers them, and the description adds the critical semantic nuances.
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 the bounded analytics overview for the current Meta /ads selection' and enumerates the exact breakdown dimensions (statuses, countries, AI categories, landing domains, advertisers, webmasters). It clearly distinguishes this from sibling search tools by emphasizing it is an analytics overview for the current selection, not a search or retrieval tool.
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 states when to use this tool: for bounded analytics on the current /ads selection, and it names exclusions: 'No pagination, sorting, source, scheduler, force-scrape, TikTok, raw SQL or storage coordinates are accepted.' It also provides operational guidance: re-issue after retry_after_seconds when pending, do not blindly retry paid calls. This is strong usage guidance.
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 only declare readOnlyHint=true and openWorldHint=true, but the description goes far beyond: it discloses that the returned card includes exact precomputed counts, that top_creatives are the most re-uploaded creatives with media stripped, and that category counts are only for AI-labeled ads. It also discloses quota (1 token) and the top_creatives contract regarding market-wide reuse. No contradiction with annotations; the description adds rich behavioral context that annotations alone do not convey.
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 most content is essential given the tool's complexity (many fields, nested contracts, interpretation rules). It is front-loaded with the core purpose and a list of fields, then procedural nuances. Some redundancy (e.g., repeating 'top_creatives' explanations) could be trimmed, but overall it is packed with actionable detail and avoids fluff. Not as succinct as possible, but justified.
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?
Despite an output schema existing (indicated by context), the description explains return contents thoroughly: the list of fields, the special semantics of counts (exact vs labeled), the top_creatives contract, and quota. It covers all practical aspects an agent needs to call the tool correctly, including id resolution and interpretation of metrics. No missing information is apparent for a correct invocation.
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 describes 'id' only as 'the resource id to fetch' (generic). The description vastly enhances meaning: id can be an advertiser UUID or a Facebook page id, and provides multiple sources (ad's advertiser_id, get_trends dimension, get_creative's fanpages, page_id, or pasted Facebook link). It also explains automatic resolution of page ids and links. This far exceeds schema coverage and gives an agent concrete guidance on constructing the id value.
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 explicitly states the tool fetches a single advertiser's full card by id and enumerates all contained fields (name, external_id, avatar_url, page_url, total_ads, active_ads, etc.). It distinguishes itself from search_ads by positioning itself as the exact-count source and mentions alternatives like get_media and search_ads for other purposes. The verb 'Fetch' and resource 'advertiser's full card' make the purpose 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 provides explicit usage directives: it is the cheap way to get exact ad counts, and warns against paginating search_ads for that. It also instructs to call search_ads when listing an advertiser's ads, explains how to resolve IDs (UUID, page id, Facebook link), and details how to interpret category counters and ai_coverage. It includes a clear exclusion (do not compare category counts to total_ads directly) and naming of alternatives, satisfying the 'when-not' criterion.
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?
Beyond the readOnlyHint and openWorldHint annotations, the description discloses extensive behavioral specifics: it never starts transcription, breakdown lists are sorted and capped at 1000, status values are explained (available_bounded, partial, unavailable), copy_language statuses are detailed, and media URLs are intentionally excluded. There is 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?
Though lengthy, the description is densely packed with essential details and every sentence serves a purpose. It is logically ordered: core fields, language specifics, breakdown behavior, id source, media exclusion, and quota. No fluff or repetition, and the most critical purpose is 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?
For a tool with this many status codes, edge cases, and output nuances, the description leaves nothing critical uncovered. It explains all breakdown statuses, language copy statuses, sorting/capping behavior, and the quota, making it sufficient for an agent to call correctly without opening the output schema. Complete.
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 describes the id as 'the resource id to fetch' with 100% coverage. The description adds crucial semantics: the id is a creative UUID from search_creatives and the same id is used for get_media, providing provenance and cross-tool linkage that the schema alone lacks. This elevates it above 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 clearly states the tool fetches one creative's metadata and its bounded breakdown by id, listing specific fields like activity period, languages, and aggregates. It differentiates itself from siblings by noting the id comes from search_creatives and that media URLs are handled by get_media, making its purpose distinct and 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 provides clear context for when to use this tool: when you need detailed creative metadata by UUID, especially from search_creatives. It also points to get_media for downloading media and mentions that fanpage ids feed other search filters, giving practical guidance. However, it doesn't explicitly state scenarios where one should NOT use this tool over alternatives, leaving some inference to the agent.
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 media URL can become unavailable, 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, 1 token per row) → pick ids → get_media. QUOTA: 50 tokens per delivered multilang-upload ad, 1 per ordinary ad, 10 per creative-catalogue item (free starter: 500 tokens total; paid plans: 40,000/month). The ad price is per returned ad, not per media file. Actual ad membership determines the price even without a preceding filtered search. Every new call is billed; get_ad already includes media, so do not call get_media again for files you already received. Invalid, missing or undelivered items are not charged. Mixed batches examine a quota-bounded prefix and return affordable items with explicit failures for the rest; an unexamined tail may contain affordable ordinary ads, which can be requested separately. get_usage is free. MULTILANG CONTENT: get_ad and get_media(ad) use the same full detail carousel. primary_creative identifies its selected first creative; primary_creative_selection.status describes selected, fallback or unavailable. carousel_position is the original 1-based carousel position, not the returned array index. Missing/unconfirmed selected primary media is not charged or disclosed; both tools report primary_creative_unavailable. Per-slide title/body/link_url describe that slide; when the API omits a slide override, the ad-level value is supplied as its fallback, consistently in both tools. original_landing_domain describes the original ad, while creative_destination_url/domain describe the primary creative's destination. cloaking_detected is a detected domain/copy mismatch, not a verified redirect or a guarantee that a creative is black. Actual per-call debit is reported in _meta.billing.tokens_charged, in Spytrend quota tokens (not model tokens); admin may be exempt.
| 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?
Adds substantial behavior beyond the annotations: quota token costs, per-item failure handling, mixed-batch prefix processing, duplicate handling, fallback_urls retry on HTTP errors, and multilang content behavior. It even explains what happens to malformed/zero UUIDs. No contradiction with the readOnlyHint/destructiveHint 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 thorough but extremely long and dense, with many details that duplicate the output schema, such as the response fields block. It is front-loaded with the core purpose, but it would benefit from bullets or sectioning to improve scanability. While much of the content is valuable, it is 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?
Covers an unusually complete set of edge cases: quota billing, partial failures, mixed batches, fallback URLs, duplicate items, multilang selection, and pricing rules. Given the output schema exists, nothing critical appears missing for an agent to correctly select, invoke, and interpret the results of this paid endpoint.
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 both parameters with 100% coverage, including the entity_type enum and the ids array limits. The description adds operational context such as billing and malformed-id handling, but it does not meaningfully enrich the semantic meaning of the parameters beyond what the schema already provides, 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 and resource: 'Fetch downloadable creative MEDIA (image/video URL + thumbnail) for 1-500 ad or creative UUIDs returned by search_ads / search_creatives.' It also says it is the 'paid delivery step,' which gives an immediate sense of why this tool exists and how it differs from free browsing tools. It distinguishes itself from get_ad by warning against duplicate fetching.
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 an explicit typical flow: 'search_ads ... → pick ids → get_media.' It also gives clear exclusion guidance: 'get_ad already includes media, so do not call get_media again for files you already received,' and notes 'get_usage is free.' This is strong when-to-use and when-not-to-use guidance.
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?
Discloses important data-quality caveats beyond the annotations: traffic estimates, legacy snapshots, non-canonical page titles, correlation-only AI signals, missing rank behavior, and quota consumption. These are exactly the non-obvious behaviors an agent needs to interpret results correctly.
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 and long, but nearly every sentence carries a distinct warning or clarification that prevents misuse. The core purpose is front-loaded, and the additional caveats are organized around the returned fields rather than random trivia.
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 output schema exists, the description focuses on field semantics, entity-type caveats, quota cost, and the follow-up save action. It covers the important interpretation pitfalls and operational constraints without needing to restate the full return shape.
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 already describes the domain parameter well, and the description adds context about domain surfaces and the correct ID for saving. However, the added value over the schema is moderate; most parameter meaning comes from the schema itself.
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?
Clearly identifies the action ('Fetch') and resource ('one domain surface's FULL profile') with a specific domain parameter. The description also clarifies what the profile does and does not represent, which prevents misclassification of the entity.
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?
Gives clear context: use it to fetch a single domain surface, and explicitly redirects saving to add_to_favorites with the correct entity type and parameter shape. It also tells the agent to inspect registrable_domain and domain surface type to avoid misreading the entity, though it does not contrast directly with the sibling search tools.
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 (50 tokens per multilang ad, 1 per ordinary 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 say readOnlyHint=true, openWorldHint=true, destructiveHint=false. The description adds extensive behavioral detail: quota costs (1 token per delivered result, refunds for short pages), free plan restrictions (6-row preview, no pagination, offset refused), rate limits (60/min, 1000/hr, structured error code), concurrency admission (one heavy call at a time), pagination semantics (has_more/offset on certain dimensions, absent on others), and plan-window truncation with total_status='restricted'. It also notes that dimension semantics arrive in meta.dimension_semantics. 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 very long but structured: opening summary, dimension one-liners, then pagination, free plan, quota, and concurrency. It front-loads the core purpose and dimension list. However, there is redundancy, e.g., the 'FREE PLAN COUNTS' paragraph appears twice with near-identical text, and some sentences are dense. It earns a high score for necessity given the 52 parameters and intricate behavior, but loses a point for not being tightened.
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 and the existence of an output schema, the description covers most operational aspects: dimensions, filters, pagination, quotas, rate limits, concurrency, account linking, and free plan behavior. It deliberately defers per-dimension row semantics to meta.dimension_semantics delivered with the response, which is acceptable. Some specific details (e.g., exact output format for each dimension) are omitted, but the description explains they arrive with data. This is complete enough for an agent to call correctly, though a bit more explicit explanation of scaling signals could help.
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?
Though schema coverage is 100%, the description adds significant meaning beyond the schema. It explains common filters, dimension-specific parameter applicability (e.g., 'snapshot=v4 unlocks spike_window...', 'store_type has NO effect on dimension=advertisers'), default behaviors (page size 20, default page cost), and nuances like 'limit is ignored for geo_timeline'. It also clarifies parameter interactions (e.g., date_basis to reproduce site numbers, min_growth compared to site default). This enriches the schema substantially.
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: 'Get aggregated trend rankings for a dimension' and enumerates the dimensions (geo, geo_timeline, advertisers, webmasters, apps, timeline, scaling, domains). It explicitly distinguishes from siblings, e.g., 'Use for rankings/aggregates, NOT to list individual ads (use search_ads)' and 'Rows never carry media URLs... fetch the asset with get_media'. This makes the tool's 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?
Clear usage context is provided: it says when to use (rankings/aggregates) and when not to (listing individual ads, use search_ads; fetching media, use get_media). It also advises on concurrency: 'Issue heavy calls sequentially; light lookups... are not gated.' It even notes dimension-specific choices like 'For BRANDS use domains' and 'For creative trends use scaling'. This goes beyond minimal 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), ordinary ad media = 1, multilang-upload ad opening = 50, creative media = 10, TikTok row = 100. Each opening call is billed separately. 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 | |
| multilang_ad_open_tokens | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, openWorldHint=true, idempotentHint=true, and destructiveHint=false. The description adds genuinely non-obvious behavior: 'This call is FREE — it consumes no quota,' plus the auto-refund billing nuance. 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 main purpose is front-loaded in the first sentence, and the token-cost tariff is genuinely useful context for cost-aware calling. It loses a point because ~100 words of billing detail appear as one dense unformatted paragraph for a tool with no parameters; light structure (bullets or separation of quota semantics from tariffs) would improve digestibility.
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 0-parameter, read-only tool with a full annotations set and an output schema available, the description is complete: it names the returned fields (plan, tokens used, remaining, limit), explains the plan tiers, and discloses that the call itself costs nothing. Nothing an agent needs to invoke 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?
The tool has zero parameters and schema_description_coverage is 100% (it's an empty object), so the baseline of 4 for 0-parameter tools applies. The description appropriately does not fabricate parameter details; there is nothing more to add beyond 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 opening sentence states a specific verb+resource: 'Return the caller's current MCP quota: plan, tokens used, remaining and limit.' The quota resource is clearly distinct from all ~20 sibling tools, which target ads, webmasters, creatives, shops, trends, or favorites, so an agent can tell this tool apart without opening schemas.
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 for when to call it — to see the current plan, token count, and limits — and it emphasizes the call is free, which is exactly the information an agent needs before making costly searches. There is no sibling quota tool, so explicit 'when-not-to-use' instructions or alternative names would add little; a small deduction for not explicitly stating when to prefer it.
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?
Annotations only declare readOnly/openWorld/non-destructive, but the description adds substantial behavioral context: top_creatives ordering semantics, total_ads interpretation as market-wide family size, analytics_pending meaning, fanpages_status nuance, strict UUID rejection, TikTok token cost and Pro-plan requirement, and edge-weighting caveats for attribution merge edges. 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 long but densely packed, with every sentence carrying a distinct operational warning or semantic clarification. It is front-loaded with the core purpose and uses recognizable labels like QUOTA and TIKTOK to structure auxiliary details. Nothing reads as filler or tautology.
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 intricate output semantics, the description is remarkably complete: it explains ambiguous fields, quota costs, plan requirements, source variants, and interpretation pitfalls. The presence of an output schema covers the raw return shape, while the description covers the semantics that the schema cannot express.
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 that id must be the canonical UUID from search_webmasters and that domain names/display names are rejected. Source semantics are already in the schema enum description, so the added parameter insight is limited but meaningful.
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: aggregate stats PLUS top_creatives'. It clearly identifies what the tool returns and distinguishes it from sibling search tools by emphasizing single-entity retrieval by canonical UUID. The TikTok variant is also explicitly differentiated.
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 this tool for a single webmaster card, use search_creatives/search_ads for the rest of the network, and get_media for downloading creatives. It also states a hard precondition: the id must be the canonical UUID from search_webmasters, with domain/display names rejected. This leaves no ambiguity about when to invoke this tool versus alternatives.
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 cover read-only, idempotent, non-destructive behavior, and the description adds meaningful specifics: normalized values, only facets with at least one other match, and FREE. This goes beyond what annotations communicate without contradicting them.
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 three concise sentences with the core purpose front-loaded and no filler. Every sentence adds information: what is returned, what similarity means, and the inclusion filter.
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 simple one-parameter, read-only tool with a rich output schema and strong annotations, the description covers purpose, scope, filtering behavior, and constraints. Nothing critical is missing for invoking it 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% and the only parameter's description already says it is the source webmaster UUID from search_webmasters. The tool description adds no additional parameter semantics, matching 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 concrete resource ('URL-parameter keys and normalized values') and clarifies that similarity means shared affiliate/tracking parameter values, not name or domain similarity. This distinguishes the tool from similarity-oriented siblings such as find_similar_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 clear context: use it to get URL-parameter facets for related webmasters, and explicitly excludes name/domain similarity. It doesn't name a sibling alternative like find_similar_webmasters or state a when-not condition beyond the similarity definition, so it falls short of full routing guidance.
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 readOnlyHint, idempotentHint, and nondestructive behavior; the description adds quota info ('FREE — does not consume quota') and explains the different item representations returned per folder type (ids, hub profiles, domains). 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 dense but not bloated: it states the return shape, both parameter modes, the recommended use, and the quota effect in about four sentences. Every sentence carries operational value.
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 output schema and rich annotations, the description still adds enough to call the tool confidently: return fields for the folder list, exact item-return behavior for the folder parameter, how to disambiguate duplicate names, and quota impact. No important operational gap remains.
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 both parameters fully; the description largely restates the same mode information and even the disambiguation caveat ('pass entity_type to disambiguate') that appears in the schema. It adds a little value by tying ids to get_creative/get_webmaster/get_ad, but does not substantially extend 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, 'List the caller's favourites folders (name, entity_type, item_count)', and then clarifies the two modes: folder listing and folder contents. It also names the related tool add_to_favorites, so an agent can see how this read operation fits.
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 instructions about when to omit entity_type (all folders), when to pass it (filter), and when to pass folder (return items). It also states the purpose: 'Use this to confirm what add_to_favorites saved and to read a folder back.' It does not mention explicit exclusions, but no sibling tool overlaps directly with favourites.
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. MULTILANG UPLOAD: formats=[carousel_multilang] selects the same detected carousel pattern as the website; carousel_multilang_categorized selects its AI-categorized subset. This is independent of the languages filter. Formats combine with OR, so do not combine the base and categorized variants when requesting only categorized results. Search remains 1 token per row; opening a matching ad with get_ad or get_media costs 50 tokens. QUOTA: 1 token per DELIVERED result (default page 20 = 20 tokens; short pages auto-refund). Results carry metadata only; get_ad/get_media charge 50 tokens per multilang-upload ad opening, 1 per ordinary ad, and get_media charges 10 per creative-catalogue item. Each new opening call is billed separately. Meta search_ads rows include is_multilang and opening_price (Spytrend tokens per ad per call); unavailable/null means the price could not be verified, not zero. Opening quotes are not a debit or a price lock. Request small limits and narrow filters. 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 (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, estimated or unavailable; unavailable is not zero). With source=tiktok: Supported TikTok filters: query/search_in, country/countries, country_match, categories, ai_subcategory, languages, formats (single|carousel|video), landing_domain, advertiser_id (TikTok business id), webmaster_id, contains_in_links, media_type, cta_buttons, domain_zones, status_today (active|inactive), min_days_active/days_active_to, max_countries, dedupe, date_from/date_to (delivery-window overlap), sort_by=date|days_active, sort_order, limit, cursor. Countries overrides country. Categories and ai_subcategory combine with OR on TikTok. title and text both search the ad text; advertiser searches the advertiser name. date_from/date_to selects ads whose delivery window overlaps the requested inclusive period, not ads launched in that period. Active means shown within the last 3 calendar days. Unsupported filters (including hub_domains, folder_id, saved and landing_domain_exact) are REJECTED before charging; no filter is silently ignored. Default TikTok limit is 10; each delivered row costs 100 tokens.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | max ad rows to return (maximum 200). Meta defaults to 20 at 1 token per delivered row; TikTok defaults to 10 at 100 tokens per delivered row. | |
| 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 | Meta: 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. TikTok: delivery-window overlap; the first delivery date must be on or before date_to | |
| formats | No | creative formats: video, carousel, single, dynamic, carousel_multilang, carousel_multilang_categorized. Multilang upload detection is independent of languages; the categorized variant additionally requires an AI category. Values combine with OR. Search costs 1 token per row; opening a multilang ad costs 50 tokens. | |
| 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 | TikTok: date or days_active only. Meta: 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 | Meta: 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. TikTok: delivery-window overlap; the last delivery date must be on or after date_from, not necessarily the launch date | |
| 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 cover read-only/open-world safety, but the description adds extensive behavior beyond them: per-row token billing (1 vs 100 for TikTok), the 40,000/month cap, Pro-plan gating with refund/partial-delivery semantics, exact vs estimated vs unavailable count contracts, the 'unavailable is not zero, re-issue' rule, and the most_reused_creative group-total omission.
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?
It is front-loaded with the return-shape constraint and routing, but it is extremely long and repetitive — the token/quota model and the TikTok premium are re-explained multiple times, and the filter list duplicates the schema. For a 45-parameter tool some length is warranted, but the verbosity costs readability.
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 45 parameters, no required params, an existing output schema, and a complex billing/counting contract, the description covers everything an agent needs: filters, sorting, pagination, exact-count windows, plan restrictions, and TikTok-specific shape and cost. Nothing essential is omitted.
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; the description still adds meaning such as 'use landing_domain — NOT query' for domain lookups and the category-vs-trends semantics. However, much of the per-filter detail (keyword, status_today, ranges) merely restates schema-documented parameters, so it does not rise far above 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?
States a specific verb (search) and resource (the spytrend.com ad database) and immediately scopes what is returned (ad METADATA, not ad text). It distinguishes itself from siblings by naming get_ad for text, get_media for creative download, get_trends for aggregates, and get_advertiser/get_webmaster for entity counts.
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 routing rules are given for nearly every alternative: use get_ad for ad text, get_media for downloadable creative, add_to_favorites to save, get_trends for rankings, and do NOT paginate search_ads for advertiser totals. It also warns which date axis to use (date_from/date_to vs first_seen_from) and when not to cross-compare category totals with get_trends.
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?
The description goes far beyond the minimal readOnlyHint=true and destructiveHint=false. It discloses quotas, rate limits (60/min, 1000/hr), concurrency gating for 'heavy' calls, free-plan archive window restrictions, and the meaning of pagination.total_status='restricted'. It even explains that a zero means 'nothing in the archive window', preventing misinterpretation. 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 long but deliberately front-loaded: first sentence states the core purpose, then immediately differentiates alternatives, then dives into quota/rate-limit/concurrency details. Every section serves a real decision the agent faces. While dense, it avoids redundancy and earns its length given the tool's operational complexity.
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, rate limits, concurrency gates, and a free-plan special case, the description covers every conceivable decision point: how to size pages, what errors look like, how to connect an unlinked agent, and what happens when the archive window shrinks. The existence of an output schema means return-value details need not be spelled out. Nothing essential 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%, so both parameters are already described in schema. The description adds actionable details beyond schema: query must be ≥2 chars, a link in any form resolves to canonical fanpage with exact match first, limit defaults to 20 but is clamped to 50 upstream (not rejected). These are value-add semantics, not just repetition.
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 clear verb+resource ('Search advertisers') and specifies exactly the three input forms accepted (name, numeric page id, or pasted link). It explicitly distinguishes itself from siblings: get_advertiser for one full card, search_ads for ads, get_trends for rankings. No ambiguity remains about what this tool does.
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 when-to-use versus alternatives is given: '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 ... use get_trends with dimension=advertisers.' It also explains when to expect restricted results and how to handle them. This is textbook usage guidance.
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 Meta creative families (source=meta only; TikTok callers must use search_ads with source=tiktok and dedupe=true) 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. query/search_in searches the texts of the creative family's ads; all query words must match the family. 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). Results carry metadata only; get_ad/get_media charge 50 tokens per multilang-upload ad opening, 1 per ordinary ad, and get_media charges 10 per creative-catalogue item. Each new opening call is billed separately. Meta search_ads rows include is_multilang and opening_price (Spytrend tokens per ad per call); unavailable/null means the price could not be verified, not zero. Opening quotes are not a debit or a price lock. Request small limits and narrow filters. 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 | |
| source | No | creative catalogue source: meta (default). TikTok is not supported by this tool; use search_ads with source=tiktok and dedupe=true for TikTok ad representatives. Unsupported sources are rejected before charging. | |
| 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 activity filter is independent from period_from/period_to; 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?
Annotations already declare readOnlyHint, openWorldHint, and destructiveHint=false, so the safety profile is covered. The description goes substantially further: full quota model (1 token per delivered result, short pages refunded, 50 tokens per multilang ad opening, 10 per catalogue item, billed separately), period parity contract, axis contract, rejection-before-charge guarantees, and empty-page semantics. This is rich beyond what annotations provide, though the density borders on documentation-level rather than concise disclosure.
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 and largely front-loaded with the meta-only scoping and sibling routing first, which is good. However, it is extremely long for a tool description, with duplicated contract material (PERIOD PARITY CONTRACT and AXIS CONTRACT restate much of what is already in the schema descriptions for period_from/period_to, sort_by, and last_seen fields). Several sentences repeat concepts already covered by the schema, reducing signal density.
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 34 parameters, 100% schema coverage, an output schema, and rich annotations, the description is more than complete—it covers quota semantics, period parity, axis semantics, and rejection behavior that the structured fields alone would not convey. It arguably over-delivers on completeness at the expense of conciseness, but nothing an agent needs to invoke 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?
Schema coverage is 100%, so the schema already documents all 34 parameters in depth, establishing a baseline of 3. The description adds meaning beyond the schema by grouping filters, clarifying cross-parameter interactions (country cannot combine with hub_category or channel; advertiser_ids supersedes advertiser_id; period_from requires period_to), and explaining sort_semantics for slices—all of which exceed the schema's per-field 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?
States a specific verb+resource (search/browse creative families), scopes it to source=meta, and explicitly differentiates from the sibling search_ads for TikTok callers. An agent can distinguish this tool from all siblings without opening a schema.
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?
Explicitly names when to use this tool vs search_ads (TikTok callers must use search_ads with dedupe=true), names get_media for downloading and add_to_favorites for saving. It also states when NOT to combine filters (country + hub_category or channel) and gives routing guidance for related tasks.
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 state readOnly/open/destructive hints, and the description goes far beyond: token pricing (catalog free, 1 token per delivered profile), free-plan restricted totals with meta.plan_window_cutoff semantics, quota tiers (500 lifetime / 40k monthly with auto-refund), rate limits with structured error.code=rate_limit_exceeded, concurrency admission (one heavy analytical call at a time), and autonomous-agent auth requirements with refusal behavior. An agent literally knows the failure modes and costs before calling.
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 genuinely long (a dense wall of text), but it is front-loaded — the first two sentences carry purpose and call modal, and sections are clearly marked with caps headers. The tool is complex (13 optional params, two modes, auth, billing, rate-limit, concurrency, free-plan constraints), so the length is mostly earned. It loses a point for writing style: the crammed prose would read better as bullets; the token-cost and quota paragraphs are best with tight formatting.
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 13-parameter, 0-required, complex tool, the description covers everything an agent needs: both modes, which params apply to which mode, free/paid quotas, restricted-window semantics, rate limits, concurrency policy, auth refusal behavior, paging defaults, and even examples. The output schema exists, so not delineating return fields is acceptable. There is almost nothing left for an agent to discover through costly trial-and-error.
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% and the schema itself is unusually detailed, so the baseline is 3. The description adds strategic meaning beyond the schema: it connects parameters into usage recipes (country+relevance+exclude_cloaking to surface storefronts), explains the catalog-vs-profile split that determines which parameter groups apply, clarifies sort=active 'hot now' semantics, and explains the pricing consequence of limit. This is real additive value rather than schema restatement.
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+resource: 'Explore HUBS where ads send traffic, grouped by destination', enumerates the categories (Social Media, App Stores, Amazon, E-commerce, Popular, Platforms), and distinguishes the two call modes (catalog vs hub drill-in). It clearly separates this tool from sibling search tools like search_ads and search_advertisers by anchoring on destination grouping rather than ad/campaign or individual objects.
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 when-to-call scenarios: 'Call WITHOUT hub to get the catalog', 'Call WITH hub set to a slug to drill into', an example recipe ('combine country + sort=relevance + exclude_cloaking for a per-geo ranking of real storefronts'), and clear when-not guidance ('window is a no-op', 'a category slug is rejected'). It stops short of naming sibling tools as alternatives or explicitly stating 'use search_ads instead when...', so it gets a 4 rather than 5.
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?
Beyond annotations (readOnlyHint=true, destructiveHint=false), the description discloses many behavioral nuances: visits is a monthly estimate not live, traffic_paid_share is only trustworthy under a condition, growth filtering has precise qualification rules, created_from/to refer to registrable root not first-seen, fb_signal is correlation-only, sort_by=relevance requires ai_category, pagination total_status can be 'restricted', quota per delivered result, concurrency gating, and autonomous-agent linking requirements. This far exceeds the annotation baseline.
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 tightly packed with critical information. It is front-loaded with the core definition, then details, then operational constraints (quota, rate limits, concurrency). Every section earns its place; the length is justified by the tool's complexity (40 parameters, multiple data-quality caveats). Minor redundancy (e.g., some parameter semantics repeated in the schema) means it could be trimmed slightly, but it remains well-structured.
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 description covers all essential aspects for correct invocation: output pagination (total, has_more, offset), free-plan behavior, quota accounting, rate limit details, concurrency admission, and relationship to related tools. The output schema presumably handles field-level documentation, and the description complements it with interpretive guidance. Nothing critical 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 schema has 100% parameter description coverage, yet the description adds essential global semantics not fully captured by individual parameter descriptions: e.g., the interpretation of visits_growth_pct as percent and its qualification rules, the meaning of traffic_start_max as max of preceding two complete months, the caveat about category vs ai_category, and the trust condition for traffic_paid_share. It also explains the interaction between parameters (e.g., min_growth and sort_by=growth). This adds significant value beyond 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 states precisely that the tool searches domain-level traffic surfaces with commerce and advertising intelligence, and clarifies the unit of analysis (domain/subdomain, not necessarily company/product). It clearly distinguishes from siblings like get_shop (full payload) and search_ads/search_creatives. The core purpose is 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 provides explicit guidance: use get_shop for the full source payload, and notes that heavy analytical tools (get_trends, search_ads, search_creatives) are concurrency-gated while this tool is not heavy. It also explains quota semantics, rate limits, and when free-plan restrictions apply. This gives clear selection and operational context.
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 (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, estimated or unavailable; unavailable is not zero). With source=tiktok rows are TikTok webmaster clusters. Supported TikTok webmaster filters: query, domain, countries (+min_geo_share), exclude_countries, ai_category (dominant), min_total_ads, min_active_ads, date_from/date_to, has_funder, sort_by=total_ads|active_ads|created_at|relevance, sort_order, offset (0-5000), limit (server caps TikTok at 100). Date filters select clusters whose activity window overlaps the requested period. Active means shown within the last 3 calendar days. Meta-only lookups, languages/platforms/creative_format(s), status_today, ai_subcategory, folder_id and saved are REJECTED before charging. Default TikTok limit is 10; each delivered row costs 100 tokens.
| 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 only cover the safety profile (readOnly/openWorld/non-destructive); the description adds the operationally decisive facts an agent cannot get elsewhere — per-row token cost, 100x TikTok premium, auto-refunds, plan window cutoffs (total_status=restricted), rate-limit error codes, per-account concurrency admission for heavy calls, and the linked-account auth requirement with its refusal shape. This is exactly the beyond-annotations context the dimension rewards.
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?
Front-loaded correctly (definition, then workflow, then cost/auth), but the TikTok block is heavily verbose and repeats cost/plan/refund mechanics already implied earlier, and the message runs very long for a search tool. Much is justified by the paid TikTok corpus and 28 params, but there is clear redundancy that dilutes signal.
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 open-world search with a complex paid corpus and output schema present, the description covers selection, cost, auth, concurrency, plan-window semantics and lifecycle caveats. An output schema exists so return-value detail is not required, and the description still flags what the row payload contains. Nothing an agent needs to call it safely 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% so the baseline is 3, but the description adds cross-cutting meaning: min_geo_share as a dominance gate rather than a presence match, sort_by=relevance requiring countries, and source=tiktok rejecting Meta-only filters before charging. It does not, however, touch many of the 28 params (folder_id, saved, languages, platforms) beyond 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?
Opens with a specific verb+resource and immediately disambiguates the domain concept ('a webmaster is a whole ad NETWORK — landing domains, pixels, pages and geos grouped as one'), which is essential since 'webmaster' is not self-evident. It also names the sibling workflow steps (search_creatives/search_ads, get_webmaster) so an agent can place it among alternatives without opening schemas.
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?
Gives an explicit workflow ('search by domain, take the canonical UUID, then search_creatives/search_ads with webmaster_id'), names alternatives for reuse analysis (get_webmaster.top_creatives or search_creatives sort_by=total_ads), and states the conditional recipe for geo ranking (countries + min_geo_share + sort_by=relevance). TikTok-vs-Meta routing is spelled out with what is accepted and rejected in each corpus.
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.
2 tool updates
- Changed
search_ads5 fields changed- changed
Input schema / properties / date_from / descriptionPrevious value: -"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"New value: +"Meta: 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. TikTok: delivery-window overlap; the last delivery date must be on or after date_from, not necessarily the launch date" - changed
Input schema / properties / date_to / descriptionPrevious value: -"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"New value: +"Meta: 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. TikTok: delivery-window overlap; the first delivery date must be on or before date_to" - changed
Input schema / properties / limit / descriptionPrevious value: -"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."New value: +"max ad rows to return (maximum 200). Meta defaults to 20 at 1 token per delivered row; TikTok defaults to 10 at 100 tokens per delivered row." - changed
Input schema / properties / sort_by / descriptionPrevious value: -"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"New value: +"TikTok: date or days_active only. Meta: 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" - changed
Input schema / properties / sort_by / enumPrevious value: -[ - "date", - "most_popular", - "folder_added", - "most_reused_creative" -]New value: +[ + "date", + "most_popular", + "folder_added", + "most_reused_creative", + "days_active" +]
- Changed
search_creatives2 fields changed- changed
Input schema / properties / last_seen_from / descriptionPrevious value: -"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."New value: +"date range start (YYYY-MM-DD): only creatives whose LAST recorded activity/fixation is on or after this date. This activity filter is independent from period_from/period_to; it is not an interval-overlap or first-seen filter." - added
Input schema / properties / sourceAdded value: +{ + "description": "creative catalogue source: meta (default). TikTok is not supported by this tool; use search_ads with source=tiktok and dedupe=true for TikTok ad representatives. Unsupported sources are rejected before charging.", + "enum": [ + "meta", + "tiktok" + ], + "type": "string" +}
4 tool updates
- Changed
find_similar_ads15 fields changed- added
Output schema / properties / data / items / properties / creative_destination_domainAdded value: +{ + "type": "string" +} - added
Output schema / properties / data / items / properties / creative_destination_urlAdded value: +{ + "type": "string" +} - added
Output schema / properties / data / items / properties / is_multilangAdded value: +{ + "type": [ + "null", + "boolean" + ] +} - added
Output schema / properties / data / items / properties / media / items / properties / bodyAdded value: +{ + "type": "string" +} - added
Output schema / properties / data / items / properties / media / items / properties / carousel_positionAdded value: +{ + "type": [ + "null", + "integer" + ] +} - added
Output schema / properties / data / items / properties / media / items / properties / claimed_domainAdded value: +{ + "type": "string" +} - added
Output schema / properties / data / items / properties / media / items / properties / cloaking_detectedAdded value: +{ + "type": "boolean" +} - added
Output schema / properties / data / items / properties / media / items / properties / fallback_urlsAdded value: +{ + "items": { + "type": "string" + }, + "type": [ + "null", + "array" + ] +} - added
Output schema / properties / data / items / properties / media / items / properties / is_aliveAdded value: +{ + "type": [ + "null", + "boolean" + ] +} - added
Output schema / properties / data / items / properties / media / items / properties / link_urlAdded value: +{ + "type": "string" +} - added
Output schema / properties / data / items / properties / media / items / properties / titleAdded value: +{ + "type": "string" +} - added
Output schema / properties / data / items / properties / opening_priceAdded value: +{ + "additionalProperties": false, + "properties": { + "billing_basis": { + "type": "string" + }, + "status": { + "type": "string" + }, + "tokens": { + "type": [ + "null", + "integer" + ] + }, + "unit": { + "type": "string" + } + }, + "required": [ + "status", + "tokens", + "unit", + "billing_basis" + ], + "type": [ + "null", + "object" + ] +} - added
Output schema / properties / data / items / properties / original_landing_domainAdded value: +{ + "type": "string" +} - added
Output schema / properties / data / items / properties / primary_creativeAdded value: +{ + "additionalProperties": false, + "properties": { + "body": { + "type": "string" + }, + "carousel_position": { + "type": [ + "null", + "integer" + ] + }, + "claimed_domain": { + "type": "string" + }, + "cloaking_detected": { + "type": "boolean" + }, + "expiring": { + "type": "boolean" + }, + "fallback_urls": { + "items": { + "type": "string" + }, + "type": [ + "null", + "array" + ] + }, + "is_alive": { + "type": [ + "null", + "boolean" + ] + }, + "link_url": { + "type": "string" + }, + "media_type": { + "type": "string" + }, + "snapshot_url": { + "type": "string" + }, + "thumbnail_url": { + "type": "string" + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": [ + "null", + "object" + ] +} - added
Output schema / properties / data / items / properties / primary_creative_selectionAdded value: +{ + "additionalProperties": false, + "properties": { + "carousel_position": { + "type": [ + "null", + "integer" + ] + }, + "reason": { + "type": "string" + }, + "status": { + "type": "string" + } + }, + "required": [ + "status" + ], + "type": [ + "null", + "object" + ] +}
- Changed
get_ad15 fields changed- added
Output schema / properties / data / properties / creative_destination_domainAdded value: +{ + "type": "string" +} - added
Output schema / properties / data / properties / creative_destination_urlAdded value: +{ + "type": "string" +} - added
Output schema / properties / data / properties / is_multilangAdded value: +{ + "type": [ + "null", + "boolean" + ] +} - added
Output schema / properties / data / properties / media / items / properties / bodyAdded value: +{ + "type": "string" +} - added
Output schema / properties / data / properties / media / items / properties / carousel_positionAdded value: +{ + "type": [ + "null", + "integer" + ] +} - added
Output schema / properties / data / properties / media / items / properties / claimed_domainAdded value: +{ + "type": "string" +} - added
Output schema / properties / data / properties / media / items / properties / cloaking_detectedAdded value: +{ + "type": "boolean" +} - added
Output schema / properties / data / properties / media / items / properties / fallback_urlsAdded value: +{ + "items": { + "type": "string" + }, + "type": [ + "null", + "array" + ] +} - added
Output schema / properties / data / properties / media / items / properties / is_aliveAdded value: +{ + "type": [ + "null", + "boolean" + ] +} - added
Output schema / properties / data / properties / media / items / properties / link_urlAdded value: +{ + "type": "string" +} - added
Output schema / properties / data / properties / media / items / properties / titleAdded value: +{ + "type": "string" +} - added
Output schema / properties / data / properties / opening_priceAdded value: +{ + "additionalProperties": false, + "properties": { + "billing_basis": { + "type": "string" + }, + "status": { + "type": "string" + }, + "tokens": { + "type": [ + "null", + "integer" + ] + }, + "unit": { + "type": "string" + } + }, + "required": [ + "status", + "tokens", + "unit", + "billing_basis" + ], + "type": [ + "null", + "object" + ] +} - added
Output schema / properties / data / properties / original_landing_domainAdded value: +{ + "type": "string" +} - added
Output schema / properties / data / properties / primary_creativeAdded value: +{ + "additionalProperties": false, + "properties": { + "body": { + "type": "string" + }, + "carousel_position": { + "type": [ + "null", + "integer" + ] + }, + "claimed_domain": { + "type": "string" + }, + "cloaking_detected": { + "type": "boolean" + }, + "expiring": { + "type": "boolean" + }, + "fallback_urls": { + "items": { + "type": "string" + }, + "type": [ + "null", + "array" + ] + }, + "is_alive": { + "type": [ + "null", + "boolean" + ] + }, + "link_url": { + "type": "string" + }, + "media_type": { + "type": "string" + }, + "snapshot_url": { + "type": "string" + }, + "thumbnail_url": { + "type": "string" + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": [ + "null", + "object" + ] +} - added
Output schema / properties / data / properties / primary_creative_selectionAdded value: +{ + "additionalProperties": false, + "properties": { + "carousel_position": { + "type": [ + "null", + "integer" + ] + }, + "reason": { + "type": "string" + }, + "status": { + "type": "string" + } + }, + "required": [ + "status" + ], + "type": [ + "null", + "object" + ] +}
- Changed
get_media16 fields changed- added
Output schema / properties / items / items / properties / creative_destination_domainAdded value: +{ + "type": "string" +} - added
Output schema / properties / items / items / properties / creative_destination_urlAdded value: +{ + "type": "string" +} - added
Output schema / properties / items / items / properties / is_multilangAdded value: +{ + "type": [ + "null", + "boolean" + ] +} - added
Output schema / properties / items / items / properties / media / items / properties / bodyAdded value: +{ + "type": "string" +} - added
Output schema / properties / items / items / properties / media / items / properties / carousel_positionAdded value: +{ + "type": [ + "null", + "integer" + ] +} - added
Output schema / properties / items / items / properties / media / items / properties / claimed_domainAdded value: +{ + "type": "string" +} - added
Output schema / properties / items / items / properties / media / items / properties / cloaking_detectedAdded value: +{ + "type": "boolean" +} - added
Output schema / properties / items / items / properties / media / items / properties / expiringAdded value: +{ + "type": "boolean" +} - added
Output schema / properties / items / items / properties / media / items / properties / is_aliveAdded value: +{ + "type": [ + "null", + "boolean" + ] +} - added
Output schema / properties / items / items / properties / media / items / properties / link_urlAdded value: +{ + "type": "string" +} - added
Output schema / properties / items / items / properties / media / items / properties / snapshot_urlAdded value: +{ + "type": "string" +} - added
Output schema / properties / items / items / properties / media / items / properties / titleAdded value: +{ + "type": "string" +} - added
Output schema / properties / items / items / properties / opening_priceAdded value: +{ + "additionalProperties": false, + "properties": { + "billing_basis": { + "type": "string" + }, + "status": { + "type": "string" + }, + "tokens": { + "type": [ + "null", + "integer" + ] + }, + "unit": { + "type": "string" + } + }, + "required": [ + "status", + "tokens", + "unit", + "billing_basis" + ], + "type": [ + "null", + "object" + ] +} - added
Output schema / properties / items / items / properties / original_landing_domainAdded value: +{ + "type": "string" +} - added
Output schema / properties / items / items / properties / primary_creativeAdded value: +{ + "additionalProperties": false, + "properties": { + "body": { + "type": "string" + }, + "carousel_position": { + "type": [ + "null", + "integer" + ] + }, + "claimed_domain": { + "type": "string" + }, + "cloaking_detected": { + "type": "boolean" + }, + "expiring": { + "type": "boolean" + }, + "fallback_urls": { + "items": { + "type": "string" + }, + "type": [ + "null", + "array" + ] + }, + "is_alive": { + "type": [ + "null", + "boolean" + ] + }, + "link_url": { + "type": "string" + }, + "media_type": { + "type": "string" + }, + "snapshot_url": { + "type": "string" + }, + "thumbnail_url": { + "type": "string" + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": [ + "null", + "object" + ] +} - added
Output schema / properties / items / items / properties / primary_creative_selectionAdded value: +{ + "additionalProperties": false, + "properties": { + "carousel_position": { + "type": [ + "null", + "integer" + ] + }, + "reason": { + "type": "string" + }, + "status": { + "type": "string" + } + }, + "required": [ + "status" + ], + "type": [ + "null", + "object" + ] +}
- Changed
search_ads15 fields changed- added
Output schema / properties / data / items / properties / creative_destination_domainAdded value: +{ + "type": "string" +} - added
Output schema / properties / data / items / properties / creative_destination_urlAdded value: +{ + "type": "string" +} - added
Output schema / properties / data / items / properties / is_multilangAdded value: +{ + "type": [ + "null", + "boolean" + ] +} - added
Output schema / properties / data / items / properties / media / items / properties / bodyAdded value: +{ + "type": "string" +} - added
Output schema / properties / data / items / properties / media / items / properties / carousel_positionAdded value: +{ + "type": [ + "null", + "integer" + ] +} - added
Output schema / properties / data / items / properties / media / items / properties / claimed_domainAdded value: +{ + "type": "string" +} - added
Output schema / properties / data / items / properties / media / items / properties / cloaking_detectedAdded value: +{ + "type": "boolean" +} - added
Output schema / properties / data / items / properties / media / items / properties / fallback_urlsAdded value: +{ + "items": { + "type": "string" + }, + "type": [ + "null", + "array" + ] +} - added
Output schema / properties / data / items / properties / media / items / properties / is_aliveAdded value: +{ + "type": [ + "null", + "boolean" + ] +} - added
Output schema / properties / data / items / properties / media / items / properties / link_urlAdded value: +{ + "type": "string" +} - added
Output schema / properties / data / items / properties / media / items / properties / titleAdded value: +{ + "type": "string" +} - added
Output schema / properties / data / items / properties / opening_priceAdded value: +{ + "additionalProperties": false, + "properties": { + "billing_basis": { + "type": "string" + }, + "status": { + "type": "string" + }, + "tokens": { + "type": [ + "null", + "integer" + ] + }, + "unit": { + "type": "string" + } + }, + "required": [ + "status", + "tokens", + "unit", + "billing_basis" + ], + "type": [ + "null", + "object" + ] +} - added
Output schema / properties / data / items / properties / original_landing_domainAdded value: +{ + "type": "string" +} - added
Output schema / properties / data / items / properties / primary_creativeAdded value: +{ + "additionalProperties": false, + "properties": { + "body": { + "type": "string" + }, + "carousel_position": { + "type": [ + "null", + "integer" + ] + }, + "claimed_domain": { + "type": "string" + }, + "cloaking_detected": { + "type": "boolean" + }, + "expiring": { + "type": "boolean" + }, + "fallback_urls": { + "items": { + "type": "string" + }, + "type": [ + "null", + "array" + ] + }, + "is_alive": { + "type": [ + "null", + "boolean" + ] + }, + "link_url": { + "type": "string" + }, + "media_type": { + "type": "string" + }, + "snapshot_url": { + "type": "string" + }, + "thumbnail_url": { + "type": "string" + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": [ + "null", + "object" + ] +} - added
Output schema / properties / data / items / properties / primary_creative_selectionAdded value: +{ + "additionalProperties": false, + "properties": { + "carousel_position": { + "type": [ + "null", + "integer" + ] + }, + "reason": { + "type": "string" + }, + "status": { + "type": "string" + } + }, + "required": [ + "status" + ], + "type": [ + "null", + "object" + ] +}
2 tool updates
- Changed
get_usage2 fields changed- added
Output schema / properties / multilang_ad_open_tokensAdded value: +{ + "type": "integer" +} - changed
Output schema / requiredPrevious value: -[ - "plan", - "used", - "remaining", - "limit", - "window" -]New value: +[ + "plan", + "used", + "remaining", + "limit", + "window", + "multilang_ad_open_tokens" +]
- Changed
search_ads1 field changed- changed
Input schema / properties / formats / descriptionPrevious value: -"creative format filter from the visible /ads Format control: video, carousel, single or dynamic. Distinct from platforms, which filters publisher placements like facebook/instagram."New value: +"creative formats: video, carousel, single, dynamic, carousel_multilang, carousel_multilang_categorized. Multilang upload detection is independent of languages; the categorized variant additionally requires an AI category. Values combine with OR. Search costs 1 token per row; opening a multilang ad costs 50 tokens."
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.13MIT
- 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.241MIT
- AlicenseNot gradedqualityDmaintenanceProvides access to Meta Marketing API for comprehensive ad analytics and AI-powered video creative analysis.1,242 npm1MIT
Glama MCP Gateway
Add one secure layer between your agents and this server.