Рекламная кампания через Meta Marketing API
В Marketing API реклама собирается из четырёх уровней: кампания (цель, например OUTCOME_TRAFFIC), группа объявлений (аудитория, бюджет, расписание), креатив (изображение, текст, ссылка) и объявление. Бюджет передаётся целым числом в минимальных единицах валюты, special_ad_categories обязателен для каждой кампании, а безопасный путь — создавать всё в PAUSED и включать после проверки.
Обновлено:
Коротко: как создать кампанию через API
Порядок жёсткий: выбираете рекламный кабинет (act_...), ищете и бесплатно оцениваете аудиторию, затем POST /act_{id}/campaigns (цель и special_ad_categories), /adsets (таргетинг, бюджет, оптимизация), /adimages и /adcreatives (изображение и текст) и в конце /ads. Всё создаётся в PAUSED, а после проверки все три уровня переводятся в ACTIVE.
Разрешения: для отчётов — ads_read, для создания и управления — ads_management. Чтобы работать с кабинетами других компаний, для ads_management нужен Advanced Access, то есть App Review.
Иерархия объектов: что где задаётся
| Уровень | Что определяет | Endpoint | Метод SDK |
|---|---|---|---|
| Рекламный кабинет | Валюта, оплата, доступы | act_{id} | ads.ad_accounts |
| Кампания | Одна цель (objective), special_ad_categories, опционально общий бюджет | POST /act_{id}/campaigns | create_campaign |
| Группа объявлений (ad set) | Аудитория, бюджет, расписание, оптимизация и событие оплаты | POST /act_{id}/adsets | create_adset |
| Изображение | hash для креатива | POST /act_{id}/adimages | upload_image |
| Креатив | Изображение или видео, текст, ссылка, CTA; после создания не меняется | POST /act_{id}/adcreatives | create_creative |
| Объявление | Связывает ad set и креатив | POST /act_{id}/ads | create_ad |
Все объявления в одной группе делят аудиторию и цель оптимизации. Креатив изменить нельзя — создаёте новый и привязываете к объявлению.
Цели кампании (objective)
Начиная с Marketing API v17.0 новые кампании создаются только с упрощёнными целями OUTCOME.
| Objective | Когда выбирать |
|---|---|
OUTCOME_AWARENESS | Показать рекламу как можно большему числу людей, узнаваемость |
OUTCOME_TRAFFIC | Переходы на сайт, в приложение или профиль |
OUTCOME_ENGAGEMENT | Вовлечённость в пост, просмотры видео, сообщения |
OUTCOME_LEADS | Лид-формы и другие заявки |
OUTCOME_SALES | Продажи и конверсии |
OUTCOME_APP_PROMOTION | Установки приложения и действия в нём |
Создать новую кампанию со старыми целями вроде LINK_CLICKS, CONVERSIONS или REACH нельзя; ранее созданные кампании продолжают работать.
В каких единицах передаётся бюджет
daily_budget и lifetime_budget — целое число в валюте кабинета, в минимальных единицах (Meta называет это offset). Для валют с offset 100 (USD, EUR, RUB и др.) 20 долларов в день передаются как 2000. Для валют с offset 1 (JPY, KRW, IDR, VND, CLP, COP, CRC, HUF, ISK, PYG, TWD) значение указывается в основной единице.
Узбекского сума (UZS) нет в списке валют Meta, поэтому всегда считайте бюджет в валюте самого кабинета (account.currency).
Бюджет задаётся либо на уровне кампании (Meta сама распределяет его по группам), либо на уровне группы объявлений — но не на обоих сразу. С lifetime_budget обязателен end_time; daily_budget — для групп длительностью больше 24 часов.
В SDK Socialift daily_budget_major=20 умножается на CURRENCY_OFFSET (100) и уходит как 2000. Это верно для валют с offset 100; если валюта кабинета с offset 1, делите значение на 100.
special_ad_categories: обязательно для каждой кампании
Meta требует явно указывать для каждой новой или изменённой кампании, относится ли она к особой категории. Для обычной рекламы передаётся пустой список ([]) — метод create_campaign в SDK делает это по умолчанию.
Категории: HOUSING (жильё), EMPLOYMENT (вакансии), FINANCIAL_PRODUCTS_SERVICES (финансовые продукты; заменила старое значение CREDIT) и ISSUES_ELECTIONS_POLITICS (социальные темы, выборы, политика). При выборе категории обязателен и special_ad_category_country.
Таргетинг в этих категориях ограничен: например, для рекламы жилья, вакансий и финансов в США, Канаде и Европе возраст обычно фиксируется на 18–65+, пол выбрать нельзя, радиус местоположения — не меньше 25 км, похожие аудитории недоступны.
Запуск кампании с нуля: 8 шагов
Выберите кабинет
ads.ad_accountsвозвращает доступные пользователю кабинеты с идентификаторомact_...и валютой. Бюджет считаете в этой валюте.Найдите интересы и местоположения
search_interests(type=adinterest) —id,name,pathи диапазон размера аудитории.search_locations(type=adgeolocation) —keyдля таргетинга. Храните ID, а не названия: Meta может переименовывать интересы.Соберите таргетинг
Без Custom Audience нужна хотя бы одна страна.
age_minне меньше 13,age_maxне больше 65;genders: 1 — мужчины, 2 — женщины, без поля — все. Радиус вокруг города — 17–80 км.Оцените аудиторию без затрат
GET /act_{id}/delivery_estimateотдаёт диапазон месячной аудитории (estimate_mau_lower_bound,estimate_mau_upper_bound) иestimate_dau. Еслиestimate_ready=false, повторите запрос чуть позже.Создайте кампанию
Цель (
OUTCOME_*),special_ad_categoriesиstatus=PAUSED.Создайте группу объявлений
Таргетинг, бюджет,
optimization_goal(напримерLINK_CLICKS),billing_event(напримерIMPRESSIONS) и сноваPAUSED.Изображение, креатив и объявление
Загружаете изображение в
/adimagesи получаетеhash, в креативе указываете его внутриobject_story_spec(страница, ссылка, текст, CTA), в объявлении —{"creative_id": ...}.Проверьте и включите
Посмотрите превью, затем переведите кампанию, группу и объявление в
ACTIVE. Объявление сначала проходит модерацию — следите заPENDING_REVIEWиDISAPPROVEDвeffective_status.
Python: поиск и оценка аудитории
Ничего не создаётся и деньги не тратятся — только поиск и оценка.
import asyncio, os
from socialift import Ads
async def main() -> None:
token = os.environ["META_USER_TOKEN"]
async with Ads(app_secret=os.environ["FACEBOOK_APP_SECRET"]) as ads:
account = (await ads.ad_accounts(token))[0]
print(account.id, account.currency)
interests = await ads.search_interests(token, "fitness", limit=5)
cities = await ads.search_locations(token, "Almaty", country_code="KZ")
print([c["key"] for c in cities]) # для cities=[...]
targeting = ads.build_targeting(
countries=["KZ"],
age_min=18,
interests=[{"id": i["id"], "name": i["name"]} for i in interests[:3]],
) # advantage_audience=True по умолчанию
estimate = await ads.delivery_estimate(token, ad_account_id=account.id, targeting=targeting)
print(estimate.get("estimate_mau_lower_bound"), estimate.get("estimate_mau_upper_bound"))
asyncio.run(main())Интересы внутри interests объединяются через «ИЛИ». Для условия «A И B» используйте параметр flexible_spec в build_targeting.
Точного числа Meta не даёт — только диапазон. Если он слишком узкий, уберите часть интересов или оставьте Advantage+ audience включённым.
Python: кампания → группа → креатив → объявление
В SDK status по умолчанию PAUSED — ни один вызов сам не начинает тратить деньги.
from socialift import Ads
async def launch(ads: Ads, token: str, account_id: str, page_id: str, targeting: dict) -> str:
campaign = await ads.create_campaign(
token, ad_account_id=account_id, name="Осенняя акция",
objective="OUTCOME_TRAFFIC", special_ad_categories=[],
)
adset = await ads.create_adset(
token, ad_account_id=account_id, name="KZ 18+", campaign_id=campaign["id"],
targeting=targeting, optimization_goal="LINK_CLICKS", billing_event="IMPRESSIONS",
daily_budget_major=20, # кабинет в USD: уйдёт 2000 центов
)
with open("banner.jpg", "rb") as f:
image = await ads.upload_image(token, ad_account_id=account_id, image=f.read(), filename="banner.jpg")
creative = await ads.create_creative(
token, ad_account_id=account_id, name="Баннер A", page_id=page_id,
link="https://example.com", message="Осенние скидки", image_hash=image["hash"],
)
ad = await ads.create_ad(
token, ad_account_id=account_id, name="Баннер A",
adset_id=adset["id"], creative_id=creative["id"],
)
return ad["id"]Чтобы объявление начало показываться, все три уровня — кампания, группа и объявление — должны быть ACTIVE: если родитель на паузе, effective_status объявления будет CAMPAIGN_PAUSED или ADSET_PAUSED. Включение: await ads.set_status(token, node_id, "ACTIVE") для каждого уровня.
Надёжнее всего останавливать на уровне кампании: при PAUSED у кампании не показывается ничего из того, что под ней.
Чтобы показывать рекламу от имени Instagram-аккаунта, передайте в create_creative параметр instagram_user_id — Meta требует его вместо устаревшего instagram_actor_id.
Advantage+ audience: с v23.0 указывается явно
Advantage+ audience разрешает системе показа Meta выходить за пределы заданной аудитории в поисках людей, которые дадут результат. В API это поле targeting.targeting_automation.advantage_audience: 1 — включено, 0 — выключено.
Начиная с Graph API v23.0 при создании новой группы объявлений значение по умолчанию равно 1 либо требуется явно передать 1 или 0. На обновление существующих групп это не влияет.
Во включённом режиме возраст — не жёсткое ограничение: age_min можно задать только в диапазоне 18–25, а age_max фиксирован на 65. Более узкий возраст передаётся как подсказка через age_range. Если нужен строгий контроль (например, юридические ограничения), вызывайте build_targeting(..., advantage_audience=False) и задавайте age_min/age_max как границы.
Разрешения и уровни доступа Marketing API
ads_read — отчёты по рекламе и Insights API. ads_management — чтение и управление кабинетом, создание кампаний; зависит от pages_read_engagement и pages_show_list и для чужих бизнесов требует Advanced Access. Tech provider, работающим с данными других компаний, нужна ещё и Access Verification.
У Marketing API свои уровни: Limited Access (бывший Standard) выдаётся автоматически при добавлении продукта, с жёсткими лимитами на каждый кабинет — для разработки. Full Access (бывший Advanced) — после App Review, лимиты мягче. Условие Meta: минимум 500 вызовов Marketing API за последние 15 дней и доля ошибок меньше 15% в последних 500 вызовах.
Чтение результатов: insights
/{object-id}/insights есть на каждом уровне: кабинет, кампания, группа и объявление.
summary = await ads.insights(token, campaign_id, since="2026-09-01", until="2026-09-16")
print(summary.spend, summary.impressions, summary.reach, summary.ctr)
rows = await ads.insights_rows(
token, account_id, level="adset", date_preset="last_7d", breakdowns=["age", "gender"]
)
for row in rows:
print(row.get("age"), row.get("gender"), row.get("spend"), row.get("cpc"))Без дат insights возвращает сумму за весь период (date_preset=maximum); сама Meta на запрос без параметров обычно отдаёт последние 30 дней.
insights_rows возвращает сырые строки с разбивкой — здесь ответ на вопрос «в какой возрастной группе клик дешевле». Не все разбивки совместимы: при ошибке добавляйте их по одной.
Проверьте перед запуском
- Известны валюта кабинета и её offset, бюджет пересчитан.
special_ad_categoriesзадан явно (для обычной рекламы[]).- В таргетинге есть хотя бы одна страна или Custom Audience.
- Диапазон из
delivery_estimateсоответствует задаче. - Кампания, группа и объявление созданы в
PAUSEDи проверены. - Для запуска все три уровня
ACTIVE, для остановки —PAUSEDу кампании. effective_statusи расход отслеживаются регулярно.
Частые вопросы
Почему бюджет получился в 100 раз больше или меньше?
Marketing API ожидает бюджет в минимальных единицах валюты. В USD 20 долларов — это 2000, а в валюте с offset 1, например JPY, 2000 — это 2000 иен. Проверьте валюту кабинета и передавайте значение соответственно.
Можно ли использовать старую цель LINK_CLICKS?
Для новых кампаний — нет: с v17.0 принимаются только цели OUTCOME_*. LINK_CLICKS теперь используется как optimization_goal на уровне группы объявлений.
Можно ли узнать размер аудитории, не тратя деньги?
Да, endpoint delivery_estimate возвращает примерный диапазон месячной аудитории и дневных активных пользователей. Точного числа Meta не даёт, только диапазон.
Объявление создано, но не показывается — почему?
Чаще всего кампания или группа ещё в PAUSED, либо объявление на модерации (PENDING_REVIEW) или отклонено (DISAPPROVED). Смотрите поле effective_status объявления.
Нужен ли App Review для ads_management?
Для своих кабинетов и людей с ролями в приложении хватает Standard (Limited) Access. Чтобы управлять кабинетами других компаний, нужен Advanced Access — App Review и Business Verification.
Можно ли не передавать special_ad_categories?
Нет, поле обязательное. Для обычной рекламы передаётся пустой список; для тем жилья, вакансий, финансов или политики — соответствующая категория и special_ad_category_country.