Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Google Ads Laravel Package

jeffersongoncalves/laravel-google-ads

Laravel package for integrating Google Ads, with configuration and tools to authenticate and interact with the Google Ads API from your application. Simplifies setup and usage within Laravel projects.

View on GitHub
Deep Wiki
Context7

Getting Started

This is the initial v1.0.0 release, so treat the public API as young: pin the version ("vendor/google-ads-laravel": "^1.0") and read the README + source before building abstractions on top of it.

Minimal setup:

  1. Install and publish config:
    composer require vendor/google-ads-laravel:^1.0
    php artisan vendor:publish --tag=google-ads-config
    
  2. Populate credentials in .env — typically a developer token, OAuth client ID/secret, refresh token, and (for MCC access) a login customer ID:
    GOOGLE_ADS_DEVELOPER_TOKEN=
    GOOGLE_ADS_CLIENT_ID=
    GOOGLE_ADS_CLIENT_SECRET=
    GOOGLE_ADS_REFRESH_TOKEN=
    GOOGLE_ADS_LOGIN_CUSTOMER_ID=
    
  3. Resolve the client from the container (facade or injected GoogleAdsClient — check the service provider for the exact binding name) and pass a customer ID without dashes.

Where to look first, in order: the package README, config/google-ads.php, the service provider (to see what is bound and whether the client is a singleton), then the GoogleAdsClient class itself for method signatures and return types.

First useful use case — an artisan command that proves credentials work end-to-end:

$client = app(GoogleAdsClient::class);

$account = $client->accountInfo($customerId);      // confirms auth + MCC header
$campaigns = $client->campaigns($customerId);      // confirms GAQL plumbing

If accountInfo() succeeds but campaigns() returns empty, you almost certainly have a customer-ID or MCC-header problem rather than a code problem (see Gotchas).

Implementation Patterns

Wrap the client behind your own service. Don't scatter app(GoogleAdsClient::class) through controllers. Create a thin GoogleAdsService that injects the client and exposes the handful of operations your app actually performs. This gives you one place to add logging, retries, and account-ID resolution, and one place to adapt if the package's method signatures shift in a 1.x minor.

Lean on raw GAQL for anything not covered. The release covers search, account info, campaigns, ad groups, keywords, and budgets — but reporting needs will outgrow that immediately. Use the search()/GAQL method for one-off queries and keep the query strings in dedicated classes or constants rather than inline heredocs:

$rows = $client->search($customerId, <<<'GAQL'
    SELECT campaign.id, campaign.name, metrics.cost_micros, metrics.conversions
    FROM campaign
    WHERE segments.date DURING LAST_30_DAYS
    ORDER BY metrics.cost_micros DESC
GAQL);

Separate read paths from write paths. Listing and performance methods are safe to call from request handlers (with caching). Pause/enable and budget updates are not — route them through queued jobs so a slow or rate-limited Google response never blocks a request, and so retries are handled by the queue rather than the user.

Multi-account / multi-tenant loops. Most agencies and SaaS products manage many customer IDs. Build a repository that iterates accounts, and cache per-account metadata (currency, timezone, name) aggressively — it changes rarely and is needed on nearly every screen:

Cache::remember("gads:account:{$customerId}", now()->addHours(12), fn () =>
    $client->accountInfo($customerId)
);

Campaign status changes deserve an audit trail. Pause/enable is a one-line call but an operationally significant action. Wrap it:

DB::transaction(function () use ($client, $customerId, $campaignId) {
    $client->pauseCampaign($customerId, $campaignId);
    CampaignAudit::create([...]);   // who, what, when, previous status
});

Note the transaction only guards your database — if the Google call succeeds and the insert fails, you have a silent divergence. Log the Google response first, then persist.

Budget updates need guardrails, not just a method call. A budget object is shared: any campaign pointing at it changes spend when you update it. Before calling updateBudget(), load the campaigns attached to that budget and require explicit confirmation if more than one is attached. Treat the new amount as a percentage-change check (reject >X% swings without a second approval) — this is the single easiest way to avoid a very expensive typo.

Testing. Don't hit the API in tests. Bind a fake implementation of your own GoogleAdsService interface in the container, and keep a small set of recorded GAQL response fixtures for parsing tests. If the package ships a contract/interface, bind against that instead of the concrete class so you can swap it.

Gotchas and Tips

  • Developer token access level. New Google Ads API tokens are test-only and will only return data for test accounts. If every call returns empty results against a real account, check this before debugging code. Basic-level approval is a separate application.
  • login-customer-id is the classic silent failure. Manager (MCC) accounts must send the manager's customer ID in the header while the request targets the child account. Symptom: USER_PERMISSION_DENIED or empty result sets. Verify the config value is the manager ID, not the child.
  • Customer IDs: strip the dashes. 123-456-7890 is the display format; the API wants 1234567890. Normalise once at your boundary.
  • Budgets are in micros. 1000000 micros = 1 unit of the account currency. Mixing units is the most common cause of "why is my budget 1,000,000× too large."
  • Currency and timezone are per-account, not per-request. Reporting numbers won't match your local dashboard if you assume a single currency. Store the account currency alongside cached figures.
  • Quota and rate limits are real. Bulk operations over many campaigns will hit them. Chunk your loops, add backoff, and prefer queued jobs with ->delay() between batches over tight foreach loops.
  • Pause/enable ≠ delete. Status changes are reversible and leave the entity in the account. If your UI implies deletion, users will be confused when paused campaigns reappear in listings — filter by status explicitly.
  • Error surfaces. Google errors carry a request ID that support will ask for. Catch the package's exception type, log requestId plus the customer ID and the operation, then rethrow a domain exception your app understands.
  • Config caching. If you deploy with php artisan config:cache, env changes require a re-cache. Credential rotation that "didn't take" is usually this.
  • Version pinning. v1.0.0 means the surface is settled but unproven. Pin to ^1.0 and read the changelog on every minor bump; expect method names around reporting to be the most likely to churn.
  • Extension points. The service provider is the place to look for swapping the HTTP client or binding a decorated client. If the package registers a singleton, remember it's shared for the request — don't mutate per-account state on it (e.g. setting a customer ID as a property) between calls in a loop.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings