Клиент Facebook: методы Graph API
Facebook — клиент Meta Graph API в Python SDK Socialift. Он получает пользовательский токен через Facebook Login (обычный scope или config_id в Facebook Login for Business), возвращает управляемые страницы вместе с их токенами, публикует посты и истории, модерирует комментарии, подписывает страницу на вебхуки и читает лиды из форм Lead Ads. Токен передаётся в каждый метод аргументом и не хранится в клиенте.
from socialift import FacebookМетодов: 25по версии socialift 1.3.0Обновлено:
Как настроить клиент Facebook?
Клиент создаётся так: Facebook(app_id=..., app_secret=...). Оба значения обязательны — при пустом значении будет ConfigurationError. Facebook.from_env() берёт их из FACEBOOK_APP_ID и FACEBOOK_APP_SECRET, необязательный config_id — из FACEBOOK_CONFIG_ID, а версию Graph API — из META_GRAPH_VERSION (иначе используется версия SDK по умолчанию).
App ID и App Secret находятся в настройках приложения в Meta for Developers — пошагово в руководстве «Настройка приложения Meta». Для приложения типа Business (Facebook Login for Business) разрешения выбираются в конфигурации в App Dashboard, а в ссылку входа передаётся config_id; в остальных случаях отправляется список scopes. Ключи можно сохранить и проверить у провайдера в разделе кабинета «Ключи».
Типы токенов: me, pages, granted_scopes и revoke работают с пользовательским токеном; методы постов, комментариев, вебхуков и лидов — с токеном страницы Page.access_token, который возвращает pages(). appsecret_proof добавляется к каждому запросу автоматически.
Лицензионный ключ берётся из переменной SOCIALIFT_LICENSE_KEY или аргумента license_key=. Используйте клиент через async with, чтобы HTTP-сессия гарантированно закрывалась.
import asyncio
import os
from socialift import Facebook
async def main():
# 1) Явные аргументы
fb = Facebook(
app_id=os.environ["FACEBOOK_APP_ID"],
app_secret=os.environ["FACEBOOK_APP_SECRET"],
config_id=os.environ.get("FACEBOOK_CONFIG_ID"), # Login for Business
)
# 2) Или из окружения: FACEBOOK_APP_ID, FACEBOOK_APP_SECRET,
# FACEBOOK_CONFIG_ID, META_GRAPH_VERSION
# fb = Facebook.from_env()
async with fb:
user_token = os.environ["FB_USER_TOKEN"] # результат exchange_code()
for page in await fb.pages(user_token):
print(page.id, page.name, page.instagram_username)
asyncio.run(main())
Клиент и OAuth
Создание клиента, ссылка Facebook Login, обмен токена, проверка и отзыв разрешений.
exchange_codefrom_envgranted_scopeslogin_urlrevoke
exchange_code
asyncОбменивает code из callback на краткосрочный, а затем сразу на долгосрочный пользовательский токен и возвращает TokenBundle. Вызывается один раз в OAuth callback.
await fb.exchange_code(code: str, *, redirect_uri: str) -> socialift.models.TokenBundleredirect_uriдолжен в точности совпадать со значением из ссылки входа (требование Meta).- По документации Meta долгосрочный пользовательский токен обычно действует около 60 дней; срок берите из
TokenBundle.expires_at, хранение токена — на вашей стороне. - Если Meta не вернула токен, будет
ApiError.
from_env
classmethodСоздаёт клиент из переменных окружения: FACEBOOK_APP_ID, FACEBOOK_APP_SECRET, FACEBOOK_CONFIG_ID и META_GRAPH_VERSION. Подходит для серверных приложений, где ключи не должны лежать в коде.
Facebook.from_env(**kwargs) -> Facebook- Если
FACEBOOK_APP_IDилиFACEBOOK_APP_SECRETпусты, будетConfigurationError. - Дополнительные аргументы (
timeout,retry,session,license_key) передаются в конструктор.
granted_scopes
asyncВозвращает разрешения, реально выданные токену, через debug_token. Пользователь может отклонить часть разрешений в диалоге, поэтому включайте функции по этому списку, а не по запрошенному.
await fb.granted_scopes(token: str) -> list[str]- Запрос подписывается токеном приложения (
app_id|app_secret); в аргумент передайте проверяемый токен. - Если разрешений нет, возвращается пустой список.
Формирует ссылку на диалог Facebook Login для редиректа пользователя; метод синхронный и запросов не отправляет. При наличии config_id (в аргументе или клиенте) используется Facebook Login for Business, иначе — список scopes.
fb.login_url(
*,
redirect_uri: str,
state: Optional[str] = None,
config_id: Optional[str] = None,
scopes: Optional[list[str]] = None,
) -> str- SDK не хранит
state: сгенерируйте случайное значение (например,secrets.token_urlsafe(32)), сохраните в сессии и сравните в callback — Meta описывает его как защиту от CSRF. - При
config_idпараметрscopeне отправляется: разрешения берутся из конфигурации в App Dashboard. Login for Business работает только в приложениях типа Business.
url = fb.login_url(redirect_uri="https://siz.uz/cb", state=state, config_id="...")revoke
asyncОтзывает все разрешения приложения на стороне Facebook (DELETE /{user_id}/permissions) и возвращает bool. Вызывайте при отключении интеграции — просто удалить токен из базы недостаточно.
await fb.revoke(token: str, *, user_id: str = 'me') -> bool- По документации Meta после отзыва всех разрешений токены пользователя аннулируются, и ему нужно войти заново.
- Вызывается с пользовательским токеном;
user_idпо умолчаниюme.
await fb.revoke(user_token)Профиль и страницы
Профиль пользователя и список страниц с их токенами.
me
asyncВозвращает профиль владельца токена в виде Profile: id, имя, фото и email. Нужен сразу после входа, чтобы понять, кто подключился.
await fb.me(token: str) -> socialift.models.Profile- Нужен пользовательский токен, а не токен страницы.
emailможет быть пустым; полный ответ — вProfile.raw.
pages
asyncВозвращает страницы, которыми управляет пользователь, в виде объектов Page: у каждой есть токен страницы (access_token) и связанный профессиональный аккаунт Instagram (instagram_id, instagram_username). Отсюда берётся токен для методов постов, комментариев, вебхуков и лидов.
await fb.pages(token: str) -> list[socialift.models.Page]- Вызывается с пользовательским токеном.
- По документации Meta токен страницы, полученный с долгосрочным пользовательским токеном, не имеет срока действия и аннулируется только при определённых условиях.
Page.access_tokenсекретен: не отдавайте его во фронтенд и не пишите в логи (вreprон скрыт).- Пагинация автоматическая, не более 500 страниц.
Нужные разрешения:pages_show_list
for page in await fb.pages(user_token):
print(page.name, page.instagram_username)Подписка на вебхуки
Подписка страницы на вебхуки приложения, проверка и отписка.
subscribe_pagesubscriptionsunsubscribe_page
subscribe_page
asyncПодписывает страницу на вебхуки приложения (POST /{page_id}/subscribed_apps). Даже при настроенном вебхуке на уровне приложения события со страницы не приходят, пока она не подписана.
await fb.subscribe_page(
page_id: str,
page_token: str,
fields: Optional[list[str]] = None,
) -> bool- Нужен токен страницы; Meta требует разрешения
pages_manage_metadataиpages_show_list. - Без
fieldsотправляется список по умолчанию:leadgen,leadgen_update,feed,messages,messaging_postbacks,mention. Для поляmessagesMeta требует ещёpages_messaging— если его нет, передайтеfieldsявно. - Правило Meta: вебхуки Instagram через
subscribed_fieldsне настраиваются.
Нужные разрешения:pages_manage_metadata
await fb.subscribe_page(page_id, page_token, fields=["leadgen", "feed"])subscriptions
asyncВозвращает, на какие приложения и поля подписана страница. Используйте для диагностики, когда события вебхука перестали приходить.
await fb.subscriptions(page_id: str, page_token: str) -> list[dict]- Вызывается с токеном страницы; возвращается массив
dataиз ответа Graph API без изменений.
apps = await fb.subscriptions(page_id, page_token)unsubscribe_page
asyncУдаляет подписку приложения на вебхуки страницы (DELETE /{page_id}/subscribed_apps) и возвращает bool. Вызывайте, когда клиент отключает страницу.
await fb.unsubscribe_page(page_id: str, page_token: str) -> bool- После отписки лиды, комментарии и другие события с этой страницы приходить перестают.
await fb.unsubscribe_page(page_id, page_token)Посты и статистика
Чтение, публикация и удаление постов страницы, статистика поста.
delete_postpost_insightspostspublish_post
delete_post
asyncБезвозвратно удаляет пост страницы (DELETE /{post_id}) и возвращает bool.
await fb.delete_post(post_id: str, page_token: str) -> bool- Нужен токен страницы;
post_id— этоidизposts()илиpublish_post().
Нужные разрешения:pages_manage_posts
await fb.delete_post(post_id, page_token)post_insights
asyncВозвращает статистику поста как словарь {метрика: значение}. SDK запрашивает post_impressions, post_impressions_unique, post_engaged_users, post_clicks и post_reactions_by_type_total.
await fb.post_insights(post_id: str, page_token: str) -> dict[str, Any]- При ошибке API исключение не выбрасывается — возвращается пустой словарь
{}(например, у старого поста нет статистики). - Meta отключила
post_impressionsдля всех версий API с 15 ноября 2025 года,post_impressions_uniqueтоже в списке устаревших (замены:post_media_view,post_total_media_view_unique). На устаревшую метрику Graph API отвечает ошибкой, поэтому метод может вернуть{}. - Нужен токен страницы; Meta требует
read_insightsиpages_read_engagement.
Нужные разрешения:read_insightspages_read_engagement
stats = await fb.post_insights(post_id, page_token)posts
asyncЧитает опубликованные посты страницы через /published_posts. /feed не используется, потому что туда попадают и записи других людей на странице.
await fb.posts(page_id: str, page_token: str, *, limit: int = 25) -> list[dict]- Вызывается с токеном страницы.
limit— общее число возвращаемых постов; пагинацию SDK проходит сам.- По документации Meta endpoint возвращает примерно 600 постов за год.
Нужные разрешения:pages_read_engagement
posts = await fb.posts(page_id, page_token, limit=25)publish_post
asyncПубликует от имени страницы текстовый пост (при необходимости со ссылкой) в /{page_id}/feed и возвращает ответ Meta с id поста. published=False отправляет published=false — пост не публикуется сразу.
await fb.publish_post(
page_id: str,
page_token: str,
*,
message: str,
link: Optional[str] = None,
published: bool = True,
) -> dict- Нужен токен страницы. В руководстве Pages API Meta указаны разрешения
pages_manage_posts,pages_manage_engagement,pages_read_engagementи задачиCREATE_CONTENT,MANAGE,MODERATEна странице. - Только текст и ссылка: загрузки фото или видео в этом методе нет.
scheduled_publish_timeне отправляется, так что это не отложенная публикация.
Нужные разрешения:pages_manage_postspages_read_engagement
post = await fb.publish_post(page_id, page_token, message="Salom!", link="https://siz.uz")Истории страницы
Публикация фото- и видеоисторий, список историй, статус загрузки видео. История живёт 24 часа.
publish_photo_storypublish_video_storystoriesvideo_story_status
publish_photo_story
asyncПубликует фото как историю страницы и возвращает {photo_id, post_id, success}. Два шага: фото сначала загружается в POST /{page-id}/photos с published=false, затем полученный photo_id передаётся в edge photo_stories.
await fb.publish_photo_story(
page_id: str,
page_token: str,
*,
photo_url: Optional[str] = None,
photo_id: Optional[str] = None,
) -> dict- Meta принимает в
photo_storiesтолькоphoto_idи не принимает ссылку — поэтому первый шаг обязателен; SDK делает его сам. - Передаётся ровно один из
photo_url(публичная ссылка, файл скачивает Meta) иphoto_id(уже загруженное неопубликованное фото). Если второй шаг оборвался, сphoto_idможно продолжить без повторной загрузки. - Требования Meta: .jpeg, .bmp, .png, .gif или .tiff, до 10 МБ (для .png рекомендуется 1 МБ). Медиа из прошлого поста переиспользовать нельзя — для каждой истории нужна новая загрузка.
- Неопубликованное фото хранится на серверах Facebook около 24 часов: столько же действует
photo_id. - Разрешения:
pages_manage_posts,pages_read_engagement,pages_show_listи задачаCREATE_CONTENTна странице. Вызывается с токеном страницы.
Нужные разрешения:pages_manage_postspages_read_engagementpages_show_list
story = await fb.publish_photo_story(page_id, page_token,
photo_url="https://cdn.siz.uz/story.jpg")publish_video_story
asyncПубликует видео как историю страницы и возвращает {video_id, post_id, success}. Meta требует здесь трёхфазную «resumable upload»: upload_phase=start → загрузка файла на upload_url из ответа → upload_phase=finish.
await fb.publish_video_story(
page_id: str,
page_token: str,
*,
video_url: Optional[str] = None,
video: Union[bytes, bytearray, str, os.PathLike, NoneType] = None,
wait_timeout: float = 300.0,
upload_attempts: int = 3,
) -> dict- Передаётся ровно один из
video_url(публичная ссылка — файл скачивает Meta) иvideo(байты или путь к локальному файлу). upload_urlберётся из ответа и не изменяется (он ведёт на rupload.facebook.com); на втором шаге токен уходит не в query, а в заголовкеAuthorization: OAuth.- Если загрузка оборвалась, Meta сообщает, сколько байт дошло, и SDK продолжает с этого места — до
upload_attemptsпопыток (по умолчанию 3), не отправляя файл заново. - Перед третьим шагом SDK ждёт статуса загрузки
completeдоwait_timeout(по умолчанию 300 секунд). По истечении времени будетApiError, вpayloadкоторого остаётсяvideo_id— состояние можно проверить черезvideo_story_status(). - Требования Meta (SDK их не проверяет): .mp4, кодек H.264 или H.265, звук AAC 48 кГц; соотношение 9:16, размер от 540x960 до 1080x1920, 24–60 FPS. По длительности в документации Meta встречаются два разных ограничения (3–90 с и «не длиннее 60 секунд») — безопасно держаться 3–60 секунд.
- Ошибка о низком качестве файла (например «Resolution too low») приходит не на третьем шаге, а в статусе после загрузки; полный текст сохраняется в
ApiError.payload.
Нужные разрешения:pages_manage_postspages_read_engagementpages_show_list
story = await fb.publish_video_story(page_id, page_token,
video_url="https://cdn.siz.uz/story.mp4")stories
asyncВозвращает истории страницы: в каждой записи post_id, status, creation_time, media_type, media_id и url. post_id из publish_photo_story()/publish_video_story() соответствует записи в этом списке — так опубликованную историю можно отследить.
await fb.stories(page_id: str, page_token: str, *, limit: int = 25) -> list[dict]- SDK намеренно не отправляет
fields: выбор полей для этого edge Meta не документирует, а неизвестныйfieldsроняет в Graph API весь запрос. - Архивные истории возвращаются, только если на странице включён архив.
- Разрешения:
pages_read_engagementиpages_show_list; вызывается с токеном страницы.
Нужные разрешения:pages_read_engagementpages_show_list
stories = await fb.stories(page_id, page_token, limit=25)video_story_status
asyncВозвращает статус загрузки и обработки видеоистории (GET /{video-id}?fields=status): uploading_phase, processing_phase и publishing_phase, в каждой — status: not_started, in_progress, complete или error.
await fb.video_story_status(video_id: str, page_token: str) -> dict- На фазе загрузки есть дополнительное поле
bytes_transfered(орфография из документации Meta) — оно показывает, с какого места продолжать прерванную загрузку. - Если
publish_video_story()завершилсяApiError,video_idостаётся вpayloadисключения: именно этим методом выясняют, что пошло не так. - Вызывается с токеном страницы.
Комментарии
Чтение комментариев к постам, ответы и скрытие.
commentshide_commentreply_comment
hide_comment
asyncСкрывает комментарий (is_hidden=true) или снова показывает его при hidden=False. Обратимая альтернатива удалению.
await fb.hide_comment(comment_id: str, page_token: str, hidden: bool = True) -> dict- По документации Meta
is_hiddenприменим только к комментариям страниц. - Вызывается с токеном страницы.
Нужные разрешения:pages_manage_engagement
await fb.hide_comment(comment_id, page_token, hidden=True)reply_comment
asyncОтвечает на комментарий от имени страницы (POST /{comment_id}/comments) и возвращает id нового комментария.
await fb.reply_comment(comment_id: str, page_token: str, message: str) -> dict- По документации Meta нужен токен страницы пользователя, который может выполнять задачу
MODERATE, и разрешениеpages_manage_engagement.
Нужные разрешения:pages_manage_engagement
await fb.reply_comment(comment_id, page_token, "Rahmat!")Lead Ads
Формы лидов, лиды и контекст рекламы, из которой пришёл лид.
ad_context
asyncВозвращает контекст рекламы, из которой пришёл лид: ID и названия объявления, группы объявлений и кампании, objective, optimization_goal и targeting. Полезно, чтобы показать источник лида в CRM.
await fb.ad_context(ad_id: Optional[str], token: str) -> dict- Исключений не выбрасывает: при ошибке API (например, без
ads_management) или пустомad_idвозвращает{}. - Ключи:
ad_id,ad_name,adset_id,adset_name,campaign_id,campaign_name,objective,optimization_goal,targeting.
Нужные разрешения:ads_management
context = await fb.ad_context(ad_id, token)lead
asyncВозвращает один полный лид по leadgen_id из события вебхука leadgen в виде Lead. Основной способ передавать лиды в CRM в реальном времени.
await fb.lead(leadgen_id: str, page_token: str) -> socialift.models.Lead- Вебхук Meta содержит только идентификаторы (
leadgen_id,page_id,form_id,adgroup_id,ad_id,created_time) — ответы формы получаются этим методом. - Нужны токен страницы и разрешение
leads_retrieval.
lead_forms
asyncВозвращает формы Lead Ads страницы в виде списка LeadForm: название, статус, язык, вопросы и число лидов. Нужен, чтобы выбрать форму и сопоставить вопросы с полями CRM.
await fb.lead_forms(page_id: str, page_token: str) -> list[socialift.models.LeadForm]- Нужен токен страницы; пагинация автоматическая, сырой ответ — в
LeadForm.raw.
Нужные разрешения:leads_retrievalpages_manage_ads
forms = await fb.lead_forms(page_id, page_token)leads
asyncВозвращает лиды одной формы в виде списка Lead. С since (unix timestamp) можно забрать только более новые лиды — например, догрузить период, пропущенный вебхуком.
await fb.leads(
form_id: str,
page_token: str,
*,
since: Optional[int] = None,
limit: int = 200,
) -> list[socialift.models.Lead]sinceотправляется в Graph API как фильтрGREATER_THANпоtime_created.Lead.fields— нормализованные поля (full_name,email,phoneи т. д.),Lead.field_data— исходный список от Meta.- По документации Meta для чтения лидов нужны
leads_retrieval,ads_management,pages_show_list,pages_read_engagement,pages_manage_adsи токен человека, который может размещать рекламу в рекламном аккаунте. limitпо умолчанию 200; пагинация автоматическая.
Нужные разрешения:leads_retrieval
leads = await fb.leads(form_id, page_token, limit=200)
for lead in leads:
print(lead.fields.get("phone"), lead.campaign_name)Общие методы всех клиентов
У каждого HTTP-клиента (Telegram, Facebook, Instagram, LinkedIn, TikTok, Ads) есть эти низкоуровневые методы: чтобы вызвать эндпоинт, для которого в SDK ещё нет обёртки, или читать данные постранично. По завершении блока async with соединение закрывается автоматически.
request
asyncОтправляет произвольный HTTP-запрос и возвращает JSON-ответ — для эндпоинта, у которого в SDK пока нет отдельного метода. Токен и параметры подписи (access_token, для Meta — appsecret_proof через socialift.crypto.appsecret_proof) вы передаёте сами в params или headers.
await fb.request(
method: str,
url: str,
*,
params: Optional[Mapping[str, Any]] = None,
data: Optional[Any] = None,
json: Optional[Any] = None,
headers: Optional[Mapping[str, str]] = None,
) -> Any- Значения
Noneвparamsне отправляются. - При лимите (
RateLimitError) и временных ошибках сервера (HTTP 429, 500, 502, 503, 504) запрос повторяется поRetryPolicy; если есть заголовокRetry-After, SDK ждёт указанное время. Обрыв сети выбрасывается какTransportError.
get
asyncКороткая форма request("GET", ...).
await fb.get(url: str, **kw) -> Anypost
asyncКороткая форма request("POST", ...).
await fb.post(url: str, **kw) -> Anydelete
asyncКороткая форма request("DELETE", ...).
await fb.delete(url: str, **kw) -> Anypaginate
asyncПроходит по курсорной пагинации Meta (paging.next) и возвращает все элементы data одним списком.
await fb.paginate(
url: str,
*,
params: Optional[Mapping[str, Any]] = None,
max_items: int = 500,
page_size: int = 100,
) -> list[dict]max_items(по умолчанию 500) — защитный предел: у крупного аккаунта могут быть десятки тысяч записей.page_size—limitодного запроса.
close
asyncЗакрывает HTTP-сессию, созданную самим SDK (переданную извне сессию не трогает). При использовании async with Client(...) вызывается автоматически.
await fb.close() -> NoneЧастые вопросы
Как получить токен страницы Facebook (page access token)?
Отправьте пользователя в Facebook Login по ссылке из login_url и в callback обменяйте code на долгосрочный пользовательский токен через exchange_code. Затем pages(user_token) вернёт Page.access_token для каждой страницы. По документации Meta токен страницы, полученный с долгосрочным пользовательским токеном, не имеет срока действия и аннулируется только при определённых условиях.
Почему на вебхук не приходят лиды или комментарии?
Настройки вебхука на уровне приложения недостаточно: каждую страницу нужно отдельно подписать на приложение через subscribe_page. Проверить подписку можно методом subscriptions. Meta требует разрешение pages_manage_metadata, а для поля messages — ещё и pages_messaging.
Как получить лиды Facebook Lead Ads на Python?
Событие вебхука leadgen содержит только идентификаторы (leadgen_id, form_id, ad_id и другие). Полные ответы получите через lead(leadgen_id, page_token), а пропущенный период догрузите через leads(form_id, page_token, since=...). В Lead.fields лежат нормализованные поля: имя, телефон, email.
Что такое config_id в Facebook Login for Business?
В приложении типа Business разрешения выбираются не через scope в коде, а в конфигурации в Meta App Dashboard, и её ID передаётся в ссылку входа как config_id. Если в SDK задан config_id (в клиенте или в login_url), параметр scope не отправляется.
Как опубликовать историю на странице Facebook через API?
Фото публикуется в два шага: publish_photo_story(page_id, page_token, photo_url=...) сначала загружает фото с published=false, а затем передаёт полученный photo_id в edge photo_stories — ссылку Meta там не принимает, только photo_id. Для видео нужна трёхфазная загрузка (открыть сессию → залить файл на выданный Meta upload_url → опубликовать), её выполняет publish_video_story(). Разрешения: pages_manage_posts, pages_read_engagement, pages_show_list и задача CREATE_CONTENT на странице.
Поддерживает ли Socialift SDK Facebook Messenger?
Нет. В клиенте Facebook нет методов для чтения или отправки сообщений Messenger. Для Instagram Direct используйте методы conversations и send_message клиента Instagram.
comments
asyncВозвращает комментарии к посту, начиная с новых (
order=reverse_chronological). Подходит для очереди модерации или выгрузки комментариев в CRM.filterне передаётся, поэтому действует значение Graph API по умолчанию: только комментарии верхнего уровня, без ответов.limit— общее число комментариев; пагинация автоматическая.Нужные разрешения:
pages_read_engagement