To connect FlutterFlow to Twitter Ads, you must deploy a Firebase Cloud Function that signs every request with OAuth 1.0a (HMAC-SHA1) — FlutterFlow API Calls cannot generate that signature themselves. Once the proxy is live, create an API Group in FlutterFlow pointing to your Function URL, then parse campaign impressions and spend into widgets using JSON Path.
| Fact | Value |
|---|---|
| Tool | Twitter Ads |
| Category | Marketing |
| Method | FlutterFlow API Call |
| Difficulty | Advanced |
| Time required | 90 minutes |
| Last updated | July 2026 |
Building a Twitter Ads Dashboard in FlutterFlow
The X/Twitter Ads API still runs on OAuth 1.0a — a signing scheme that wraps every HTTP request with an HMAC-SHA1 digest built from your consumer key, consumer secret, access token, access token secret, a timestamp, and a random nonce. If any of those ingredients are wrong or the timestamp is stale, the request returns 401. More importantly, FlutterFlow's API Call panel only supports static headers: it cannot run cryptographic code per request. Trying to paste a pre-signed header once and reuse it will fail immediately because the timestamp is single-use.
This means the Twitter Ads integration is one of the few FlutterFlow scenarios where a Cloud Function proxy is not just recommended — it is the only option. The Function lives on Google's servers, holds all four credentials securely in environment variables, generates a fresh signature for each call, and forwards the request to ads-api.twitter.com/12. FlutterFlow knows only the Function's URL, which receives a simple JSON payload and returns clean campaign data. Your four OAuth secrets never touch the compiled Dart app.
Access to the X Ads API also requires applying for and receiving an approved developer application through the X Developer Portal. Without approval, credentials alone will not get you past the API gateway. Budget extra time to apply and wait for approval before starting the FlutterFlow build. Once access is confirmed, this guide walks you through deploying the proxy and wiring it to a FlutterFlow dashboard showing campaign spend, impressions, and engagement rates.
Integration method
Twitter Ads API v12 uses OAuth 1.0a, which requires a per-request HMAC-SHA1 signature calculated from four credentials plus a timestamp and nonce. FlutterFlow API Calls use static headers and cannot perform that cryptographic signing step, so a Firebase Cloud Function (or Supabase Edge Function) must hold all four credentials, sign each outbound request, and return clean JSON to FlutterFlow. FlutterFlow then reads the Function's URL as a regular API Group and displays campaign metrics in charts or tables.
Prerequisites
- An X (Twitter) developer account with an approved Ads API application
- All four OAuth 1.0a credentials: Consumer Key, Consumer Secret, Access Token, Access Token Secret
- A Firebase project with Cloud Functions enabled (Blaze pay-as-you-go plan required)
- A FlutterFlow project on a plan that supports API Calls and Custom Actions
- Basic familiarity with pasting and deploying a Firebase Cloud Function via the Firebase console or CLI
Step-by-step guide
Apply for X Ads API access and collect OAuth 1.0a credentials
Before writing a single line of code, you need approval from X to use the Ads API. Navigate to developer.twitter.com, create a developer app if you haven't already, and apply for Ads API access from your app's dashboard. Approval can take several days. Once approved, open your app in the X Developer Portal and locate the Keys and Tokens section. You need four values: Consumer Key (also called API Key), Consumer Secret (API Key Secret), Access Token, and Access Token Secret. The Access Token and Secret are tied to the X account that will own the ad spend — make sure you're generating them for the correct account. Copy all four values to a secure temporary location such as a password manager. Also note your Ads Account ID (also called Account ID or Advertiser ID), which appears in the X Ads dashboard under Account Information. You will need it as a path variable in every Ads API call. Do not store any of these credentials in your FlutterFlow project or in any client-side file — they will be placed exclusively in the Cloud Function environment in the next step.
Pro tip: The Access Token generated in the Developer Portal belongs to your personal X account. If you want the app to read a different advertiser's data, that advertiser must grant your app access through the X Ads Authorization flow.
Expected result: You have all four OAuth 1.0a credentials plus the Ads Account ID saved securely and ready to paste into the Cloud Function configuration.
Deploy a Firebase Cloud Function that signs and proxies Ads API requests
In your Firebase project, navigate to Cloud Functions and create a new function (or use the Firebase CLI locally). The function receives a request from FlutterFlow with an `endpoint` query parameter — for example `/12/accounts/{account_id}/campaigns` — and a `params` object, computes the full OAuth 1.0a Authorization header using the `oauth-1.0a` npm package and Node's built-in `crypto` module, then forwards the request to `https://ads-api.twitter.com` and returns the response as JSON. Store all four credentials as Firebase environment variables using `firebase functions:config:set` or as Secret Manager secrets (recommended). The function must accept CORS requests from your FlutterFlow web preview origin if you plan to test on web — add the `cors` npm package. Deploy with `firebase deploy --only functions`. After deployment, copy the Function's HTTPS trigger URL from the Firebase console; this is the URL you will paste into FlutterFlow in the next step. Note: the Functions URL itself is not a secret — it is the combination of that URL plus the server-side credentials that provides security.
1// Firebase Cloud Function — index.js2const functions = require('firebase-functions');3const admin = require('firebase-admin');4const OAuth = require('oauth-1.0a');5const crypto = require('crypto');6const axios = require('axios');7const cors = require('cors')({ origin: true });89admin.initializeApp();1011const oauth = OAuth({12 consumer: {13 key: process.env.TWITTER_CONSUMER_KEY,14 secret: process.env.TWITTER_CONSUMER_SECRET15 },16 signature_method: 'HMAC-SHA1',17 hash_function(base_string, key) {18 return crypto.createHmac('sha1', key).update(base_string).digest('base64');19 }20});2122const token = {23 key: process.env.TWITTER_ACCESS_TOKEN,24 secret: process.env.TWITTER_ACCESS_TOKEN_SECRET25};2627exports.twitterAdsProxy = functions.https.onRequest((req, res) => {28 cors(req, res, async () => {29 const endpoint = req.query.endpoint || '';30 const url = `https://ads-api.twitter.com${endpoint}`;31 const requestData = { url, method: 'GET' };32 const headers = oauth.toHeader(oauth.authorize(requestData, token));33 headers['Content-Type'] = 'application/json';3435 try {36 const response = await axios.get(url, {37 headers,38 params: req.query.params ? JSON.parse(req.query.params) : {}39 });40 res.status(200).json(response.data);41 } catch (err) {42 const status = err.response ? err.response.status : 500;43 res.status(status).json({ error: err.message });44 }45 });46});47Pro tip: Set environment variables with the Firebase CLI: `firebase functions:config:set twitter.consumer_key="YOUR_KEY" twitter.consumer_secret="YOUR_SECRET"` and access them as `functions.config().twitter.consumer_key` — or use Secret Manager for production workloads.
Expected result: The Cloud Function deploys successfully and you can test it in your browser by visiting its URL with an `?endpoint=/12/accounts/{account_id}/campaigns` parameter and seeing a JSON response with your campaign list.
Create an API Group in FlutterFlow pointing to the Cloud Function
With the Cloud Function live, open your FlutterFlow project and click API Calls in the left navigation panel. Click the + Add button and choose Create API Group. Name it TwitterAdsProxy. In the Base URL field, paste your Firebase Cloud Function's HTTPS trigger URL — for example `https://us-central1-your-project.cloudfunctions.net/twitterAdsProxy`. You do not need authentication headers at the FlutterFlow level because authentication is handled inside the Cloud Function; simply leave the Headers section empty or add a Content-Type: application/json header if your function requires it. Next, click + Add API Call inside the group. Name the call GetCampaigns. Set the method to GET. In the Query Params section, click + Add Variable to create an `endpoint` variable — set its default to `/12/accounts/YOUR_ACCOUNT_ID/campaigns` (replace with your actual account ID). Add a second variable `params` with a default of `{}` if you want to pass date ranges or additional filters. Click Save. Now open the Response & Test tab, click Test, and check that the response body shows your real campaign data as JSON. If the test returns an empty array, verify that your Cloud Function is deployed and that the account ID in the endpoint path is correct.
1// API Call config reference (not pasted into FlutterFlow — visual setup)2{3 "group_name": "TwitterAdsProxy",4 "base_url": "https://us-central1-YOUR_PROJECT.cloudfunctions.net/twitterAdsProxy",5 "calls": [6 {7 "name": "GetCampaigns",8 "method": "GET",9 "query_params": {10 "endpoint": "/12/accounts/{{account_id}}/campaigns",11 "params": "{}"12 }13 },14 {15 "name": "GetCampaignStats",16 "method": "GET",17 "query_params": {18 "endpoint": "/12/stats/accounts/{{account_id}}",19 "params": "{\"entity\":\"CAMPAIGN\",\"entity_ids\":\"{{campaign_id}}\",\"start_time\":\"{{start_date}}\",\"end_time\":\"{{end_date}}\"}"20 }21 }22 ]23}Pro tip: Create a second API Call called GetCampaignStats with an endpoint variable pointing to `/12/stats/accounts/{account_id}` — this is the endpoint that returns impressions, spend, clicks, and engagements per campaign.
Expected result: The API Group shows a green test result with a list of your campaigns in JSON format. Both GetCampaigns and GetCampaignStats appear as separate calls inside the TwitterAdsProxy group.
Parse campaign metrics with JSON Path and create a Data Type
After a successful test, FlutterFlow can automatically generate JSON Path expressions from the response. In the GetCampaigns call, click the Response & Test tab and paste a sample JSON response from your last test. Click Generate JSON Paths. FlutterFlow will display a tree of extractable fields. Map the following paths into your Data Type: `$.data[*].id` for campaign ID, `$.data[*].name` for campaign name, `$.data[*].status` for the current state (ACTIVE, PAUSED, etc.). For the GetCampaignStats call, paste a sample stats response and map `$.data[0].id_data[0].metrics.impressions[0]` for total impressions, `$.data[0].id_data[0].metrics.billed_charge_local_micro[0]` for spend in microcurrency (divide by 1,000,000 for dollars), and `$.data[0].id_data[0].metrics.engagements[0]` for engagements. Create a Dart Data Type called CampaignSummary with String fields (id, name, status) and Integer fields (impressions, engagements, spendMicro). Then go to the page that will host your dashboard, open its Backend Query settings, select the TwitterAdsProxy → GetCampaigns call, and set the Return Type to a list of CampaignSummary. FlutterFlow will automatically deserialize the API response into your Data Type list whenever the page loads.
1// JSON Path examples for Twitter Ads API response2// Campaign list response3$.data[*].id // campaign IDs4$.data[*].name // campaign names5$.data[*].status // ACTIVE | PAUSED | DELETED6$.data[*].currency // currency code e.g. USD78// Stats response (impressions, spend, engagements)9$.data[0].id_data[0].metrics.impressions[0]10$.data[0].id_data[0].metrics.billed_charge_local_micro[0]11$.data[0].id_data[0].metrics.engagements[0]12$.data[0].id_data[0].metrics.clicks[0]Pro tip: Twitter Ads API returns spend in microcurrency (1 USD = 1,000,000 micros). Use a Dart Custom Function to divide `spendMicro` by 1000000.0 and format it as a currency string before displaying it.
Expected result: The CampaignSummary Data Type is created and FlutterFlow can populate a list of it from the GetCampaigns response. JSON Path fields are validated (green checkmarks) in the Response tab.
Bind campaign data to dashboard widgets
Now that FlutterFlow has a typed data source, you can bind it to UI components. On your dashboard page, add a ListView widget and set its data source to the CampaignSummary list from the GetCampaigns backend query. Inside each list item, add a Text widget bound to `item.name`, a Badge or Chip bound to `item.status` (show green for ACTIVE, grey for PAUSED), and a Subtext bound to formatted impressions from the GetCampaignStats call. For the stats detail view, add a separate page and pass the selected campaign ID as a page parameter. On that page, use the GetCampaignStats API Call as a page-level backend query with the campaign ID variable injected, then bind impressions, spend, and engagements to stat cards. For a chart view, add a BarChart widget (available in FlutterFlow's chart panel), set the X-axis to campaign name and Y-axis to impressions from your data list. Pull-to-refresh behavior is configured by wrapping the ListView in a RefreshIndicator widget in the widget tree and setting its On Refresh action to trigger the backend query again. Cache aggressively: rate limits on some Ads API endpoints cap at 60 requests per 15 minutes per app — a busy ListView that refetches on every scroll will hit this ceiling quickly. Consider storing the last-fetched results in App State and only re-fetching when the user explicitly pulls to refresh.
Pro tip: If you are building for multiple advertisers, store each advertiser's Account ID in Firestore and pass it as a variable to the endpoint parameter — no need for separate Cloud Functions per account.
Expected result: The dashboard page shows a scrollable list of campaigns with names and statuses. Tapping a campaign navigates to a detail screen with spend, impressions, and engagement metrics pulled live from the Cloud Function proxy.
Common use cases
Mobile ad performance dashboard for social media managers
An agency builds a branded Flutter app where account managers check X/Twitter campaign spend and impressions for multiple clients on the go. The app authenticates managers with Supabase Auth, then calls the signed proxy to pull each account's metrics and render them in a bar chart and summary cards.
Show me a screen with a dropdown of campaign names, and below it a bar chart of daily impressions and a spend counter for the selected campaign, refreshed with a pull-to-refresh gesture.
Copy this prompt to try it in FlutterFlow
Post-campaign reporting tool for e-commerce brands
A DTC brand's marketing team uses a FlutterFlow app to view end-of-week campaign summaries: total spend, click-through rates, and video completions. The app pulls data from the Cloud Function proxy and exports a summary list the team can screenshot for weekly reports.
Build a campaign list view where tapping a campaign reveals a detail screen with impressions, clicks, spend, and engagement rate for the last 7 days.
Copy this prompt to try it in FlutterFlow
Real-time budget monitor with overspend alerts
A startup marketing lead configures a Flutter app to check daily ad spend against a monthly budget cap. The app calls the signed proxy on a timer, compares spend to a threshold stored in Firestore, and sends a local push notification when spend crosses 80% of the cap.
Create a budget tracker screen that shows today's X Ads spend as a progress bar against a manually set monthly budget, with a red warning when spend exceeds 80%.
Copy this prompt to try it in FlutterFlow
Troubleshooting
Cloud Function returns 401 Unauthorized from Twitter Ads API
Cause: The OAuth 1.0a signature is invalid — usually caused by a stale timestamp (clock skew), an incorrect credential value, or the wrong account's Access Token being used.
Solution: Check that the server's system clock is synchronized (Firebase Functions use Google's time servers, so this is rare but verify). Double-check all four credential values — paste them into the Firebase environment config one more time and redeploy. Confirm the Access Token matches the X account that owns the ad account you are querying.
FlutterFlow API Call test returns 'XMLHttpRequest error' or no response
Cause: CORS is blocking the request from FlutterFlow's web preview. The Cloud Function's CORS configuration may not allow requests from the FlutterFlow preview origin.
Solution: In your Cloud Function, make sure the `cors` package is initialized with `{ origin: true }` (which allows all origins) or with the specific FlutterFlow preview URL. Redeploy the function. On native iOS/Android builds, CORS is not enforced by the OS so this only affects web preview testing.
1const cors = require('cors')({ origin: true });2// Wrap your handler: cors(req, res, async () => { ... });Twitter Ads API returns 403 Forbidden: you are not authorized to access ads for this account
Cause: The Access Token used to sign the request does not have permission to manage or read the specified Ads Account ID. Either the account ID is wrong, or the Ads API access application has not been approved for that account.
Solution: Verify the Account ID in X Ads Manager (it is a numeric ID, not your handle). Confirm your developer app has been approved for Ads API access and that the approval covers the account you are querying. If accessing a different advertiser's account, that advertiser must grant your app access via the OAuth authorization flow.
Rate limit hit: API returns 429 Too Many Requests
Cause: The app is calling the stats endpoint too frequently — some Ads API endpoints cap at 60 requests per 15-minute window per app, and if multiple users share the same developer app, quotas are shared across all of them.
Solution: Add caching inside the Cloud Function using Firestore or Firebase in-memory cache: store the last response and its timestamp, and only forward to Twitter if the data is older than 5 minutes. On the FlutterFlow side, avoid re-triggering the backend query on widget rebuild — use a one-time page-load query and pull-to-refresh instead of streaming queries.
Best practices
- Store all four OAuth 1.0a credentials exclusively in Firebase environment variables or Secret Manager — never in FlutterFlow API Call headers, App Values, or Dart code.
- Cache API responses in Firestore or Firebase in-memory store inside the Cloud Function; the Ads API rate limits are per-app, not per-user, so every user's request counts against the same quota.
- Use a single Cloud Function that accepts an `endpoint` variable rather than deploying separate functions per Ads API endpoint — easier to maintain and deploy.
- Add error handling in the Cloud Function that maps Twitter's error codes (32, 64, 88, 89, 135, 161) to human-readable messages and returns them as structured JSON — FlutterFlow can then show a proper error snackbar.
- Test the Cloud Function directly in the Firebase console with its Test tab before connecting it to FlutterFlow — this isolates OAuth signing problems from FlutterFlow configuration issues.
- Request only the metric fields you need in the stats endpoint's `metric_groups` parameter — pulling all metrics is slower and counts against rate limits.
- For multi-advertiser dashboards, store Account IDs in Firestore per authenticated user rather than hardcoding them — the Cloud Function can look up the right Account ID from the user's Firestore document.
- Apply for the minimum Ads API permission scope your app needs — if you only need read analytics access, don't request write (campaign creation) permissions, which face a higher approval bar.
Alternatives
Meta Ads API uses OAuth 2.0 Bearer tokens instead of OAuth 1.0a, making it simpler to proxy — a better choice if your clients advertise primarily on Meta platforms.
Google Ads API is gRPC-based with OAuth 2.0 and has broader agency tooling; choose it if your app serves Google Ads clients at scale.
TikTok Ads API uses a simpler Access Token header and is better suited for apps targeting younger demographics with video ad performance.
Frequently asked questions
Can I create or pause campaigns from my FlutterFlow app, not just read data?
Technically yes — the Twitter Ads API supports write operations like creating campaigns, line items, and promoted tweets. However, you would need to send POST requests through your Cloud Function with the appropriate body parameters, and your Ads API application must have write scope approved. Most FlutterFlow apps focus on read-only dashboards because campaign management from mobile is complex and risky — a misfire could immediately charge your ad budget.
Why can't I just paste a pre-generated OAuth signature into the FlutterFlow header field?
OAuth 1.0a signatures include a timestamp and a random nonce, both of which must be unique per request. A signature generated at setup time will be rejected by the API within seconds because the timestamp becomes stale. The signing must happen live, at the moment of each request, which is why a server-side function is the only workable approach.
How do I test this integration without running up real ad spend?
The Twitter Ads API v12 analytics endpoints only read data — they do not create or charge for ads. Your existing campaigns' historical data is safe to read without triggering any spend. Set your test app's API calls to read from inactive or completed campaigns to avoid any interference with live campaigns.
Will this integration work on both iOS and Android?
Yes. Since the integration is a plain REST API call to your Cloud Function (not a native SDK), it compiles normally for both iOS and Android. CORS is not an issue on native builds — only the FlutterFlow web preview runs in a browser that enforces CORS policies. Make sure the Cloud Function allows cross-origin requests if you plan to also deploy a web version of the app.
What should I do if I can't get Ads API access approved?
X's Ads API approval process is selective, and some applications are denied. If approval is taking too long, consider whether your use case can be served by X's standard v2 API (which offers tweet analytics for your own account) combined with manually exported ad reports. For a managed integration that handles the approval process, RapidDev's team builds FlutterFlow integrations like this every week — book a free scoping call at rapidevelopers.com/contact.
Does the Twitter Ads API cost money to use?
The API itself requires an approved developer application, but accessing your own ad account's analytics data does not incur additional API fees beyond what you pay for the ads themselves. Third-party tools that resell Ads API data may charge extra — but if you are building your own FlutterFlow app to read your own account, the API access is part of your X developer account at no additional direct cost. Verify current terms on the X Developer Portal as policies change.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation