Skip to main content
RapidDev - Software Development Agency
bolt-ai-integrationsBolt Chat + API Route

How to Integrate Bolt.new with Facebook Ads

Integrate Bolt.new with Facebook Ads using the Meta Marketing API via Next.js API routes. Create a Meta App in Meta for Developers, obtain a Marketing API access token, then fetch campaign, ad set, and ad performance data server-side. Build a custom analytics dashboard showing spend, impressions, clicks, and ROAS. Creating and modifying campaigns also works through the same Graph API. A reviewed Meta App is required for production access.

What you'll learn

  • How to create a Meta App and obtain a Marketing API access token
  • How to fetch campaign, ad set, and ad performance insights via a Next.js API route
  • How to build an analytics dashboard displaying spend, impressions, clicks, CPM, and ROAS
  • How to create and update Facebook ad campaigns programmatically
  • How to handle Meta App review requirements for production access
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate19 min read35 minutesMarketingApril 2026RapidDev Engineering Team
TL;DR

Integrate Bolt.new with Facebook Ads using the Meta Marketing API via Next.js API routes. Create a Meta App in Meta for Developers, obtain a Marketing API access token, then fetch campaign, ad set, and ad performance data server-side. Build a custom analytics dashboard showing spend, impressions, clicks, and ROAS. Creating and modifying campaigns also works through the same Graph API. A reviewed Meta App is required for production access.

Build a Custom Facebook Ads Dashboard in Bolt.new with Meta Marketing API

Meta's Marketing API gives you complete programmatic control over Facebook and Instagram advertising. Rather than living inside Ads Manager — Meta's browser-based interface — you can pull campaign data into your own Bolt-built application and present it exactly how your business needs. A custom dashboard built with the Marketing API can combine ad performance data with your own conversion data, apply your own attribution models, track custom KPIs, or aggregate multiple ad accounts into a single view that Ads Manager cannot provide.

The Marketing API is part of Meta's broader Graph API, which follows a consistent pattern: every endpoint is a node (an ad account, campaign, ad set, or ad) and you request specific fields by appending ?fields= parameters. Performance insights (impressions, clicks, spend, reach, frequency, CPM, CPC, CTR, ROAS) are fetched through the /insights edge with date range and breakdown parameters. This HTTP-REST design means the entire API works via fetch() calls, making it fully compatible with Bolt's WebContainer for outbound requests during development.

There is one important access tier to understand before building. A standard Marketing API access token can read data from ad accounts where the token owner has permission. This is sufficient for building tools for your own business. Building tools that access other people's ad accounts — a SaaS dashboard for clients, an agency tool — requires Meta App Review, where Meta manually reviews your use case before granting permissions like ads_read and ads_management to other users. Plan for a two to four week review process if you are building a multi-account product.

Integration method

Bolt Chat + API Route

Bolt generates Next.js API routes that proxy requests to Meta's Graph API, keeping your access token server-side and out of client bundles. The Marketing API is purely HTTP-based (REST over HTTPS), which means it works in Bolt's WebContainer for outbound data fetching during development. You can build and test campaign dashboards in the preview before deploying. Webhooks and Lead Ads real-time notifications require a deployed URL since the WebContainer cannot receive incoming connections.

Prerequisites

  • A Bolt.new account with a Next.js project
  • A Facebook account with admin access to a Facebook Business Manager
  • At least one Facebook Ad Account with existing campaigns (for reading data)
  • A Meta Developer account at developers.facebook.com and a created Meta App
  • A Marketing API access token with ads_read permission (ads_management for creating campaigns)

Step-by-step guide

1

Create a Meta App and Get a Marketing API Access Token

Go to developers.facebook.com and click 'My Apps' in the top-right, then 'Create App.' Select 'Other' as the use case, then 'Business' as the app type. Give your app a name, enter a contact email, and optionally associate it with a Business Manager. Click 'Create App' to complete creation. Once your app is created, you land on the App Dashboard. Click 'Add Product' and find 'Marketing API' — click 'Set Up.' This adds the Marketing API product to your app. In the left sidebar, you will now see Marketing API under your app's products. For development, the fastest way to get an access token is the Graph API Explorer. Go to developers.facebook.com/tools/explorer. At the top right, select your app from the dropdown. Click 'Generate Access Token' — this opens a permissions dialog. Select the permissions you need: `ads_read` for reading campaign data, `ads_management` for creating and modifying campaigns. Click 'Generate Access Token' and approve the permissions in the popup. The resulting token is a User Access Token that expires in one to two hours. For a long-lived token, click the information icon next to the token and select 'Extend Access Token' — this exchanges it for a 60-day token. For production use, you need a System User token (created in Business Manager under Users → System Users), which does not expire and does not require a logged-in Facebook user. Go to your Business Manager → Settings → Users → System Users → Create System User, then generate a token for that system user with the required permissions and assign it to your ad account. Copy the access token and your Ad Account ID (format: `act_XXXXXXXXXX`, found in Ads Manager URL or Business Manager). You will add both to your Bolt project's .env file.

Bolt.new Prompt

Add a .env file to this Next.js project with META_ACCESS_TOKEN=your-token-here and META_AD_ACCOUNT_ID=act_your-account-id. Create a lib/meta.ts file that exports a metaFetch helper function which takes an endpoint string and optional query params, calls https://graph.facebook.com/v20.0/{endpoint} with the META_ACCESS_TOKEN from process.env in an Authorization Bearer header, and returns the JSON response. Include error handling that extracts Meta's error.message from the response.

Paste this in Bolt.new chat

lib/meta.ts
1// lib/meta.ts
2const META_BASE_URL = 'https://graph.facebook.com/v20.0';
3
4interface MetaFetchOptions {
5 params?: Record<string, string>;
6 method?: 'GET' | 'POST' | 'DELETE';
7 body?: Record<string, unknown>;
8}
9
10export async function metaFetch<T = unknown>(
11 endpoint: string,
12 options: MetaFetchOptions = {}
13): Promise<T> {
14 const accessToken = process.env.META_ACCESS_TOKEN;
15 if (!accessToken) throw new Error('META_ACCESS_TOKEN is not configured');
16
17 const url = new URL(`${META_BASE_URL}/${endpoint}`);
18 if (options.params) {
19 Object.entries(options.params).forEach(([key, value]) => {
20 url.searchParams.set(key, value);
21 });
22 }
23
24 const response = await fetch(url.toString(), {
25 method: options.method || 'GET',
26 headers: {
27 Authorization: `Bearer ${accessToken}`,
28 'Content-Type': 'application/json',
29 },
30 body: options.body ? JSON.stringify(options.body) : undefined,
31 });
32
33 const data = await response.json() as { error?: { message: string; code: number } } & T;
34
35 if (!response.ok || data.error) {
36 throw new Error(data.error?.message || `Meta API error: ${response.status}`);
37 }
38
39 return data;
40}

Pro tip: System User tokens are the correct long-term solution for production. User Access Tokens expire and require a Facebook login, which breaks automated dashboards. Create a System User in Business Manager and assign it to your ad account with Standard access.

Expected result: The metaFetch helper is ready. You have a valid access token and ad account ID stored in .env. A quick test calling graph.facebook.com/v20.0/me with your token should return your name.

2

Build the Campaign Insights API Route

The Marketing API's insights endpoint returns performance metrics for any ad object. You can request insights on a campaign (which aggregates all ad sets and ads beneath it), an ad set, or an individual ad. The endpoint pattern is: `/{ad-object-id}/insights` with a `fields` parameter listing the metrics you want and a `date_preset` or `time_range` parameter controlling the date window. The most commonly needed fields for a campaign dashboard are: `campaign_name`, `campaign_id`, `objective`, `status`, `spend`, `impressions`, `clicks`, `reach`, `frequency`, `cpm`, `cpc`, `ctr`, `cpp`, `actions` (conversions), `cost_per_action_type`, and `purchase_roas`. The `purchase_roas` field is an array of objects with value strings — you need to parse the float from the first element. The `actions` field is also an array that you filter for the action type you care about (e.g., `omni_purchase`, `lead`). To get insights for all campaigns in an ad account at once, request them from the ad account node with a `level=campaign` parameter: `/{ad-account-id}/insights?level=campaign&fields=...`. This returns all campaigns in one paginated response rather than requiring individual requests for each campaign ID. Date presets supported include: `today`, `yesterday`, `last_7d`, `last_14d`, `last_30d`, `last_month`, `this_month`, `last_quarter`, `this_year`. For custom date ranges, use `time_range={since:'2024-01-01',until:'2024-01-31'}` as a JSON string in the query parameter. The response is paginated with a `paging.next` URL for subsequent pages — for most dashboards fetching the first page of up to 25 campaigns is sufficient. The API route should accept query parameters for the date range, and optionally for filtering by campaign status (ACTIVE, PAUSED, DELETED). Return the campaigns array with parsed numeric values — the API returns spend and ROAS as strings, which should be converted to numbers for chart rendering.

Bolt.new Prompt

Create a Next.js API route at app/api/facebook/campaigns/route.ts that fetches campaign performance data from Meta Marketing API. Accept query params: datePreset (default 'last_30d') and status (default 'ACTIVE'). Call the Marketing API at /{META_AD_ACCOUNT_ID}/insights with level=campaign, fields for campaign_name, campaign_id, objective, status, spend, impressions, clicks, cpm, cpc, ctr, purchase_roas, and actions. Parse spend as a float and purchase_roas from its array format. Return a campaigns array sorted by spend descending. Use the metaFetch helper from lib/meta.ts.

Paste this in Bolt.new chat

app/api/facebook/campaigns/route.ts
1// app/api/facebook/campaigns/route.ts
2import { NextResponse } from 'next/server';
3import { metaFetch } from '@/lib/meta';
4
5const INSIGHTS_FIELDS = [
6 'campaign_name',
7 'campaign_id',
8 'objective',
9 'status',
10 'spend',
11 'impressions',
12 'clicks',
13 'reach',
14 'cpm',
15 'cpc',
16 'ctr',
17 'purchase_roas',
18 'actions',
19 'cost_per_action_type',
20].join(',');
21
22interface MetaInsight {
23 campaign_id: string;
24 campaign_name: string;
25 objective: string;
26 status?: string;
27 spend: string;
28 impressions: string;
29 clicks: string;
30 reach: string;
31 cpm: string;
32 cpc: string;
33 ctr: string;
34 purchase_roas?: Array<{ action_type: string; value: string }>;
35 actions?: Array<{ action_type: string; value: string }>;
36}
37
38export async function GET(request: Request) {
39 const { searchParams } = new URL(request.url);
40 const datePreset = searchParams.get('datePreset') || 'last_30d';
41 const status = searchParams.get('status') || 'ACTIVE';
42
43 const adAccountId = process.env.META_AD_ACCOUNT_ID;
44 if (!adAccountId) {
45 return NextResponse.json({ error: 'META_AD_ACCOUNT_ID is not configured' }, { status: 500 });
46 }
47
48 try {
49 const data = await metaFetch<{ data: MetaInsight[] }>(
50 `${adAccountId}/insights`,
51 {
52 params: {
53 level: 'campaign',
54 fields: INSIGHTS_FIELDS,
55 date_preset: datePreset,
56 filtering: JSON.stringify([{ field: 'campaign.delivery_info', operator: 'IN', value: [status] }]),
57 limit: '50',
58 },
59 }
60 );
61
62 const campaigns = (data.data || []).map((insight) => ({
63 id: insight.campaign_id,
64 name: insight.campaign_name,
65 objective: insight.objective,
66 spend: parseFloat(insight.spend || '0'),
67 impressions: parseInt(insight.impressions || '0', 10),
68 clicks: parseInt(insight.clicks || '0', 10),
69 reach: parseInt(insight.reach || '0', 10),
70 cpm: parseFloat(insight.cpm || '0'),
71 cpc: parseFloat(insight.cpc || '0'),
72 ctr: parseFloat(insight.ctr || '0'),
73 roas: insight.purchase_roas?.[0]
74 ? parseFloat(insight.purchase_roas[0].value)
75 : null,
76 purchases: insight.actions?.find((a) => a.action_type === 'omni_purchase')
77 ? parseInt(insight.actions.find((a) => a.action_type === 'omni_purchase')!.value, 10)
78 : 0,
79 }));
80
81 campaigns.sort((a, b) => b.spend - a.spend);
82
83 const totals = campaigns.reduce(
84 (acc, c) => ({
85 spend: acc.spend + c.spend,
86 impressions: acc.impressions + c.impressions,
87 clicks: acc.clicks + c.clicks,
88 purchases: acc.purchases + c.purchases,
89 }),
90 { spend: 0, impressions: 0, clicks: 0, purchases: 0 }
91 );
92
93 return NextResponse.json({ campaigns, totals, datePreset });
94 } catch (err) {
95 const message = err instanceof Error ? err.message : 'Failed to fetch campaigns';
96 return NextResponse.json({ error: message }, { status: 500 });
97 }
98}

Pro tip: The Marketing API rate limit is based on ad account activity level. High-spend accounts get more API calls per hour. If you hit rate limits (error code 17 or 613), implement caching on the API route — campaign insights don't change faster than every few minutes.

Expected result: The API route returns campaign performance data as a JSON array. You can verify it by calling /api/facebook/campaigns?datePreset=last_7d in the browser and seeing your real campaign data with parsed numeric values.

3

Build the Analytics Dashboard React Component

With the API route returning structured data, build the React dashboard components that display it. A well-designed Facebook Ads dashboard typically has three sections: a summary header with total spend, total ROAS, and date range; a top-level metrics bar with impressions, clicks, CTR, and CPM; and a data table or card grid showing individual campaign performance. For ROAS (Return on Ad Spend), calculate it as total purchase revenue divided by total ad spend. The Marketing API returns ROAS as a multiplier — a value of 3.5 means every dollar spent returned $3.50 in revenue. Display it as '3.5x' with green coloring for values above 2x and red for below 1x. For campaigns without purchase tracking (awareness or traffic objectives), ROAS will be null — handle this gracefully by showing 'N/A'. The date range picker should offer preset options (Last 7 days, Last 30 days, Last quarter, This year) that map to Meta's date_preset values. When the user changes the date range, the component re-fetches data by updating the query parameter on the API route call. For charts, Recharts works well in Bolt-generated Next.js projects. A bar chart comparing spend across campaigns, a line chart showing daily spend trend (requires a separate sparklines API call with time_increment=1), and a scatter plot of CPC vs CTR for identifying efficient campaigns are all high-value visualizations. For the spend trend, use the same insights endpoint but add `time_increment=1` to get daily breakdowns instead of aggregated totals.

Bolt.new Prompt

Build a Facebook Ads dashboard page at /facebook-ads. It should fetch from /api/facebook/campaigns with a date preset selector (Last 7d / Last 30d / Last 90d). Show: (1) a summary header with total spend formatted as currency, total clicks, average CTR, and average ROAS; (2) a Recharts bar chart of spend by campaign; (3) a sortable table of campaigns with columns for Name, Spend, Impressions, Clicks, CTR, CPC, and ROAS. Color the ROAS column green if above 2x, yellow between 1-2x, red below 1x. Add a loading skeleton while data fetches and an error state if the API fails.

Paste this in Bolt.new chat

Pro tip: Display currency values using Intl.NumberFormat with the user's locale and 'USD' currency style. Facebook Ads spend is always returned in the ad account's currency — check the currency field on the ad account object if you support multiple currencies.

Expected result: The dashboard renders with real campaign data, a functional date range selector, and a bar chart of campaign spend. The table sorts by clicking column headers, with ROAS values color-coded by performance.

4

Add Campaign Creation Endpoint

Creating campaigns via the Marketing API requires the `ads_management` permission in addition to `ads_read`. The creation flow involves three sequential API calls: create a Campaign object (the top-level container with objective and spending limits), create an Ad Set (targeting, budget, schedule, and bidding), and create an Ad (the creative that users actually see). Each step depends on the ID returned by the previous step. For the Campaign, the key fields are `name`, `objective` (OUTCOME_SALES, OUTCOME_LEADS, OUTCOME_TRAFFIC, OUTCOME_AWARENESS, OUTCOME_ENGAGEMENT, OUTCOME_APP_PROMOTION), `status` (PAUSED is safest for initial creation), and optionally `special_ad_categories` (required if running housing, employment, or credit ads). Set status to PAUSED initially so you can review the campaign before it starts spending. The Ad Set specifies where and when the campaign runs and how much it spends. Key fields: `campaign_id` (from the campaign just created), `name`, `daily_budget` or `lifetime_budget` (in cents, so $50/day = 5000), `start_time` and `end_time` (ISO 8601 format), `targeting` (an object specifying geo, age, interests, behaviors), `billing_event` (IMPRESSIONS is standard), and `optimization_goal` (OFFSITE_CONVERSIONS for purchase tracking, LINK_CLICKS for traffic). The targeting object has many fields. A minimal targeting object includes `geo_locations` (countries array, e.g., `{countries: ['US']}`), `age_min` (18-65), and `age_max` (18-65). Interest-based targeting uses `flexible_spec` with interest IDs that you look up via the Targeting Search API. For a working MVP, use broad targeting with just geo and age — detailed interest targeting can be added later. Important: the Meta Marketing API performs validation and may return errors for budget minimums (typically $1/day minimum), missing required fields, or campaigns in restricted categories. Always handle the error response and display Meta's error message directly — it is usually specific and actionable.

Bolt.new Prompt

Create a Next.js API route at app/api/facebook/create-campaign/route.ts that accepts POST with { name, objective, dailyBudgetCents, targetCountries, ageMin, ageMax }. Make three sequential Meta API calls: (1) create a Campaign with PAUSED status at /{META_AD_ACCOUNT_ID}/campaigns, (2) create an Ad Set at /{META_AD_ACCOUNT_ID}/adsets with the campaign_id from step 1, daily_budget, and targeting. Return { campaignId, adSetId } on success. Include clear error messages for budget minimums and missing permissions.

Paste this in Bolt.new chat

app/api/facebook/create-campaign/route.ts
1// app/api/facebook/create-campaign/route.ts
2import { NextResponse } from 'next/server';
3import { metaFetch } from '@/lib/meta';
4
5export async function POST(request: Request) {
6 const adAccountId = process.env.META_AD_ACCOUNT_ID;
7 if (!adAccountId) {
8 return NextResponse.json({ error: 'META_AD_ACCOUNT_ID is not configured' }, { status: 500 });
9 }
10
11 const {
12 name,
13 objective = 'OUTCOME_TRAFFIC',
14 dailyBudgetCents = 500, // $5.00
15 targetCountries = ['US'],
16 ageMin = 18,
17 ageMax = 65,
18 } = await request.json();
19
20 try {
21 // Step 1: Create Campaign
22 const campaign = await metaFetch<{ id: string }>(
23 `${adAccountId}/campaigns`,
24 {
25 method: 'POST',
26 body: {
27 name,
28 objective,
29 status: 'PAUSED', // Always start paused
30 special_ad_categories: [],
31 },
32 }
33 );
34
35 // Step 2: Create Ad Set
36 const startTime = new Date();
37 startTime.setHours(startTime.getHours() + 1);
38
39 const adSet = await metaFetch<{ id: string }>(
40 `${adAccountId}/adsets`,
41 {
42 method: 'POST',
43 body: {
44 campaign_id: campaign.id,
45 name: `${name} - Ad Set`,
46 daily_budget: dailyBudgetCents,
47 billing_event: 'IMPRESSIONS',
48 optimization_goal: 'LINK_CLICKS',
49 bid_strategy: 'LOWEST_COST_WITHOUT_CAP',
50 targeting: {
51 geo_locations: { countries: targetCountries },
52 age_min: ageMin,
53 age_max: ageMax,
54 },
55 start_time: startTime.toISOString(),
56 status: 'PAUSED',
57 },
58 }
59 );
60
61 return NextResponse.json({
62 success: true,
63 campaignId: campaign.id,
64 adSetId: adSet.id,
65 message: `Campaign "${name}" created with status PAUSED. Add creatives before activating.`,
66 });
67 } catch (err) {
68 const message = err instanceof Error ? err.message : 'Campaign creation failed';
69 return NextResponse.json({ error: message }, { status: 500 });
70 }
71}

Pro tip: Always create campaigns with status PAUSED. Activating a campaign (setting status to ACTIVE) should require explicit confirmation from the user since it starts spending budget immediately. Build an explicit 'Activate Campaign' button separate from 'Create Campaign.'

Expected result: The create campaign endpoint returns a campaign ID and ad set ID. You can verify the campaign exists in Facebook Ads Manager under its PAUSED status, with the correct objective and targeting settings.

5

Deploy and Configure for Production Access

During development in Bolt's WebContainer, all outbound Marketing API calls work correctly — you can read campaign data and test the dashboard with real numbers. The one integration feature that requires deployment is webhook-based data: Lead Ads form submissions and real-time event notifications use Meta's webhook system, which sends POST requests to your server. Since the WebContainer cannot receive incoming connections, these webhooks only function after deployment. Deploy to Netlify via Settings → Applications → Connect Netlify, or use Bolt Cloud (click Publish). After deployment, go to your Netlify or Bolt Cloud dashboard and add environment variables: `META_ACCESS_TOKEN` and `META_AD_ACCOUNT_ID` with your production values. For production use with other users' ad accounts (building a multi-tenant dashboard), your Meta App must go through App Review. In Meta for Developers, under your app, go to App Review → Permissions and Features. Request the `ads_read` permission (and `ads_management` if you need write access). You must provide a screencast demonstrating your integration, a privacy policy URL, and a business verification. Meta typically completes review in two to four weeks. Without App Review, your app can only access data for users who have a role in your Meta App (developers, testers, admins). For a single-tenant dashboard (your own ad account only), App Review is not required. Use a System User token from Business Manager, which does not require any end-user OAuth flow and does not expire. This is the production-ready approach for internal tools and agency dashboards accessing specific accounts with explicit permission.

Bolt.new Prompt

Add a /api/facebook/health route that checks if META_ACCESS_TOKEN and META_AD_ACCOUNT_ID are configured and tests the Meta API connection by calling /me?fields=id,name. Return { configured: true, userId, adAccountId } on success or { configured: false, error } if env vars are missing or the token is invalid. Display this health check status on the dashboard settings page.

Paste this in Bolt.new chat

.env.example
1# Environment variables to add in Netlify dashboard or Bolt Cloud
2# Settings Environment Variables (Netlify) or Secrets (Bolt Cloud)
3
4META_ACCESS_TOKEN=EAAxxxxxxxxxxxxxxxxxx
5META_AD_ACCOUNT_ID=act_1234567890
6
7# Optional: restrict to specific ad account for security
8# NEXT_PUBLIC_APP_URL=https://your-app.netlify.app

Pro tip: Meta access tokens can be long-lived (60 days for User tokens) but not permanent. Consider implementing a token refresh flow or setting up a cron job that alerts you before expiry. System User tokens generated in Business Manager do not expire and are the best choice for production deployments.

Expected result: The app is deployed and the Meta API connection works with production credentials. The health check endpoint returns your Meta user ID and confirms the ad account is accessible.

Common use cases

Campaign Performance Analytics Dashboard

Build a custom analytics dashboard that displays Facebook and Instagram campaign performance across all active campaigns. Show spend, impressions, clicks, CPM, CPC, CTR, and ROAS for any date range. Drill down from campaigns to ad sets to individual ads. Compare performance across time periods to spot trends that Ads Manager's default views obscure.

Bolt.new Prompt

Build a Facebook Ads analytics dashboard. Create a Next.js API route at /api/facebook/campaigns that calls Meta's Marketing API to fetch all campaigns in an ad account with their insights (spend, impressions, clicks, cpm, ctr, purchase_roas) for a given date range. Accept adAccountId and dateRange as query params. Build a React dashboard with a date range picker, a summary row of total spend and ROAS, and a sortable table of campaigns. Store META_ACCESS_TOKEN and META_AD_ACCOUNT_ID in process.env.

Copy this prompt to try it in Bolt.new

Ad Creative Performance Analyzer

Pull ad-level performance data to identify which creatives are driving results and which are fatiguing. Compare hook rates, scroll-stop rates (measured via thumb-stop ratio), and conversion rates across ad creative variants to inform your next creative sprint with data rather than guesswork.

Bolt.new Prompt

Create an ad creative analyzer using Facebook Marketing API. Build a Next.js API route at /api/facebook/ads that fetches all active ads in an ad account with ad creative thumbnails, ad names, and insights (impressions, clicks, spend, ctr, cpc, purchase_roas, cost_per_unique_click). Create a React grid view showing each ad's thumbnail, key metrics, and a performance score badge. Highlight top performers in green and underperformers in red. Use META_ACCESS_TOKEN from process.env.

Copy this prompt to try it in Bolt.new

Campaign Creator and Budget Manager

Build a simplified campaign creation interface that lets non-technical team members launch new Facebook ad campaigns without navigating Ads Manager. Set budgets, schedules, targeting parameters, and ad creatives through a clean form interface that calls the Marketing API to create the campaign objects.

Bolt.new Prompt

Build a Facebook ad campaign creator. Create Next.js API routes at /api/facebook/create-campaign (POST) and /api/facebook/update-budget (PATCH). The create endpoint should create a campaign, ad set with targeting, and ad via the Meta Marketing API using META_ACCESS_TOKEN from process.env. The update endpoint should update daily or lifetime budget on an existing ad set. Build a React form with campaign name, objective (OUTCOME_SALES, OUTCOME_LEADS, OUTCOME_TRAFFIC), daily budget, start/end date, and a targeting section. Show a success message with the campaign ID after creation.

Copy this prompt to try it in Bolt.new

Troubleshooting

Error 190: Invalid OAuth access token or Session has expired

Cause: The META_ACCESS_TOKEN has expired. User Access Tokens from Graph API Explorer expire in 1-2 hours. Even 'extended' tokens expire after 60 days.

Solution: Generate a new access token in Graph API Explorer and update your .env file. For a permanent solution, create a System User in Business Manager → Settings → Users → System Users, generate a token there, and use it instead — System User tokens do not expire.

Error 200 or 'Permissions error' — missing ads_read or ads_management permission

Cause: The access token was generated without including the required permissions. Graph API Explorer requires you to explicitly check each permission checkbox when generating a token.

Solution: Return to developers.facebook.com/tools/explorer, select your app, click 'Generate Access Token,' and explicitly check ads_read (and ads_management if needed) before generating. Permissions are not inherited — you must request them each time.

API returns empty data array even though campaigns exist in Ads Manager

Cause: The ad account ID format is incorrect, or the token does not have access to that specific ad account. The ad account ID must include the 'act_' prefix.

Solution: Verify the ad account ID includes the 'act_' prefix (act_1234567890, not 1234567890). Also confirm the token's user has at least 'Analyst' access to the ad account in Business Manager → Ad Accounts → People.

typescript
1// Ensure act_ prefix is included:
2const adAccountId = process.env.META_AD_ACCOUNT_ID; // Should be: act_1234567890
3// If stored without prefix, add it:
4const formattedId = adAccountId?.startsWith('act_') ? adAccountId : `act_${adAccountId}`;

Webhooks for Lead Ads form submissions are not arriving at the API route

Cause: Webhooks cannot reach Bolt's WebContainer preview URL. Meta sends an HTTP POST to your webhook URL, which requires a publicly accessible server.

Solution: Deploy to Netlify or Bolt Cloud to get a stable HTTPS URL. In Meta for Developers, navigate to your app → Webhooks → Add Subscription, and enter your deployed URL as the callback. Meta requires your endpoint to respond to a GET verification request with the hub.challenge parameter before activating the webhook.

typescript
1// Webhook verification handler (GET request from Meta):
2export async function GET(request: Request) {
3 const { searchParams } = new URL(request.url);
4 const mode = searchParams.get('hub.mode');
5 const token = searchParams.get('hub.verify_token');
6 const challenge = searchParams.get('hub.challenge');
7 if (mode === 'subscribe' && token === process.env.META_WEBHOOK_VERIFY_TOKEN) {
8 return new Response(challenge, { status: 200 });
9 }
10 return new Response('Forbidden', { status: 403 });
11}

Best practices

  • Use System User tokens from Business Manager for production deployments — they do not expire and do not require a logged-in Facebook user, making them ideal for automated dashboards and server-side only access.
  • Cache Marketing API responses for at least 5 minutes on your API route — campaign insights do not update in real time and the API has per-ad-account hourly rate limits that vary based on account spend level.
  • Store META_ACCESS_TOKEN and META_AD_ACCOUNT_ID as server-side environment variables without the NEXT_PUBLIC_ prefix to prevent them from appearing in client-side JavaScript bundles.
  • Always create campaigns with status PAUSED initially, and require explicit user confirmation before changing status to ACTIVE — active campaigns spend budget immediately.
  • Request only the specific fields you need using the fields parameter rather than fetching all available fields — this reduces response size and API quota consumption significantly.
  • Handle Meta's nested error format explicitly: error responses have a JSON body with error.message and error.code, not a plain text body, so ensure your error handling reads the JSON before extracting the message.
  • For multi-tenant applications where users connect their own ad accounts, Meta App Review is required for ads_read and ads_management permissions — budget 2-4 weeks for the review process and prepare a video demonstration of your integration.
  • Use the /insights endpoint with level=campaign for account-level aggregation rather than fetching each campaign individually — one API call for all campaigns is far more efficient than N calls for N campaigns.

Alternatives

Frequently asked questions

Does Bolt.new have a native Facebook Ads integration?

No — Bolt.new does not have a native Meta Marketing API connector. The integration requires creating Next.js API routes that call Meta's Graph API. Bolt's AI assistant can generate this boilerplate from a prompt, which significantly reduces setup time even without a native connector.

Can I test the Facebook Ads dashboard in Bolt's preview without deploying?

Yes — all outbound calls to Meta's Marketing API work in Bolt's WebContainer preview since they are standard HTTPS requests. You can read campaign data, build the dashboard, and test performance metrics entirely in the preview. The only feature that requires deployment is incoming webhooks, such as Lead Ads form submission notifications.

Do I need Meta App Review to read my own ad account data?

No — App Review is only required when your app accesses other users' ad accounts (building a SaaS product or agency tool). For accessing your own ad account data, or accounts where you have an admin or analyst role in Business Manager, App Review is not needed. Use a System User token from Business Manager for long-term access without expiry.

Why is the Marketing API returning an empty data array even though I have active campaigns?

The most common cause is an incorrect ad account ID format — it must include the 'act_' prefix (act_1234567890). The second common cause is insufficient permissions on the access token — verify it includes ads_read. The third is that the token's associated user does not have at least Analyst access to the specific ad account in Business Manager.

How do I get ROAS data from the Meta Marketing API?

Include 'purchase_roas' in your fields parameter. The API returns it as an array of objects like [{action_type: 'omni_purchase', value: '3.45'}]. Parse the float from the first element's value property. Note that ROAS only appears for campaigns tracking purchase conversions — awareness and traffic campaigns will return an empty or absent purchase_roas field.

What is the rate limit for the Meta Marketing API?

Rate limits are calculated per ad account based on the account's ad spend tier. Higher-spending accounts receive more API calls per hour. Typically, accounts get several hundred calls per hour. If you hit limits (error code 17 or 613), the response includes a Retry-After header. Caching responses for 5-15 minutes eliminates most rate limit issues for dashboard use cases.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.