meta-ads-mcp 0.7.1__tar.gz → 0.7.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/PKG-INFO +1 -1
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_mcp/__init__.py +1 -1
- meta_ads_mcp-0.7.3/meta_ads_mcp/core/ads_library.py +74 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_mcp/core/authentication.py +57 -39
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/pyproject.toml +1 -1
- meta_ads_mcp-0.7.1/meta_ads_mcp/core/ads_library.py +0 -69
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/.github/workflows/publish.yml +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/.github/workflows/test.yml +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/.gitignore +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/CUSTOM_META_APP.md +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/Dockerfile +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/LICENSE +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/LOCAL_INSTALLATION.md +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/META_API_NOTES.md +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/README.md +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/RELEASE.md +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/STREAMABLE_HTTP_SETUP.md +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/examples/README.md +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/examples/example_http_client.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/future_improvements.md +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/images/meta-ads-example.png +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_auth.sh +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_mcp/__main__.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_mcp/core/__init__.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_mcp/core/accounts.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_mcp/core/ads.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_mcp/core/adsets.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_mcp/core/api.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_mcp/core/auth.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_mcp/core/budget_schedules.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_mcp/core/callback_server.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_mcp/core/campaigns.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_mcp/core/duplication.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_mcp/core/http_auth_integration.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_mcp/core/insights.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_mcp/core/openai_deep_research.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_mcp/core/pipeboard_auth.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_mcp/core/reports.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_mcp/core/resources.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_mcp/core/server.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_mcp/core/targeting.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/meta_ads_mcp/core/utils.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/requirements.txt +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/setup.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/smithery.yaml +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/tests/README.md +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/tests/README_REGRESSION_TESTS.md +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/tests/__init__.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/tests/conftest.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/tests/test_account_search.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/tests/test_duplication.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/tests/test_duplication_regression.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/tests/test_get_ad_creatives_fix.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/tests/test_get_ad_image_regression.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/tests/test_http_transport.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/tests/test_integration_openai_mcp.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/tests/test_openai.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/tests/test_openai_mcp_deep_research.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/tests/test_targeting.py +0 -0
- {meta_ads_mcp-0.7.1 → meta_ads_mcp-0.7.3}/tests/test_targeting_search_e2e.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meta-ads-mcp
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.3
|
|
4
4
|
Summary: Model Context Protocol (MCP) plugin for interacting with Meta Ads API
|
|
5
5
|
Project-URL: Homepage, https://github.com/pipeboard-co/meta-ads-mcp
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/pipeboard-co/meta-ads-mcp/issues
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Adds Library-related functionality for Meta Ads API."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from typing import Optional, List, Dict, Any
|
|
6
|
+
from .api import meta_api_tool, make_api_request
|
|
7
|
+
from .server import mcp_server
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# Only register the search_ads_archive function if the environment variable is NOT set
|
|
11
|
+
DISABLE_ADS_LIBRARY = bool(os.environ.get("META_ADS_DISABLE_ADS_LIBRARY", ""))
|
|
12
|
+
|
|
13
|
+
if not DISABLE_ADS_LIBRARY:
|
|
14
|
+
@mcp_server.tool()
|
|
15
|
+
@meta_api_tool
|
|
16
|
+
async def search_ads_archive(
|
|
17
|
+
access_token: str = None,
|
|
18
|
+
search_terms: str = None,
|
|
19
|
+
ad_type: str = "ALL",
|
|
20
|
+
ad_reached_countries: List[str] = None,
|
|
21
|
+
limit: int = 25, # Default limit, adjust as needed
|
|
22
|
+
fields: str = "ad_creation_time,ad_creative_body,ad_creative_link_caption,ad_creative_link_description,ad_creative_link_title,ad_delivery_start_time,ad_delivery_stop_time,ad_snapshot_url,currency,demographic_distribution,funding_entity,impressions,page_id,page_name,publisher_platform,region_distribution,spend"
|
|
23
|
+
) -> str:
|
|
24
|
+
"""
|
|
25
|
+
Search the Facebook Ads Library archive.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
access_token: Meta API access token (optional - will use cached token if not provided).
|
|
29
|
+
search_terms: The search query for ads.
|
|
30
|
+
ad_type: Type of ads to search for (e.g., POLITICAL_AND_ISSUE_ADS, HOUSING_ADS, ALL).
|
|
31
|
+
ad_reached_countries: List of country codes (e.g., ["US", "GB"]).
|
|
32
|
+
limit: Maximum number of ads to return.
|
|
33
|
+
fields: Comma-separated string of fields to retrieve for each ad.
|
|
34
|
+
|
|
35
|
+
Example Usage via curl equivalent:
|
|
36
|
+
curl -G \\
|
|
37
|
+
-d "search_terms='california'" \\
|
|
38
|
+
-d "ad_type=POLITICAL_AND_ISSUE_ADS" \\
|
|
39
|
+
-d "ad_reached_countries=['US']" \\
|
|
40
|
+
-d "fields=ad_snapshot_url,spend" \\
|
|
41
|
+
-d "access_token=<ACCESS_TOKEN>" \\
|
|
42
|
+
"https://graph.facebook.com/<API_VERSION>/ads_archive"
|
|
43
|
+
"""
|
|
44
|
+
if not access_token:
|
|
45
|
+
# Attempt to get token implicitly if not provided - meta_api_tool handles this
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
if not search_terms:
|
|
49
|
+
return json.dumps({"error": "search_terms parameter is required"}, indent=2)
|
|
50
|
+
|
|
51
|
+
if not ad_reached_countries:
|
|
52
|
+
return json.dumps({"error": "ad_reached_countries parameter is required"}, indent=2)
|
|
53
|
+
|
|
54
|
+
endpoint = "ads_archive"
|
|
55
|
+
params = {
|
|
56
|
+
"search_terms": search_terms,
|
|
57
|
+
"ad_type": ad_type,
|
|
58
|
+
"ad_reached_countries": json.dumps(ad_reached_countries), # API expects a JSON array string
|
|
59
|
+
"limit": limit,
|
|
60
|
+
"fields": fields,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
data = await make_api_request(endpoint, access_token, params, method="GET")
|
|
65
|
+
return json.dumps(data, indent=2)
|
|
66
|
+
except Exception as e:
|
|
67
|
+
error_msg = str(e)
|
|
68
|
+
# Consider logging the full error for debugging
|
|
69
|
+
# print(f"Error calling Ads Library API: {error_msg}")
|
|
70
|
+
return json.dumps({
|
|
71
|
+
"error": "Failed to search ads archive",
|
|
72
|
+
"details": error_msg,
|
|
73
|
+
"params_sent": {k: v for k, v in params.items() if k != 'access_token'} # Avoid logging token
|
|
74
|
+
}, indent=2)
|
|
@@ -60,18 +60,22 @@ async def get_login_link(access_token: str = None) -> str:
|
|
|
60
60
|
# If an access token was provided, this is likely a test - return success
|
|
61
61
|
if access_token:
|
|
62
62
|
return json.dumps({
|
|
63
|
-
"message": "
|
|
64
|
-
"
|
|
65
|
-
"
|
|
63
|
+
"message": "✅ Authentication Token Provided",
|
|
64
|
+
"status": "Using provided access token for authentication",
|
|
65
|
+
"token_info": f"Token preview: {access_token[:10]}...",
|
|
66
|
+
"authentication_method": "manual_token",
|
|
67
|
+
"ready_to_use": "You can now use all Meta Ads MCP tools and commands."
|
|
66
68
|
}, indent=2)
|
|
67
69
|
|
|
68
70
|
# Check if Pipeboard token is working
|
|
69
71
|
token = pipeboard_auth_manager.get_access_token()
|
|
70
72
|
if token:
|
|
71
73
|
return json.dumps({
|
|
72
|
-
"message": "Already
|
|
73
|
-
"
|
|
74
|
-
"
|
|
74
|
+
"message": "✅ Already Authenticated",
|
|
75
|
+
"status": "You're successfully authenticated with Meta Ads via Pipeboard!",
|
|
76
|
+
"token_info": f"Token preview: {token[:10]}...",
|
|
77
|
+
"authentication_method": "pipeboard_token",
|
|
78
|
+
"ready_to_use": "You can now use all Meta Ads MCP tools and commands."
|
|
75
79
|
}, indent=2)
|
|
76
80
|
|
|
77
81
|
# Start Pipeboard auth flow
|
|
@@ -80,39 +84,51 @@ async def get_login_link(access_token: str = None) -> str:
|
|
|
80
84
|
|
|
81
85
|
if login_url:
|
|
82
86
|
return json.dumps({
|
|
87
|
+
"message": "🔗 Click to Authenticate",
|
|
83
88
|
"login_url": login_url,
|
|
84
|
-
"markdown_link": f"[
|
|
85
|
-
"
|
|
86
|
-
"instructions_for_llm": "You must present this link as clickable Markdown to the user using the markdown_link format provided.",
|
|
89
|
+
"markdown_link": f"[🚀 Authenticate with Meta Ads]({login_url})",
|
|
90
|
+
"instructions": "Click the link above to complete authentication with Meta Ads.",
|
|
87
91
|
"authentication_method": "pipeboard_oauth",
|
|
88
|
-
"
|
|
92
|
+
"what_happens_next": "After clicking, you'll be redirected to Meta's authentication page. Once completed, your token will be automatically saved.",
|
|
93
|
+
"token_duration": "Your token will be valid for approximately 60 days."
|
|
89
94
|
}, indent=2)
|
|
90
95
|
else:
|
|
91
96
|
return json.dumps({
|
|
92
|
-
"
|
|
97
|
+
"message": "❌ Authentication Error",
|
|
98
|
+
"error": "Could not generate authentication URL from Pipeboard",
|
|
99
|
+
"troubleshooting": [
|
|
100
|
+
"Check that your PIPEBOARD_API_TOKEN is valid",
|
|
101
|
+
"Ensure the Pipeboard service is accessible",
|
|
102
|
+
"Try again in a few moments"
|
|
103
|
+
],
|
|
93
104
|
"authentication_method": "pipeboard_oauth_failed"
|
|
94
105
|
}, indent=2)
|
|
95
106
|
|
|
96
107
|
except Exception as e:
|
|
97
108
|
logger.error(f"Error initiating Pipeboard auth flow: {e}")
|
|
98
109
|
return json.dumps({
|
|
110
|
+
"message": "❌ Pipeboard Authentication Error",
|
|
99
111
|
"error": f"Failed to initiate Pipeboard authentication: {str(e)}",
|
|
100
|
-
"
|
|
101
|
-
|
|
112
|
+
"troubleshooting": [
|
|
113
|
+
"✅ Check that PIPEBOARD_API_TOKEN environment variable is set correctly",
|
|
114
|
+
"🌐 Verify that pipeboard.co is accessible from your network",
|
|
115
|
+
"🔄 Try refreshing your Pipeboard API token",
|
|
116
|
+
"⏰ Wait a moment and try again"
|
|
117
|
+
],
|
|
118
|
+
"get_help": "Contact support if the issue persists",
|
|
119
|
+
"authentication_method": "pipeboard_error"
|
|
102
120
|
}, indent=2)
|
|
103
121
|
elif callback_server_disabled:
|
|
104
122
|
# Production OAuth flow - use Pipeboard OAuth endpoints directly
|
|
105
123
|
logger.info("Production OAuth flow - using Pipeboard OAuth endpoints")
|
|
106
124
|
|
|
107
125
|
return json.dumps({
|
|
108
|
-
"
|
|
109
|
-
"
|
|
110
|
-
"
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"
|
|
114
|
-
"authentication_method": "production_oauth",
|
|
115
|
-
"note": "For manual authentication, clients need to register with Pipeboard OAuth service first"
|
|
126
|
+
"message": "🔐 Authentication Required",
|
|
127
|
+
"instructions": "Please sign in to your Pipeboard account to authenticate with Meta Ads.",
|
|
128
|
+
"sign_in_url": "https://pipeboard.co/auth/signin",
|
|
129
|
+
"markdown_link": "[🚀 Sign in to Pipeboard](https://pipeboard.co/auth/signin)",
|
|
130
|
+
"what_to_do": "Click the link above to sign in to your Pipeboard account and complete authentication.",
|
|
131
|
+
"authentication_method": "production_oauth"
|
|
116
132
|
}, indent=2)
|
|
117
133
|
else:
|
|
118
134
|
# Original Meta authentication flow (development/local)
|
|
@@ -124,12 +140,13 @@ async def get_login_link(access_token: str = None) -> str:
|
|
|
124
140
|
if cached_token and not access_token:
|
|
125
141
|
logger.info("get_login_link called with existing valid token")
|
|
126
142
|
return json.dumps({
|
|
127
|
-
"message": "Already
|
|
128
|
-
"
|
|
129
|
-
"
|
|
143
|
+
"message": "✅ Already Authenticated",
|
|
144
|
+
"status": "You're successfully authenticated with Meta Ads!",
|
|
145
|
+
"token_info": f"Token preview: {cached_token[:10]}...",
|
|
130
146
|
"created_at": auth_manager.token_info.created_at if hasattr(auth_manager, "token_info") else None,
|
|
131
147
|
"expires_in": auth_manager.token_info.expires_in if hasattr(auth_manager, "token_info") else None,
|
|
132
|
-
"authentication_method": "meta_oauth"
|
|
148
|
+
"authentication_method": "meta_oauth",
|
|
149
|
+
"ready_to_use": "You can now use all Meta Ads MCP tools and commands."
|
|
133
150
|
}, indent=2)
|
|
134
151
|
|
|
135
152
|
# IMPORTANT: Start the callback server first by calling our helper function
|
|
@@ -147,9 +164,14 @@ async def get_login_link(access_token: str = None) -> str:
|
|
|
147
164
|
except Exception as e:
|
|
148
165
|
logger.error(f"Failed to start callback server: {e}")
|
|
149
166
|
return json.dumps({
|
|
150
|
-
"
|
|
151
|
-
"
|
|
152
|
-
"
|
|
167
|
+
"message": "❌ Local Authentication Unavailable",
|
|
168
|
+
"error": "Cannot start local callback server for authentication",
|
|
169
|
+
"reason": str(e),
|
|
170
|
+
"solutions": [
|
|
171
|
+
"🌐 Use Pipeboard authentication: Set PIPEBOARD_API_TOKEN environment variable",
|
|
172
|
+
"🔑 Use direct token: Set META_ACCESS_TOKEN environment variable",
|
|
173
|
+
"🔧 Check if another service is using the required ports"
|
|
174
|
+
],
|
|
153
175
|
"authentication_method": "meta_oauth_disabled"
|
|
154
176
|
}, indent=2)
|
|
155
177
|
|
|
@@ -159,19 +181,15 @@ async def get_login_link(access_token: str = None) -> str:
|
|
|
159
181
|
|
|
160
182
|
# Return a special format that helps the LLM format the response properly
|
|
161
183
|
response = {
|
|
184
|
+
"message": "🔗 Click to Authenticate",
|
|
162
185
|
"login_url": login_url,
|
|
163
|
-
"
|
|
164
|
-
"
|
|
165
|
-
"
|
|
166
|
-
"
|
|
167
|
-
"instructions_for_llm": "You must present this link as clickable Markdown to the user using the markdown_link format provided.",
|
|
168
|
-
"token_exchange": "enabled" if token_exchange_supported else "disabled",
|
|
169
|
-
"token_duration": token_duration,
|
|
186
|
+
"markdown_link": f"[🚀 Authenticate with Meta Ads]({login_url})",
|
|
187
|
+
"instructions": "Click the link above to authenticate with Meta Ads.",
|
|
188
|
+
"server_info": f"Local callback server running on port {port}",
|
|
189
|
+
"token_duration": f"Your token will be valid for approximately {token_duration}",
|
|
170
190
|
"authentication_method": "meta_oauth",
|
|
171
|
-
"
|
|
172
|
-
|
|
173
|
-
" For direct Meta authentication, long-lived tokens require META_APP_SECRET. Consider using Pipeboard authentication instead (60-day tokens by default)."),
|
|
174
|
-
"note": "After authenticating, the token will be automatically saved."
|
|
191
|
+
"what_happens_next": "After clicking, you'll be redirected to Meta's authentication page. Once completed, your token will be automatically saved.",
|
|
192
|
+
"security_note": "This uses a secure local callback server for development purposes."
|
|
175
193
|
}
|
|
176
194
|
|
|
177
195
|
# Wait a moment to ensure the server is fully started
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
"""Adds Library-related functionality for Meta Ads API."""
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
from typing import Optional, List, Dict, Any
|
|
5
|
-
from .api import meta_api_tool, make_api_request
|
|
6
|
-
from .server import mcp_server
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@mcp_server.tool()
|
|
10
|
-
@meta_api_tool
|
|
11
|
-
async def search_ads_archive(
|
|
12
|
-
access_token: str = None,
|
|
13
|
-
search_terms: str = None,
|
|
14
|
-
ad_type: str = "ALL",
|
|
15
|
-
ad_reached_countries: List[str] = None,
|
|
16
|
-
limit: int = 25, # Default limit, adjust as needed
|
|
17
|
-
fields: str = "ad_creation_time,ad_creative_body,ad_creative_link_caption,ad_creative_link_description,ad_creative_link_title,ad_delivery_start_time,ad_delivery_stop_time,ad_snapshot_url,currency,demographic_distribution,funding_entity,impressions,page_id,page_name,publisher_platform,region_distribution,spend"
|
|
18
|
-
) -> str:
|
|
19
|
-
"""
|
|
20
|
-
Search the Facebook Ads Library archive.
|
|
21
|
-
|
|
22
|
-
Args:
|
|
23
|
-
access_token: Meta API access token (optional - will use cached token if not provided).
|
|
24
|
-
search_terms: The search query for ads.
|
|
25
|
-
ad_type: Type of ads to search for (e.g., POLITICAL_AND_ISSUE_ADS, HOUSING_ADS, ALL).
|
|
26
|
-
ad_reached_countries: List of country codes (e.g., ["US", "GB"]).
|
|
27
|
-
limit: Maximum number of ads to return.
|
|
28
|
-
fields: Comma-separated string of fields to retrieve for each ad.
|
|
29
|
-
|
|
30
|
-
Example Usage via curl equivalent:
|
|
31
|
-
curl -G \\
|
|
32
|
-
-d "search_terms='california'" \\
|
|
33
|
-
-d "ad_type=POLITICAL_AND_ISSUE_ADS" \\
|
|
34
|
-
-d "ad_reached_countries=['US']" \\
|
|
35
|
-
-d "fields=ad_snapshot_url,spend" \\
|
|
36
|
-
-d "access_token=<ACCESS_TOKEN>" \\
|
|
37
|
-
"https://graph.facebook.com/<API_VERSION>/ads_archive"
|
|
38
|
-
"""
|
|
39
|
-
if not access_token:
|
|
40
|
-
# Attempt to get token implicitly if not provided - meta_api_tool handles this
|
|
41
|
-
pass
|
|
42
|
-
|
|
43
|
-
if not search_terms:
|
|
44
|
-
return json.dumps({"error": "search_terms parameter is required"}, indent=2)
|
|
45
|
-
|
|
46
|
-
if not ad_reached_countries:
|
|
47
|
-
return json.dumps({"error": "ad_reached_countries parameter is required"}, indent=2)
|
|
48
|
-
|
|
49
|
-
endpoint = "ads_archive"
|
|
50
|
-
params = {
|
|
51
|
-
"search_terms": search_terms,
|
|
52
|
-
"ad_type": ad_type,
|
|
53
|
-
"ad_reached_countries": json.dumps(ad_reached_countries), # API expects a JSON array string
|
|
54
|
-
"limit": limit,
|
|
55
|
-
"fields": fields,
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
try:
|
|
59
|
-
data = await make_api_request(endpoint, access_token, params, method="GET")
|
|
60
|
-
return json.dumps(data, indent=2)
|
|
61
|
-
except Exception as e:
|
|
62
|
-
error_msg = str(e)
|
|
63
|
-
# Consider logging the full error for debugging
|
|
64
|
-
# print(f"Error calling Ads Library API: {error_msg}")
|
|
65
|
-
return json.dumps({
|
|
66
|
-
"error": "Failed to search ads archive",
|
|
67
|
-
"details": error_msg,
|
|
68
|
-
"params_sent": {k: v for k, v in params.items() if k != 'access_token'} # Avoid logging token
|
|
69
|
-
}, indent=2)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|