curl --request GET \
--url https://app.base44.com/api/apps/{app_id}/google-ads/campaigns/{campaign_id}/impact \
--header 'Authorization: Bearer <token>'import requests
url = "https://app.base44.com/api/apps/{app_id}/google-ads/campaigns/{campaign_id}/impact"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://app.base44.com/api/apps/{app_id}/google-ads/campaigns/{campaign_id}/impact', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.base44.com/api/apps/{app_id}/google-ads/campaigns/{campaign_id}/impact",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.base44.com/api/apps/{app_id}/google-ads/campaigns/{campaign_id}/impact"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://app.base44.com/api/apps/{app_id}/google-ads/campaigns/{campaign_id}/impact")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.base44.com/api/apps/{app_id}/google-ads/campaigns/{campaign_id}/impact")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"goals": {
"leads": {
"conversions": 6,
"conversions_value": 0
},
"other": {
"conversions": 0,
"conversions_value": 0
},
"sales": {
"conversions": 24,
"conversions_value": 1830
},
"shopping": {
"conversions": 9,
"conversions_value": 410
}
},
"goals_unavailable": false,
"channels": [
{
"channel": "SEARCH",
"conversions": 18
}
],
"channels_unavailable": false,
"countries": [
{
"code": "DE",
"conversions": 20
}
],
"locations": [
{
"conversions": 11,
"name": "Berlin"
}
],
"locations_unavailable": false,
"goals_configured": [
"sales"
],
"goal_actions": [
{
"category": "PURCHASE",
"conversions": 12,
"conversions_value": 960,
"family": "sales"
}
],
"channels_unresolved": 2,
"countries_unresolved": 0,
"locations_unresolved": 1
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}Get Google Ads campaign conversion breakdown
Breaks one campaign’s conversions down by goal, by the Google surface they came through, and by where the people who converted were, for a date window.
Base44 reads all three live from Google Ads as separate reads, and each one can fail on its own. A failed read still returns a 200: its *_unavailable flag is true and its lists are empty, so show that section as unavailable rather than as zero conversions.
- Goals.
goalstotals conversions and conversion value for thesales,leads,shoppingandotherfamilies, andgoal_actionslists the conversion categories behind them. A category whose conversions round to zero is left out ofgoal_actionsbut still counts ingoals, so a total can differ slightly from the sum of its rows.BEGIN_CHECKOUTandADD_TO_CARTcount asshopping, notsales, and can carry a value, so don’t addshoppingto a sales total as revenue. - Channels.
channelsranks conversions by ad network. Conversions Google reports with no resolved network, which includes every Performance Max date before 1 June 2025, are summed intochannels_unresolvedinstead. - Locations.
countriesandlocationsrank conversions by where people were, not by what they searched for. Conversions Base44 can’t name a place for are summed intocountries_unresolvedandlocations_unresolved.
The ranked lists leave out entries that round to zero. goals_configured names the goal families the app reports conversions for, so you can hide one it never tracks.
start_date and end_date are inclusive, must both be YYYY-MM-DD, and are read in the Google Ads account’s time zone. Anything else is rejected with a 400. Keep start_date on or before end_date. The campaign has to have reached Google Ads, so one with no google_campaign_id yet returns a 404.
This is limited to 30 requests a minute per app. Some workspaces have a different limit.
curl --request GET \
--url https://app.base44.com/api/apps/{app_id}/google-ads/campaigns/{campaign_id}/impact \
--header 'Authorization: Bearer <token>'import requests
url = "https://app.base44.com/api/apps/{app_id}/google-ads/campaigns/{campaign_id}/impact"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://app.base44.com/api/apps/{app_id}/google-ads/campaigns/{campaign_id}/impact', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.base44.com/api/apps/{app_id}/google-ads/campaigns/{campaign_id}/impact",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.base44.com/api/apps/{app_id}/google-ads/campaigns/{campaign_id}/impact"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://app.base44.com/api/apps/{app_id}/google-ads/campaigns/{campaign_id}/impact")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.base44.com/api/apps/{app_id}/google-ads/campaigns/{campaign_id}/impact")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"goals": {
"leads": {
"conversions": 6,
"conversions_value": 0
},
"other": {
"conversions": 0,
"conversions_value": 0
},
"sales": {
"conversions": 24,
"conversions_value": 1830
},
"shopping": {
"conversions": 9,
"conversions_value": 410
}
},
"goals_unavailable": false,
"channels": [
{
"channel": "SEARCH",
"conversions": 18
}
],
"channels_unavailable": false,
"countries": [
{
"code": "DE",
"conversions": 20
}
],
"locations": [
{
"conversions": 11,
"name": "Berlin"
}
],
"locations_unavailable": false,
"goals_configured": [
"sales"
],
"goal_actions": [
{
"category": "PURCHASE",
"conversions": 12,
"conversions_value": 960,
"family": "sales"
}
],
"channels_unresolved": 2,
"countries_unresolved": 0,
"locations_unresolved": 1
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}Authorizations
Personal access token, sent as Authorization: Bearer <token>.
Path Parameters
ID of the app whose Google Ads reporting you want to read.
Base44's ID for the campaign, as returned in id by List campaigns. This is not the Google Ads campaign ID, which is reported separately as google_campaign_id.
Query Parameters
First day of the window, as YYYY-MM-DD.
Last day of the window, inclusive, as YYYY-MM-DD.
Response
The campaign's conversions by goal, channel and location.
One campaign's conversions, broken down by goal, channel and location.
Totals per goal family, keyed by sales, leads, shopping and other. Empty when goals_unavailable is true.
Show child attributes
Show child attributes
{
"leads": { "conversions": 6, "conversions_value": 0 },
"other": { "conversions": 0, "conversions_value": 0 },
"sales": {
"conversions": 24,
"conversions_value": 1830
},
"shopping": {
"conversions": 9,
"conversions_value": 410
}
}
true when the goal read failed, so goals is empty because the numbers are missing, not zero.
false
Conversions per ad network, most first. Empty when channels_unavailable is true.
Show child attributes
Show child attributes
[{ "channel": "SEARCH", "conversions": 18 }]
true when the channel read failed, so channels is empty because the numbers are missing, not zero.
false
Conversions per country the people who converted were in, most first. Empty when locations_unavailable is true.
Show child attributes
Show child attributes
[{ "code": "DE", "conversions": 20 }]
Conversions per city, or the most specific place Google resolved, most first. Empty when locations_unavailable is true.
Show child attributes
Show child attributes
[{ "conversions": 11, "name": "Berlin" }]
true when the location read failed, so countries and locations are empty because the numbers are missing, not zero.
false
The goal families, sales and leads, that the app has an enabled conversion mapping for. Empty when Base44 can't tell, in which case treat both as possible.
["sales"]
The conversion categories behind goals, most conversions first. Categories that round to zero conversions are left out here but still count in goals. Absent when goals_unavailable is true.
Show child attributes
Show child attributes
[
{
"category": "PURCHASE",
"conversions": 12,
"conversions_value": 960,
"family": "sales"
}
]
Conversions Google reported with no resolved network, which includes every Performance Max conversion before 1 June 2025. Absent when channels_unavailable is true.
2
Conversions Base44 could not name a country for, so they are missing from countries. Absent when locations_unavailable is true.
0
Conversions Base44 could not name a place for, so they are missing from locations. Absent when locations_unavailable is true.
1
Was this page helpful?