Every 30 minutes
Create Facebook ads from rows in a sheet
Each ad you describe in a sheet row, with copy, image, audience and budget, is created in Ads Manager as a paused ad, with its ids written back.
- Runs
- Every 30 minutes
- Apps
- Facebook Pages, Google Sheets
- AI
- Not needed
- Access
- ads_management; Sheet edit
Send this to your assistant
Create a ClickRuntime automation: every 15 minutes, check my Ad Builder sheet for rows marked Ready. Create each one in my Meta ad account as a paused campaign, ad set and ad using the row's copy, image, audience and budget, and write the ad id back to the row.
First time? Adding ClickRuntime to your assistant takes about two minutes.
Why use it
Why teams use this automation
A marketer tests lots of ad variations for an online course: different headlines, images and audiences. Building each one by clicking through Ads Manager takes ten minutes and invites mistakes in budgets and targeting.
Variations are now written as rows in a sheet, which is quicker to plan and easy to review with a colleague. Rows marked ready are built in Ads Manager as paused ads, so nothing spends money until the marketer checks and switches them on.
What you get out of it
Plan many variations quickly
Writing rows in a sheet is much faster than clicking through Ads Manager for each version.
Fewer setup mistakes
Budgets, audiences and copy are copied exactly from the row, the same way every time.
Nothing spends until you say so
Every ad is created paused, so you review it in Ads Manager before turning it on.
Ids written back for tracking
The sheet records which ad each row became, which makes reporting later straightforward.
How it works
What happens, step by step
Every 15 minutes, ClickRuntime checks your ad sheet for rows marked Ready.
It uploads each row's image to your ad account.
It creates a paused campaign, ad set and ad with the row's copy, audience and budget.
The new ad id is written to the row and the status changes to Created, so it's never built twice.
Before it runs live
- Connect Facebook Pages
- Connect your Google Sheets account and pick the spreadsheets it may use
What you get
Build your own
Need it to work differently?
Your own rules
Conditions, thresholds, filters and exceptions, written the way you'd explain them to a colleague.
Any number of apps
One automation can read from one app and update several others. Over 248 apps are ready, and anything with an API works too.
AI where it helps
Summaries, classification, extraction and drafts use ClickRuntime's built-in AI, with no extra key.
On a schedule or on each event
Runs as often as every 5 minutes, or the moment an app sends an event, and keeps track of what it already handled.
Changes you can ask for
- Act only on the items that match your own rules, and skip the rest
- Add an AI step, for example to summarize, sort or draft a reply
- Add another app, for example also send it to Slack
- Combine it with other Facebook Pages and Google Sheets steps into one automation
Under the hood
The code that runs this automation
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
from clickruntime import on_trigger, credentials, http, report_fetched
ACT, PAGE_ID, SHEET_ID = "act_1234567890", "1234567890", "your-spreadsheet-id"
G = f"https://graph.facebook.com/v21.0/{ACT}"
@on_trigger
def run(event):
values = build("sheets", "v4", credentials=Credentials(credentials.connection("google", "work")), cache_discovery=False).spreadsheets().values()
rows = values.get(spreadsheetId=SHEET_ID, range="Ads!A2:I").execute().get("values", [])
ready = [(i + 2, r) for i, r in enumerate(rows) if len(r) >= 9 and r[8] == "Ready"]
report_fetched(len(ready))
if not ready:
return {"summary": "No ready rows"}
t = {"access_token": credentials.connection("facebook", "work")}
for n, (name, headline, text, image, link, budget, country, age, _) in ready:
img = http.post(f"{G}/adimages", data={**t, "url": image}).json()["images"]
camp = http.post(f"{G}/campaigns", data={**t, "name": name, "objective": "OUTCOME_TRAFFIC", "status": "PAUSED", "special_ad_categories": "[]"}).json()["id"]
adset = http.post(f"{G}/adsets", data={**t, "name": name, "campaign_id": camp, "daily_budget": int(float(budget) * 100), "billing_event": "IMPRESSIONS",
"optimization_goal": "LINK_CLICKS", "status": "PAUSED", "targeting": f'{{"geo_locations":{{"countries":["{country}"]}},"age_min":{age}}}'}).json()["id"]
story = {"page_id": PAGE_ID, "link_data": {"link": link, "message": text, "name": headline, "image_hash": next(iter(img.values()))["hash"]}}
creative = http.post(f"{G}/adcreatives", json={**t, "object_story_spec": story}).json()["id"]
ad = http.post(f"{G}/ads", data={**t, "name": name, "adset_id": adset, "creative": f'{{"creative_id":"{creative}"}}', "status": "PAUSED"}).json()["id"]
values.update(spreadsheetId=SHEET_ID, range=f"Ads!I{n}:J{n}", valueInputOption="RAW", body={"values": [["Created", ad]]}).execute()
return {"created": len(ready)}FAQ
Common questions
What does each row need?
Why are ads created paused?
What happens if Meta rejects something?
What permissions does it need?
Does it need AI?
How many runs does it use?
Can I change how it works?
Related
You might also like
Share each new blog post on Facebook and log it
Each new article on your blog is shared on your Facebook Page with a short caption and recorded in your sheet.
See how it worksModerate Facebook comments and log their sentiment
New comments on your Page are sorted by AI: spam is hidden, questions get a reply, and every comment is logged with its sentiment in your sheet.
See how it worksExport Meta Ads results to Google Sheets every day
Every morning yesterday's spend, clicks and conversions for each campaign are added to your sheet, and you get a short summary by email.
See how it worksKeep exploring
Set up “Create Facebook ads from rows in a sheet” from your assistant
Copy the prompt above, or connect your assistant first. Free plan, no credit card.