adloop 0.19.0__tar.gz → 0.19.1__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.
- {adloop-0.19.0 → adloop-0.19.1}/PKG-INFO +5 -5
- {adloop-0.19.0 → adloop-0.19.1}/README.md +4 -4
- {adloop-0.19.0 → adloop-0.19.1}/pyproject.toml +1 -1
- {adloop-0.19.0 → adloop-0.19.1}/pyproject.toml.orig +1 -1
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/ads/custom_conversion_goals.py +0 -2
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/ads/read.py +6 -27
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/ads/write.py +0 -1
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/server.py +22 -13
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/__init__.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/__main__.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/_mcp_patches.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/ads/__init__.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/ads/ai_max.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/ads/brands.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/ads/client.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/ads/conversion_actions.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/ads/currency.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/ads/enums.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/ads/forecast.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/ads/gaql.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/ads/image_fetch.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/ads/pmax.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/ads/validate_only.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/auth.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/cli.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/config.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/crossref.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/diagnostics.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/ga4/__init__.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/ga4/client.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/ga4/reports.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/ga4/tracking.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/ga4/write.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/gsc/__init__.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/gsc/client.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/gsc/reports.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/gtm/__init__.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/gtm/client.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/gtm/read.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/merchant/__init__.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/merchant/client.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/merchant/read.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/pagespeed.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/reddit/__init__.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/reddit/auth.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/reddit/client.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/reddit/read.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/reddit/schedule.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/reddit/write.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/rules/__init__.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/rules/adloop.md +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/rules/commands/analyze-performance.md +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/rules/commands/budget-plan.md +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/rules/commands/create-ad.md +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/rules/commands/create-campaign.md +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/rules/commands/diagnose-tracking.md +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/rules/commands/optimize-campaign.md +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/rules_install.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/runtime.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/safety/__init__.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/safety/audit.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/safety/guards.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/safety/preview.py +0 -0
- {adloop-0.19.0 → adloop-0.19.1}/src/adloop/tracking.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: adloop
|
|
3
|
-
Version: 0.19.
|
|
3
|
+
Version: 0.19.1
|
|
4
4
|
Summary: The AI command center for Google Ads, Reddit Ads, GA4, and tracking code.
|
|
5
5
|
Keywords: mcp,google-ads,reddit-ads,google-analytics,ga4,cursor,marketing
|
|
6
6
|
Author: Daniel Klose
|
|
@@ -255,7 +255,7 @@ All write operations follow a **draft → preview → confirm** workflow. Nothin
|
|
|
255
255
|
| `draft_callouts` | Create campaign callout assets from 1-25 character text snippets. |
|
|
256
256
|
| `draft_structured_snippets` | Create campaign structured snippet assets using official header values and 3-10 snippet values. |
|
|
257
257
|
| `draft_image_assets` | Create campaign image assets from local files or public image URLs (PNG, JPEG, or GIF). |
|
|
258
|
-
| `draft_keywords` | Propose keyword additions with match types. Proactively checks bidding strategy —
|
|
258
|
+
| `draft_keywords` | Propose keyword additions with match types. Proactively checks bidding strategy — flags BROAD match on campaigns without Smart Bidding as dangerous in the preview. |
|
|
259
259
|
| `add_negative_keywords` | Propose negative keywords directly on a campaign |
|
|
260
260
|
| `add_negative_locations` | Propose negative geo exclusions on a campaign — exclude cities/regions while keeping broader positive targets |
|
|
261
261
|
| `draft_key_event` | Mark a GA4 event as a key event (conversion) — the fix for "fires but isn't tracked as a conversion" |
|
|
@@ -314,8 +314,8 @@ AdLoop manages real ad spend, so safety is not optional.
|
|
|
314
314
|
- **Budget caps.** Configurable maximum daily budget — the server rejects anything above the cap.
|
|
315
315
|
- **Audit log.** Every operation (including dry runs) is logged to `~/.adloop/audit.log`.
|
|
316
316
|
- **New campaigns and ads are PAUSED.** Nothing goes live without manual enablement.
|
|
317
|
-
- **Destructive ops
|
|
318
|
-
- **Broad Match
|
|
317
|
+
- **Destructive ops are flagged for double confirmation.** Removing entities or large budget increases come back with extra warnings, and the AI is instructed to confirm twice.
|
|
318
|
+
- **Broad Match without Smart Bidding caught.** The #1 cause of wasted ad spend: `draft_campaign` refuses BROAD keywords unless the campaign uses Smart Bidding, and `draft_keywords` / `draft_ad_group` mark them as dangerous in the preview, so the AI has to raise it before anything is applied.
|
|
319
319
|
- **Pre-write validation.** Before any write, the AI checks bidding strategy, conversion tracking status, and quality scores. If the campaign is fundamentally broken, AdLoop warns you instead of making things worse.
|
|
320
320
|
- **Structured error handling.** All tools return actionable error messages with hints instead of raw exceptions. Auth errors include specific re-authorization steps.
|
|
321
321
|
- **API version pinning.** The Google Ads API version is pinned to prevent silent breaking changes from library updates. `health_check` warns when a newer version is available.
|
|
@@ -578,7 +578,7 @@ What's been shipped and what's next:
|
|
|
578
578
|
- ~~PyPI package~~ ✓ — `pip install adloop`
|
|
579
579
|
- ~~[AdLoop Cloud](https://getadloop.com)~~ ✓ — the hosted version: no Google Cloud project, no API access application, connect Google in two clicks (EU-hosted, GDPR-first)
|
|
580
580
|
- ~~Headless server support~~ ✓ — manual URL copy-paste flow for servers without a browser
|
|
581
|
-
- ~~Behavioral eval suites~~ ✓ —
|
|
581
|
+
- ~~Behavioral eval suites~~ ✓ — prompt-and-expectation tests in `tests/evals/` covering read, write, tracking, and planning workflows
|
|
582
582
|
- ~~Google Tag Manager integration~~ ✓ — read tools for tags, triggers, variables, workspaces, and version history, plus the `audit_event_coverage` three-way join across codebase events, GTM tags, and GA4 actual fires
|
|
583
583
|
- **Community launch** — HN, Indie Hackers, r/cursor, Twitter
|
|
584
584
|
- **Video walkthrough**
|
|
@@ -228,7 +228,7 @@ All write operations follow a **draft → preview → confirm** workflow. Nothin
|
|
|
228
228
|
| `draft_callouts` | Create campaign callout assets from 1-25 character text snippets. |
|
|
229
229
|
| `draft_structured_snippets` | Create campaign structured snippet assets using official header values and 3-10 snippet values. |
|
|
230
230
|
| `draft_image_assets` | Create campaign image assets from local files or public image URLs (PNG, JPEG, or GIF). |
|
|
231
|
-
| `draft_keywords` | Propose keyword additions with match types. Proactively checks bidding strategy —
|
|
231
|
+
| `draft_keywords` | Propose keyword additions with match types. Proactively checks bidding strategy — flags BROAD match on campaigns without Smart Bidding as dangerous in the preview. |
|
|
232
232
|
| `add_negative_keywords` | Propose negative keywords directly on a campaign |
|
|
233
233
|
| `add_negative_locations` | Propose negative geo exclusions on a campaign — exclude cities/regions while keeping broader positive targets |
|
|
234
234
|
| `draft_key_event` | Mark a GA4 event as a key event (conversion) — the fix for "fires but isn't tracked as a conversion" |
|
|
@@ -287,8 +287,8 @@ AdLoop manages real ad spend, so safety is not optional.
|
|
|
287
287
|
- **Budget caps.** Configurable maximum daily budget — the server rejects anything above the cap.
|
|
288
288
|
- **Audit log.** Every operation (including dry runs) is logged to `~/.adloop/audit.log`.
|
|
289
289
|
- **New campaigns and ads are PAUSED.** Nothing goes live without manual enablement.
|
|
290
|
-
- **Destructive ops
|
|
291
|
-
- **Broad Match
|
|
290
|
+
- **Destructive ops are flagged for double confirmation.** Removing entities or large budget increases come back with extra warnings, and the AI is instructed to confirm twice.
|
|
291
|
+
- **Broad Match without Smart Bidding caught.** The #1 cause of wasted ad spend: `draft_campaign` refuses BROAD keywords unless the campaign uses Smart Bidding, and `draft_keywords` / `draft_ad_group` mark them as dangerous in the preview, so the AI has to raise it before anything is applied.
|
|
292
292
|
- **Pre-write validation.** Before any write, the AI checks bidding strategy, conversion tracking status, and quality scores. If the campaign is fundamentally broken, AdLoop warns you instead of making things worse.
|
|
293
293
|
- **Structured error handling.** All tools return actionable error messages with hints instead of raw exceptions. Auth errors include specific re-authorization steps.
|
|
294
294
|
- **API version pinning.** The Google Ads API version is pinned to prevent silent breaking changes from library updates. `health_check` warns when a newer version is available.
|
|
@@ -551,7 +551,7 @@ What's been shipped and what's next:
|
|
|
551
551
|
- ~~PyPI package~~ ✓ — `pip install adloop`
|
|
552
552
|
- ~~[AdLoop Cloud](https://getadloop.com)~~ ✓ — the hosted version: no Google Cloud project, no API access application, connect Google in two clicks (EU-hosted, GDPR-first)
|
|
553
553
|
- ~~Headless server support~~ ✓ — manual URL copy-paste flow for servers without a browser
|
|
554
|
-
- ~~Behavioral eval suites~~ ✓ —
|
|
554
|
+
- ~~Behavioral eval suites~~ ✓ — prompt-and-expectation tests in `tests/evals/` covering read, write, tracking, and planning workflows
|
|
555
555
|
- ~~Google Tag Manager integration~~ ✓ — read tools for tags, triggers, variables, workspaces, and version history, plus the `audit_event_coverage` three-way join across codebase events, GTM tags, and GA4 actual fires
|
|
556
556
|
- **Community launch** — HN, Indie Hackers, r/cursor, Twitter
|
|
557
557
|
- **Video walkthrough**
|
|
@@ -537,7 +537,6 @@ def draft_clear_custom_conversion_goal(
|
|
|
537
537
|
|
|
538
538
|
Call ``confirm_and_apply`` with the returned plan_id to execute.
|
|
539
539
|
"""
|
|
540
|
-
from adloop.ads.client import normalize_customer_id
|
|
541
540
|
from adloop.safety.guards import SafetyViolation, check_blocked_operation
|
|
542
541
|
from adloop.safety.preview import ChangePlan, store_plan
|
|
543
542
|
|
|
@@ -553,7 +552,6 @@ def draft_clear_custom_conversion_goal(
|
|
|
553
552
|
"details": ["campaign_id must be a numeric ID"],
|
|
554
553
|
}
|
|
555
554
|
|
|
556
|
-
cid = normalize_customer_id(customer_id or config.ads.customer_id)
|
|
557
555
|
state = _custom_goal_state(config, customer_id, campaign_id=campaign_id)
|
|
558
556
|
campaign = find_campaign(state, campaign_id)
|
|
559
557
|
if campaign is None:
|
|
@@ -373,7 +373,11 @@ def get_search_terms(
|
|
|
373
373
|
"""Get search terms report — what users actually typed before clicking ads."""
|
|
374
374
|
from adloop.ads.gaql import execute_query
|
|
375
375
|
|
|
376
|
-
|
|
376
|
+
# search_term_view requires an explicit date segment, so the default is
|
|
377
|
+
# DURING LAST_30_DAYS and a given range replaces it. ``_date_clause``
|
|
378
|
+
# prefixes its fragment with AND (the other read tools append it to a
|
|
379
|
+
# WHERE); here it is the only predicate, so the AND comes off.
|
|
380
|
+
where = _date_clause(date_range_start, date_range_end).removeprefix("AND ")
|
|
377
381
|
|
|
378
382
|
query = f"""
|
|
379
383
|
SELECT search_term_view.search_term,
|
|
@@ -381,35 +385,10 @@ def get_search_terms(
|
|
|
381
385
|
metrics.impressions, metrics.clicks,
|
|
382
386
|
metrics.cost_micros, metrics.conversions
|
|
383
387
|
FROM search_term_view
|
|
384
|
-
WHERE
|
|
385
|
-
{f"AND segments.date BETWEEN '{date_range_start}' AND '{date_range_end}'" if date_range_start and date_range_end else ""}
|
|
388
|
+
WHERE {where}
|
|
386
389
|
ORDER BY metrics.clicks DESC
|
|
387
390
|
LIMIT 200
|
|
388
391
|
"""
|
|
389
|
-
# search_term_view requires an explicit date segment, so we always
|
|
390
|
-
# include DURING LAST_30_DAYS as baseline and override if dates given.
|
|
391
|
-
if date_range_start and date_range_end:
|
|
392
|
-
query = f"""
|
|
393
|
-
SELECT search_term_view.search_term,
|
|
394
|
-
campaign.name, ad_group.name,
|
|
395
|
-
metrics.impressions, metrics.clicks,
|
|
396
|
-
metrics.cost_micros, metrics.conversions
|
|
397
|
-
FROM search_term_view
|
|
398
|
-
WHERE segments.date BETWEEN '{date_range_start}' AND '{date_range_end}'
|
|
399
|
-
ORDER BY metrics.clicks DESC
|
|
400
|
-
LIMIT 200
|
|
401
|
-
"""
|
|
402
|
-
else:
|
|
403
|
-
query = """
|
|
404
|
-
SELECT search_term_view.search_term,
|
|
405
|
-
campaign.name, ad_group.name,
|
|
406
|
-
metrics.impressions, metrics.clicks,
|
|
407
|
-
metrics.cost_micros, metrics.conversions
|
|
408
|
-
FROM search_term_view
|
|
409
|
-
WHERE segments.date DURING LAST_30_DAYS
|
|
410
|
-
ORDER BY metrics.clicks DESC
|
|
411
|
-
LIMIT 200
|
|
412
|
-
"""
|
|
413
392
|
|
|
414
393
|
rows = execute_query(config, customer_id, query)
|
|
415
394
|
currency_code = get_currency_code(config, customer_id)
|
|
@@ -3867,7 +3867,6 @@ def _apply_create_campaign(client: object, cid: str, changes: dict) -> dict:
|
|
|
3867
3867
|
results = {}
|
|
3868
3868
|
num_keywords = len(kw_list)
|
|
3869
3869
|
num_geo = len(changes.get("geo_target_ids") or [])
|
|
3870
|
-
num_lang = len(changes.get("language_ids") or [])
|
|
3871
3870
|
for i, resp in enumerate(response.mutate_operation_responses):
|
|
3872
3871
|
rn = _extract_resource_name(resp)
|
|
3873
3872
|
if rn:
|
|
@@ -13,9 +13,11 @@ from pydantic import BeforeValidator
|
|
|
13
13
|
from adloop import diagnostics
|
|
14
14
|
from adloop.runtime import current_config
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
# openWorldHint: every tool talks to an external service (Google, Reddit,
|
|
17
|
+
# a live web page), which directories such as ChatGPT's require to be said.
|
|
18
|
+
_READONLY = ToolAnnotations(readOnlyHint=True, destructiveHint=False, openWorldHint=True)
|
|
19
|
+
_WRITE = ToolAnnotations(readOnlyHint=False, destructiveHint=False, openWorldHint=True)
|
|
20
|
+
_DESTRUCTIVE = ToolAnnotations(readOnlyHint=False, destructiveHint=True, openWorldHint=True)
|
|
19
21
|
|
|
20
22
|
# Toolset taxonomy: every tool carries exactly one of these tags (or "core",
|
|
21
23
|
# which survives every ADLOOP_TOOLSETS selection — health_check and
|
|
@@ -552,6 +554,8 @@ def run_ga4_report(
|
|
|
552
554
|
|
|
553
555
|
Date formats: "today", "yesterday", "7daysAgo", "28daysAgo", "90daysAgo", or "YYYY-MM-DD".
|
|
554
556
|
If property_id is empty, uses the default from config.
|
|
557
|
+
Queries the GA4 Data API. Dimensions and metrics:
|
|
558
|
+
https://developers.google.com/analytics/devguides/reporting/data/v1/api-schema
|
|
555
559
|
"""
|
|
556
560
|
from adloop.ga4.reports import run_ga4_report as _impl
|
|
557
561
|
|
|
@@ -660,6 +664,8 @@ def run_gsc_report(
|
|
|
660
664
|
[{"filters": [{"dimension": "query", "operator": "contains",
|
|
661
665
|
"expression": "analytics"}]}]
|
|
662
666
|
limit: maximum rows to return (default 100, max 25000).
|
|
667
|
+
Queries the Search Console API:
|
|
668
|
+
https://developers.google.com/webmaster-tools/v1/searchanalytics/query
|
|
663
669
|
"""
|
|
664
670
|
from adloop.gsc.reports import run_gsc_report as _impl
|
|
665
671
|
|
|
@@ -708,8 +714,9 @@ def analyze_page_speed(url: str, strategy: str = "mobile") -> dict:
|
|
|
708
714
|
def list_merchant_accounts() -> dict:
|
|
709
715
|
"""List Google Merchant Center accounts the connected user can access.
|
|
710
716
|
|
|
711
|
-
|
|
712
|
-
|
|
717
|
+
Returns each account's numeric ID, name and whether it is a test
|
|
718
|
+
account. Call it to find the account_id for get_merchant_feed_health;
|
|
719
|
+
not needed when merchant.account_id is set in the config.
|
|
713
720
|
"""
|
|
714
721
|
from adloop.merchant.read import list_merchant_accounts as _impl
|
|
715
722
|
|
|
@@ -899,9 +906,9 @@ def get_negative_keyword_lists(
|
|
|
899
906
|
) -> dict:
|
|
900
907
|
"""List all shared negative keyword lists (SharedSets) in the account.
|
|
901
908
|
|
|
902
|
-
Returns each list's ID, name, status, and keyword count.
|
|
903
|
-
|
|
904
|
-
|
|
909
|
+
Returns each list's ID, name, status, and keyword count. Useful before
|
|
910
|
+
propose_negative_keyword_list: a suitable list may already exist and
|
|
911
|
+
just need attaching to a campaign.
|
|
905
912
|
"""
|
|
906
913
|
from adloop.ads.read import get_negative_keyword_lists as _impl
|
|
907
914
|
|
|
@@ -1543,7 +1550,8 @@ def run_gaql(
|
|
|
1543
1550
|
"""Execute an arbitrary GAQL (Google Ads Query Language) query.
|
|
1544
1551
|
|
|
1545
1552
|
Use this for advanced queries not covered by the other tools.
|
|
1546
|
-
|
|
1553
|
+
Queries the Google Ads API. GAQL syntax and fields:
|
|
1554
|
+
https://developers.google.com/google-ads/api/docs/query/overview
|
|
1547
1555
|
|
|
1548
1556
|
format: "table" (default, readable), "json" (structured), "csv" (exportable)
|
|
1549
1557
|
"""
|
|
@@ -2125,8 +2133,8 @@ def add_to_negative_keyword_list(
|
|
|
2125
2133
|
|
|
2126
2134
|
Use this when a suitable list already exists and only needs more keywords
|
|
2127
2135
|
(instead of propose_negative_keyword_list, which creates a new list).
|
|
2128
|
-
|
|
2129
|
-
|
|
2136
|
+
The shared_set_id comes from get_negative_keyword_lists;
|
|
2137
|
+
get_negative_keyword_list_keywords shows the terms already in a list.
|
|
2130
2138
|
|
|
2131
2139
|
shared_set_id: numeric ID from get_negative_keyword_lists (shared_set.id).
|
|
2132
2140
|
keywords: list of keyword strings to append (duplicates in the input list
|
|
@@ -2815,6 +2823,7 @@ def run_reddit_report(
|
|
|
2815
2823
|
(HOUR and DATE cannot be combined). Dates are account-local days.
|
|
2816
2824
|
filter: Reddit filter expression (e.g. "campaign_id==abc123").
|
|
2817
2825
|
Microcurrency fields are converted to currency amounts.
|
|
2826
|
+
Queries the Reddit Ads API: https://ads-api.reddit.com/docs/v3/
|
|
2818
2827
|
"""
|
|
2819
2828
|
from adloop.reddit.read import run_reddit_report as _impl
|
|
2820
2829
|
|
|
@@ -3734,8 +3743,8 @@ def get_brand_lists(
|
|
|
3734
3743
|
) -> dict:
|
|
3735
3744
|
"""List all brand lists (SharedSets of type BRANDS) in the account.
|
|
3736
3745
|
|
|
3737
|
-
Returns each list's ID, name, status, and member count.
|
|
3738
|
-
propose_brand_list so an existing list can be reused instead of duplicated.
|
|
3746
|
+
Returns each list's ID, name, status, and member count. Useful before
|
|
3747
|
+
propose_brand_list, so an existing list can be reused instead of duplicated.
|
|
3739
3748
|
|
|
3740
3749
|
Args:
|
|
3741
3750
|
customer_id: Ads account ID. Defaults to the configured account.
|
|
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
|
|
File without changes
|
|
File without changes
|