curl --request POST \
--url https://app.base44.com/api/apps/{app_id}/google-ads/campaigns \
--header 'Content-Type: application/json' \
--header 'api_key: <api-key>' \
--data '
{
"campaign_name": "Spring sale in Berlin",
"campaign_type": "SMART",
"daily_budget_micros": 15000000,
"landing_page": "https://example.com/spring",
"keyword_themes": [
"spring sale",
"discount furniture"
],
"geo_targets": [
"1003854"
]
}
'import requests
url = "https://app.base44.com/api/apps/{app_id}/google-ads/campaigns"
payload = {
"campaign_name": "Spring sale in Berlin",
"campaign_type": "SMART",
"daily_budget_micros": 15000000,
"landing_page": "https://example.com/spring",
"keyword_themes": ["spring sale", "discount furniture"],
"geo_targets": ["1003854"]
}
headers = {
"api_key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {api_key: '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
campaign_name: 'Spring sale in Berlin',
campaign_type: 'SMART',
daily_budget_micros: 15000000,
landing_page: 'https://example.com/spring',
keyword_themes: ['spring sale', 'discount furniture'],
geo_targets: ['1003854']
})
};
fetch('https://app.base44.com/api/apps/{app_id}/google-ads/campaigns', 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",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'campaign_name' => 'Spring sale in Berlin',
'campaign_type' => 'SMART',
'daily_budget_micros' => 15000000,
'landing_page' => 'https://example.com/spring',
'keyword_themes' => [
'spring sale',
'discount furniture'
],
'geo_targets' => [
'1003854'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"api_key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.base44.com/api/apps/{app_id}/google-ads/campaigns"
payload := strings.NewReader("{\n \"campaign_name\": \"Spring sale in Berlin\",\n \"campaign_type\": \"SMART\",\n \"daily_budget_micros\": 15000000,\n \"landing_page\": \"https://example.com/spring\",\n \"keyword_themes\": [\n \"spring sale\",\n \"discount furniture\"\n ],\n \"geo_targets\": [\n \"1003854\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("api_key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.base44.com/api/apps/{app_id}/google-ads/campaigns")
.header("api_key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"campaign_name\": \"Spring sale in Berlin\",\n \"campaign_type\": \"SMART\",\n \"daily_budget_micros\": 15000000,\n \"landing_page\": \"https://example.com/spring\",\n \"keyword_themes\": [\n \"spring sale\",\n \"discount furniture\"\n ],\n \"geo_targets\": [\n \"1003854\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.base44.com/api/apps/{app_id}/google-ads/campaigns")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["api_key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"campaign_name\": \"Spring sale in Berlin\",\n \"campaign_type\": \"SMART\",\n \"daily_budget_micros\": 15000000,\n \"landing_page\": \"https://example.com/spring\",\n \"keyword_themes\": [\n \"spring sale\",\n \"discount furniture\"\n ],\n \"geo_targets\": [\n \"1003854\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"id": "68b1c0d4e7b91d003c45a1f2",
"google_campaign_id": "21098765432",
"campaign_name": "Spring sale in Berlin",
"campaign_type": "SMART",
"status": "ENABLED",
"daily_budget_micros": 15000000,
"landing_page": "https://example.com/spring",
"geo_targets": [
"1003854"
],
"phone_number": "+493012345678",
"review_status": "REVIEWED",
"serving_status": "SERVING",
"primary_status": "ELIGIBLE",
"primary_status_reasons": [
"CAMPAIGN_BUDGET_LIMITED"
],
"created_date": "2026-08-25T11:20:00Z",
"updated_date": "2026-08-25T14:05:00Z",
"learning_ends_at": "2026-09-08T11:20:00Z"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}Create Google Ads campaign
Creates a campaign on the app’s Google Ads account and starts serving it.
This spends money. The workspace needs a payment method on file and an account in good standing. Without either, the call returns a 402 and no campaign is created. A campaign is also created paused when the app’s conversion tracking is not yet verified, so check status on the response rather than assuming it is serving.
Set campaign_type to SMART or PERFORMANCE_MAX. daily_budget_micros is in micros of the account currency and has a per-currency floor Google enforces. A budget below it comes back as a 400 carrying Google’s reason, and the campaign is not created. Advertisers in the EU must declare whether the campaign carries political advertising. The call is rejected until that declaration is on file.
Google validates the campaign as it is created, so a rejected ad text, an unreachable landing page, or a budget below the floor comes back as a 400 with Google’s own reason.
curl --request POST \
--url https://app.base44.com/api/apps/{app_id}/google-ads/campaigns \
--header 'Content-Type: application/json' \
--header 'api_key: <api-key>' \
--data '
{
"campaign_name": "Spring sale in Berlin",
"campaign_type": "SMART",
"daily_budget_micros": 15000000,
"landing_page": "https://example.com/spring",
"keyword_themes": [
"spring sale",
"discount furniture"
],
"geo_targets": [
"1003854"
]
}
'import requests
url = "https://app.base44.com/api/apps/{app_id}/google-ads/campaigns"
payload = {
"campaign_name": "Spring sale in Berlin",
"campaign_type": "SMART",
"daily_budget_micros": 15000000,
"landing_page": "https://example.com/spring",
"keyword_themes": ["spring sale", "discount furniture"],
"geo_targets": ["1003854"]
}
headers = {
"api_key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {api_key: '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
campaign_name: 'Spring sale in Berlin',
campaign_type: 'SMART',
daily_budget_micros: 15000000,
landing_page: 'https://example.com/spring',
keyword_themes: ['spring sale', 'discount furniture'],
geo_targets: ['1003854']
})
};
fetch('https://app.base44.com/api/apps/{app_id}/google-ads/campaigns', 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",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'campaign_name' => 'Spring sale in Berlin',
'campaign_type' => 'SMART',
'daily_budget_micros' => 15000000,
'landing_page' => 'https://example.com/spring',
'keyword_themes' => [
'spring sale',
'discount furniture'
],
'geo_targets' => [
'1003854'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"api_key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.base44.com/api/apps/{app_id}/google-ads/campaigns"
payload := strings.NewReader("{\n \"campaign_name\": \"Spring sale in Berlin\",\n \"campaign_type\": \"SMART\",\n \"daily_budget_micros\": 15000000,\n \"landing_page\": \"https://example.com/spring\",\n \"keyword_themes\": [\n \"spring sale\",\n \"discount furniture\"\n ],\n \"geo_targets\": [\n \"1003854\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("api_key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.base44.com/api/apps/{app_id}/google-ads/campaigns")
.header("api_key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"campaign_name\": \"Spring sale in Berlin\",\n \"campaign_type\": \"SMART\",\n \"daily_budget_micros\": 15000000,\n \"landing_page\": \"https://example.com/spring\",\n \"keyword_themes\": [\n \"spring sale\",\n \"discount furniture\"\n ],\n \"geo_targets\": [\n \"1003854\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.base44.com/api/apps/{app_id}/google-ads/campaigns")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["api_key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"campaign_name\": \"Spring sale in Berlin\",\n \"campaign_type\": \"SMART\",\n \"daily_budget_micros\": 15000000,\n \"landing_page\": \"https://example.com/spring\",\n \"keyword_themes\": [\n \"spring sale\",\n \"discount furniture\"\n ],\n \"geo_targets\": [\n \"1003854\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"id": "68b1c0d4e7b91d003c45a1f2",
"google_campaign_id": "21098765432",
"campaign_name": "Spring sale in Berlin",
"campaign_type": "SMART",
"status": "ENABLED",
"daily_budget_micros": 15000000,
"landing_page": "https://example.com/spring",
"geo_targets": [
"1003854"
],
"phone_number": "+493012345678",
"review_status": "REVIEWED",
"serving_status": "SERVING",
"primary_status": "ELIGIBLE",
"primary_status_reasons": [
"CAMPAIGN_BUDGET_LIMITED"
],
"created_date": "2026-08-25T11:20:00Z",
"updated_date": "2026-08-25T14:05:00Z",
"learning_ends_at": "2026-09-08T11:20:00Z"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}Authorizations
Personal API key.
Path Parameters
ID of the app to advertise.
Body
Name for the campaign.
Daily budget in micros of the account currency, so 15000000 is 15.00. Google enforces a per-currency minimum and rejects anything below it with a 400.
Which campaign type to create.
SMART, PERFORMANCE_MAX URL the ads send clicks to. Google rejects the campaign if it cannot reach this page.
URL of a square logo image. Required for a PERFORMANCE_MAX campaign and rejected with a 400 when missing; ignored for SMART. Google must be able to fetch it.
Themes to match searches on, for a SMART campaign.
Ad headlines. Google reviews these against its advertising policies.
Ad description lines. Google reviews these against its advertising policies.
Google Ads geo target constant IDs to target.
Business name shown in the ad.
Phone number shown in the ad. On PERFORMANCE_MAX it becomes a call asset and must be in international format with the country code (e.g. +18506168085) — Google needs the country alongside the number, and any other format is rejected with a 400. SMART campaigns take it as typed. Omit for no phone.
Whether the campaign carries political advertising. Required before an EU advertiser can create a campaign.
Campaign settings. language_code is the only key this API commits to; anything else is passed through undocumented.
Show child attributes
Show child attributes
Response
Successful Response
The campaign fields this API commits to.
Base44's ID for the campaign. Pass this as campaign_id on the other campaign endpoints.
"68b1c0d4e7b91d003c45a1f2"
The campaign's ID in Google Ads. Empty while a just-created campaign is still being pushed to Google.
"21098765432"
Name shown for the campaign.
"Spring sale in Berlin"
Campaign type. Base44 creates SMART and PERFORMANCE_MAX. The other values appear only on campaigns created outside Base44 and synced in.
SMART, PERFORMANCE_MAX, SEARCH, DISPLAY, SHOPPING, VIDEO, DEMAND_GEN, LOCAL, UNKNOWN "SMART"
Serving state in Base44's cache. A status of REMOVED is a deleted campaign, which Google keeps for reporting.
ENABLED, PAUSED, REMOVED, UNKNOWN "ENABLED"
Daily budget in micros of the account currency, where 1,000,000 micros is one unit, so 15000000 is 15.00.
15000000
URL the ads send clicks to.
"https://example.com/spring"
Google Ads geo target constant IDs the campaign targets.
["1003854"]
Campaign phone number, empty when the campaign has none. E.164 on PERFORMANCE_MAX (where it is a call asset); as typed on SMART.
"+493012345678"
Google's policy review status, passed through as Google reports it (REVIEWED, UNDER_REVIEW, …). Empty until the first sync after creation.
"REVIEWED"
Google's serving status, passed through as Google reports it. Can still report review gating after policy review clears, so read it alongside review_status.
"SERVING"
Google's summary of whether the campaign is serving well, passed through as Google reports it (ELIGIBLE, LIMITED, NOT_SERVING, …).
"ELIGIBLE"
Google's reasons behind primary_status, for example why a campaign is limited. Empty when there is nothing to explain.
["CAMPAIGN_BUDGET_LIMITED"]
When the campaign was created in Base44.
"2026-08-25T11:20:00Z"
When Base44 last changed its record of the campaign.
"2026-08-25T14:05:00Z"
When Smart Bidding's learning period is expected to end, about 14 days after creation, or null on a campaign that has not started learning.
"2026-09-08T11:20:00Z"
Was this page helpful?