Lead Gen Forms
Create and manage Meta lead generation forms
Lead Gen Forms
Create native Meta Lead Gen (Instant) Forms, attach them to ads, and receive submitted leads in real time. The full flow:
- Create a form on a connected Facebook account with
POST /v1/ads/lead-forms. Forms are Page-scoped (Instagram lead ads use the linked Page's form under the hood). - Attach it to an ad by setting
goal: "lead_generation"andleadGenFormIdonPOST /v1/ads/create(standalone) orPOST /v1/ads/boost(boost an existing post). The ad set's lead optimization and promoted Page are derived automatically, no destination URL is needed. - Receive leads in real time via the
lead.receivedwebhook, or pull them withGET /v1/ads/lead-forms/{formId}/leads.
One-time setup: your Facebook Page must accept Facebook's Lead Generation Terms of Service before lead ads can run. If ad creation returns Meta error subcode 1815089 ("Terms of Service Not Accepted"), accept them once at https://www.facebook.com/ads/leadgen/tos/?page_id=YOUR_PAGE_ID. Creating forms and reading leads works without it, only running ads requires it.
Create a form, then a lead ad
Prefilled question types (EMAIL, PHONE, FULL_NAME, FIRST_NAME, LAST_NAME, …) auto-generate their label and key, send only { "type": "..." }. CUSTOM questions require key, label, and (for choice questions) options.
// 1. Create the form
const { form } = await zernio.ads.createLeadForm({ body: {
accountId: 'FACEBOOK_ACCOUNT_ID',
name: 'Spring promo',
questions: [
{ type: 'EMAIL' },
{ type: 'FULL_NAME' },
{ type: 'CUSTOM', key: 'budget', label: 'Monthly budget?', options: [
{ key: 'low', value: 'Under $1k' },
{ key: 'high', value: '$1k+' },
] },
],
privacyPolicyUrl: 'https://example.com/privacy',
} });
// 2. Create an ad that opens the form (no linkUrl needed)
const { ad } = await zernio.ads.createStandaloneAd({ body: {
accountId: 'FACEBOOK_ACCOUNT_ID',
adAccountId: 'act_123',
name: 'Spring promo lead ad',
goal: 'lead_generation',
leadGenFormId: form.id,
budgetAmount: 10,
budgetType: 'daily',
headline: 'Get a quote',
body: 'Tell us about your project',
callToAction: 'SIGN_UP',
imageUrl: 'https://example.com/creative.jpg',
} });# 1. Create the form
form = client.ads.create_lead_form(
account_id="FACEBOOK_ACCOUNT_ID",
name="Spring promo",
questions=[
{"type": "EMAIL"},
{"type": "FULL_NAME"},
{"type": "CUSTOM", "key": "budget", "label": "Monthly budget?",
"options": [{"key": "low", "value": "Under $1k"}, {"key": "high", "value": "$1k+"}]},
],
privacy_policy_url="https://example.com/privacy",
)["form"]
# 2. Create an ad that opens the form (no link_url needed)
ad = client.ads.create_standalone_ad(
account_id="FACEBOOK_ACCOUNT_ID",
ad_account_id="act_123",
name="Spring promo lead ad",
goal="lead_generation",
lead_gen_form_id=form["id"],
budget_amount=10,
budget_type="daily",
headline="Get a quote",
body="Tell us about your project",
call_to_action="SIGN_UP",
image_url="https://example.com/creative.jpg",
)# 1. Create the form
curl -X POST "https://zernio.com/api/v1/ads/lead-forms" \
-H "Authorization: Bearer $ZERNIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"accountId": "FACEBOOK_ACCOUNT_ID",
"name": "Spring promo",
"questions": [{"type":"EMAIL"},{"type":"FULL_NAME"}],
"privacyPolicyUrl": "https://example.com/privacy"
}'
# 2. Create an ad that opens the form (use the returned form id as leadGenFormId)
curl -X POST "https://zernio.com/api/v1/ads/create" \
-H "Authorization: Bearer $ZERNIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"accountId": "FACEBOOK_ACCOUNT_ID", "adAccountId": "act_123",
"name": "Spring promo lead ad", "goal": "lead_generation",
"leadGenFormId": "FORM_ID", "budgetAmount": 10, "budgetType": "daily",
"headline": "Get a quote", "body": "Tell us about your project",
"callToAction": "SIGN_UP", "imageUrl": "https://example.com/creative.jpg"
}'Receiving leads
The best way to capture leads is the lead.received webhook, fired the moment a lead is submitted. The payload carries lead.fields (the question-key to answer map) plus formId / adId provenance. You can also poll for them:
const { leads } = await zernio.ads.listFormLeads({
path: { formId: 'FORM_ID' },
query: { accountId: 'FACEBOOK_ACCOUNT_ID', limit: 50 },
});
for (const lead of leads) console.log(lead.fields);leads = client.ads.list_form_leads(
form_id="FORM_ID", account_id="FACEBOOK_ACCOUNT_ID", limit=50,
)["leads"]
for lead in leads:
print(lead["fields"])curl "https://zernio.com/api/v1/ads/lead-forms/FORM_ID/leads?accountId=FACEBOOK_ACCOUNT_ID&limit=50" \
-H "Authorization: Bearer $ZERNIO_API_KEY"To attach a form when boosting an existing post, pass the same leadGenFormId (and goal: "lead_generation") to POST /v1/ads/boost. To retire a form, DELETE /v1/ads/lead-forms/{formId} (Meta archives it, there is no hard delete).