To integrate Lovable with X (Twitter) Ads, create a Supabase Edge Function that signs requests using OAuth 1.0a — the mandatory signing mechanism for Twitter's Ads API. Store your consumer key, consumer secret, access token, and access token secret in Cloud → Secrets. Use Lovable's chat to build X Ads campaign dashboards, promoted tweet managers, and conversation analytics tools.
Build X (Twitter) Ads campaign dashboards with OAuth 1.0a signing in Lovable
The X Ads API (formerly Twitter Ads) is the most technically demanding of the major social advertising APIs to integrate. While most modern advertising APIs (TikTok, LinkedIn, Pinterest) use standard OAuth 2.0 Bearer tokens, X's Ads API requires OAuth 1.0a — a signing mechanism where every individual request is signed with a cryptographic HMAC-SHA1 signature computed from the request URL, parameters, HTTP method, and your credentials. There is no way to generate this signature client-side without exposing your credentials, making the Edge Function proxy absolutely non-negotiable for this integration.
The X Ads API's structure follows X's three-tier advertising hierarchy: accounts (the advertiser account), campaigns (with objective and budget), line items (ad groups with targeting and bid settings), and promoted tweets (the specific tweet content being promoted). This maps to the typical advertising platform hierarchy but with X-specific terminology.
X (formerly Twitter) advertising is most effective for brands where conversation is central to the value: B2C brands that engage in real-time cultural conversations, news and media organizations, political campaigns, sports brands, and technology companies whose audience is active on X. Unlike LinkedIn (professional targeting) or Pinterest (visual discovery), X's advertising reaches consumers who are engaged in public discourse and trending topics. The targeting options reflect this: interest categories, keyword targeting, follower lookalikes, and event targeting. Due to X's multiple API version changes following its acquisition in 2022 and rebrand from Twitter, verify endpoint URLs against current X Developer documentation before implementing.
Integration method
X Ads API requires OAuth 1.0a request signing — unlike most modern APIs that use Bearer tokens, every X Ads API request must be signed with a cryptographic HMAC-SHA1 signature generated from your credentials and request parameters. This signing process must happen server-side in a Supabase Edge Function using credentials stored in Cloud → Secrets.
Prerequisites
- A Lovable account with a project that has Lovable Cloud enabled
- An X Developer account with Elevated or higher access — apply at developer.twitter.com
- An X Ads account with active campaigns and Ads API access enabled
- OAuth 1.0a credentials: consumer key, consumer secret, access token, and access token secret
- Your X Ads account ID — the numeric string in the ads.twitter.com URL
Step-by-step guide
Set up X Developer credentials with Ads API access
Set up X Developer credentials with Ads API access
Navigate to developer.twitter.com and sign in with your X account. If you do not have a developer account, click 'Apply for a developer account' and go through the application process. X requires Basic or Elevated access for API usage; Ads API access specifically requires Elevated access or a partner agreement. Once your developer account is approved, go to the Developer Portal and create a project and app. Under your app's settings, navigate to 'Keys and tokens'. You need four credential values: 1. API Key (also called Consumer Key) 2. API Key Secret (also called Consumer Secret) 3. Access Token 4. Access Token Secret The API Key and Secret are generated when you create the app. The Access Token and Secret are generated by clicking 'Generate' in the app's authentication settings — these represent your own account's authorization. For multi-user apps where other X users connect their accounts, you would implement the OAuth 1.0a three-legged flow, but for a single-account ads dashboard, your own access token is sufficient. For Ads API access, go to developer.twitter.com/en/portal/projects/{your-project}/apps/{your-app}/auth-settings and verify the app has Read, Write, and Direct Messages permissions. You also need to apply for Ads API access at developer.twitter.com/en/docs/twitter-ads-api — this may require a separate application review for your specific use case. Find your Ads account ID by logging into ads.twitter.com — it is the numeric string in the URL when viewing your campaigns.
Pro tip: X Ads API access requires Elevated developer account access at minimum. The standard Basic developer access tier does not include Ads API access. Apply for Elevated access at developer.twitter.com and mention your advertising use case in the application.
Expected result: You have an X Developer app with four OAuth 1.0a credentials: API Key, API Key Secret, Access Token, and Access Token Secret. Your developer account has Elevated access and Ads API access approved.
Store X Ads credentials in Cloud → Secrets
Store X Ads credentials in Cloud → Secrets
Open your Lovable project and navigate to Cloud → Secrets via the '+' icon next to Preview. Add the following five secrets: - Name: X_CONSUMER_KEY — Value: your API Key (Consumer Key) - Name: X_CONSUMER_SECRET — Value: your API Key Secret (Consumer Secret) - Name: X_ACCESS_TOKEN — Value: your Access Token - Name: X_ACCESS_TOKEN_SECRET — Value: your Access Token Secret - Name: X_ADS_ACCOUNT_ID — Value: your numeric X Ads account ID All five are required. OAuth 1.0a signing requires all four credential values to compute the HMAC-SHA1 signature on every request — unlike OAuth 2.0 where only the access token is needed for API calls. The signing happens in the Edge Function using Deno's Web Crypto API. X advertising credentials authorize access to campaign management and ad spend data. Lovable's security infrastructure blocks approximately 1,200 hardcoded API keys per day, but these credentials specifically require extra care — never paste them in Lovable's chat or any visible code.
Expected result: All five X Ads secrets — X_CONSUMER_KEY, X_CONSUMER_SECRET, X_ACCESS_TOKEN, X_ACCESS_TOKEN_SECRET, and X_ADS_ACCOUNT_ID — appear in Cloud → Secrets with masked values.
Implement OAuth 1.0a signing and create the Edge Function
Implement OAuth 1.0a signing and create the Edge Function
The core challenge of the X Ads integration is implementing OAuth 1.0a request signing. Unlike OAuth 2.0 where you simply add an Authorization: Bearer {token} header, OAuth 1.0a requires you to: 1. Collect the request URL, HTTP method, and all query/body parameters 2. Add OAuth-specific parameters (oauth_consumer_key, oauth_nonce, oauth_signature_method, oauth_timestamp, oauth_token, oauth_version) 3. Percent-encode and sort all parameters alphabetically 4. Build the signature base string from the HTTP method, encoded URL, and encoded parameters 5. Compute the HMAC-SHA1 signature using consumer_secret&access_token_secret as the signing key 6. Build the Authorization header with all OAuth parameters and the signature Deno's Web Crypto API provides the HMAC-SHA1 implementation needed for this signing. The code below implements the full OAuth 1.0a signing process — paste it into Lovable's chat with the prompt below to generate the full Edge Function. The X Ads API base URL is https://ads-api.twitter.com/12/ (verify the version number against current documentation). Key endpoints: GET /accounts/{id}/campaigns for campaign list, GET /accounts/{id}/stats/async/query for analytics, GET /accounts/{id}/line_items for line items.
Create a Supabase Edge Function at supabase/functions/twitter-ads/index.ts. Implement OAuth 1.0a request signing using Deno's Web Crypto API (HMAC-SHA1) with credentials from Deno.env: X_CONSUMER_KEY, X_CONSUMER_SECRET, X_ACCESS_TOKEN, X_ACCESS_TOKEN_SECRET. Accept POST requests with 'action' (campaigns | line_items | stats | account) and 'params'. For 'campaigns': GET https://ads-api.twitter.com/12/accounts/{X_ADS_ACCOUNT_ID}/campaigns. For 'stats': POST https://ads-api.twitter.com/12/stats/accounts/{id} with entity type and metrics. Include CORS headers.
Paste this in Lovable chat
1// supabase/functions/twitter-ads/index.ts2// OAuth 1.0a signing for X Ads API34function percentEncode(str: string): string {5 return encodeURIComponent(str).replace(/[!'()*]/g, (c) => '%' + c.charCodeAt(0).toString(16).toUpperCase());6}78async function sign(method: string, url: string, params: Record<string, string>, consumerKey: string, consumerSecret: string, accessToken: string, tokenSecret: string): Promise<string> {9 const oauthParams: Record<string, string> = {10 oauth_consumer_key: consumerKey,11 oauth_nonce: crypto.randomUUID().replace(/-/g, ''),12 oauth_signature_method: 'HMAC-SHA1',13 oauth_timestamp: Math.floor(Date.now() / 1000).toString(),14 oauth_token: accessToken,15 oauth_version: '1.0',16 ...params,17 };1819 const sortedParams = Object.keys(oauthParams)20 .sort()21 .map((k) => `${percentEncode(k)}=${percentEncode(oauthParams[k])}`)22 .join('&');2324 const baseString = [method.toUpperCase(), percentEncode(url), percentEncode(sortedParams)].join('&');25 const signingKey = `${percentEncode(consumerSecret)}&${percentEncode(tokenSecret)}`;2627 const key = await crypto.subtle.importKey(28 'raw',29 new TextEncoder().encode(signingKey),30 { name: 'HMAC', hash: 'SHA-1' },31 false,32 ['sign']33 );34 const signature = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(baseString));35 const signatureB64 = btoa(String.fromCharCode(...new Uint8Array(signature)));3637 const authHeader = 'OAuth ' + Object.keys(oauthParams)38 .filter((k) => k.startsWith('oauth_'))39 .sort()40 .map((k) => `${percentEncode(k)}="${percentEncode(oauthParams[k])}"`)41 .join(', ') + `, oauth_signature="${percentEncode(signatureB64)}"`;4243 return authHeader;44}4546Deno.serve(async (req) => {47 if (req.method === 'OPTIONS') {48 return new Response(null, {49 headers: {50 'Access-Control-Allow-Origin': '*',51 'Access-Control-Allow-Methods': 'POST, OPTIONS',52 'Access-Control-Allow-Headers': 'Content-Type, Authorization',53 },54 });55 }5657 const consumerKey = Deno.env.get('X_CONSUMER_KEY')!;58 const consumerSecret = Deno.env.get('X_CONSUMER_SECRET')!;59 const accessToken = Deno.env.get('X_ACCESS_TOKEN')!;60 const tokenSecret = Deno.env.get('X_ACCESS_TOKEN_SECRET')!;61 const accountId = Deno.env.get('X_ADS_ACCOUNT_ID')!;6263 if (!consumerKey || !consumerSecret || !accessToken || !tokenSecret) {64 return new Response(JSON.stringify({ error: 'X Ads credentials not fully configured' }), {65 status: 500,66 headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' },67 });68 }6970 const { action, params } = await req.json();71 let url: string;7273 if (action === 'campaigns') {74 url = `https://ads-api.twitter.com/12/accounts/${accountId}/campaigns`;75 } else if (action === 'account') {76 url = `https://ads-api.twitter.com/12/accounts/${accountId}`;77 } else if (action === 'line_items') {78 url = `https://ads-api.twitter.com/12/accounts/${accountId}/line_items${params?.campaign_id ? `?campaign_ids=${params.campaign_id}` : ''}`;79 } else {80 return new Response(JSON.stringify({ error: 'Invalid action' }), {81 status: 400,82 headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' },83 });84 }8586 const authHeader = await sign('GET', url.split('?')[0], {}, consumerKey, consumerSecret, accessToken, tokenSecret);8788 const response = await fetch(url, {89 method: 'GET',90 headers: { 'Authorization': authHeader, 'Content-Type': 'application/json' },91 });9293 const data = await response.json();94 return new Response(JSON.stringify(data), {95 status: response.ok ? 200 : response.status,96 headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' },97 });98});Pro tip: The OAuth 1.0a signature computation is order-sensitive. Parameters must be sorted alphabetically by percent-encoded key before generating the signature base string. A single wrong sort order or missed percent-encoding will produce an invalid signature and a 401 error.
Expected result: The twitter-ads Edge Function is deployed with OAuth 1.0a signing. Calling it with { action: 'account' } returns your X Ads account information. Calling { action: 'campaigns' } returns your campaigns.
Build the X Ads campaign dashboard
Build the X Ads campaign dashboard
With the OAuth 1.0a signing working, use Lovable's chat to build the campaign management dashboard. X Ads analytics are retrieved through an asynchronous stats query system — you POST a stats request, receive a job ID, then poll the job status until results are available. This asynchronous pattern is more complex than synchronous analytics endpoints used by most other ad platforms. For the campaign overview, fetch campaigns synchronously (the /campaigns endpoint is synchronous) and display the core campaign attributes: name, status, funding_instrument_id, daily_budget_amount_local_micro (divided by 1,000,000 to get dollar amounts), entity_status, and start/end times. For performance metrics, use the synchronous /stats endpoint for entity-level stats (impressions, engagements, spend) with a date range and the correct metric groups. X Ads metrics are returned in the 'organic_metrics' and 'promoted_metrics' groups — promoted_metrics covers paid amplification while organic_metrics covers unpromoted engagement. For promoted tweet creative performance, fetch line items and then the promoted tweets for each line item. This shows which specific tweet content is being amplified, its engagement rate, and whether it warrants continuation or pausing.
Build an X Ads campaign dashboard with a campaigns table showing campaign name, status, daily budget (convert from micro-cents: divide by 1000000), start date, and end date. Add a date range picker. For each campaign row, show a 'Details' button that expands to show line items fetched from the twitter-ads Edge Function. Add a summary cards row showing total spend, total impressions, and average engagement rate across all active campaigns.
Paste this in Lovable chat
Expected result: An X Ads dashboard shows campaigns with their status and budget data. Budget amounts are displayed in dollar format after dividing the micro-cent values by 1,000,000. The details row shows line items for each campaign.
Common use cases
X Ads campaign performance dashboard
Build a campaign analytics dashboard showing X Ads performance metrics: impressions, engagements, engagement rate, clicks, spend, and CPE (cost per engagement) across all active campaigns. The dashboard lets marketers monitor campaign health and compare performance across different creative executions.
Create an X Ads dashboard that calls the twitter-ads Edge Function to fetch all campaigns for my account. Show a table with campaign name, funding instrument, daily budget, total spend, impressions, engagements, engagement rate, and status. Add a date range picker and campaign objective filter. Show a line chart of daily spend vs. engagement over the last 30 days.
Copy this prompt to try it in Lovable
Promoted tweet manager with creative comparison
Build a tool for managing promoted tweet content that shows how different tweet creative executions perform head-to-head. The dashboard shows per-tweet metrics for promoted content, helping ad managers identify high-performing organic tweets worth amplifying and underperforming paid content to pause.
Build a promoted tweet performance page that calls the twitter-ads Edge Function to fetch promoted tweets for my current campaigns. Show each tweet with its text preview, promotion status, impressions, engagements, engagement rate, URL clicks, and spend. Add a 'Pause Promotion' button for each tweet. Sort by engagement rate descending to show the best performers first.
Copy this prompt to try it in Lovable
Real-time campaign budget monitoring and alerts
Create a budget pacing monitor that checks X Ads campaign spend against daily and lifetime budget targets, flags campaigns that are over- or under-spending, and stores alerts in Supabase. This helps marketers catch budget issues before significant overspend occurs on campaigns.
Build a budget monitoring dashboard that calls the twitter-ads Edge Function to fetch today's spend for all active campaigns. Calculate the expected daily spend from the campaign's daily budget and compare. Show a traffic light indicator (green/yellow/red) for each campaign based on pacing — green if within 20% of expected, yellow if 20-50% off, red if over 50% off target. Store alerts in a Supabase table.
Copy this prompt to try it in Lovable
Troubleshooting
API returns 401 Unauthorized with 'Invalid or expired token' or 'Could not authenticate you'
Cause: The OAuth 1.0a signature is invalid. Common causes: incorrect parameter sorting, wrong percent-encoding, clock skew (OAuth timestamps must be within 5 minutes of server time), or credentials stored with extra whitespace.
Solution: Verify all four credential values in Cloud → Secrets have no leading or trailing spaces. Check that the timestamp in the signing function uses the current UTC time — Deno's Math.floor(Date.now() / 1000) is correct. Log the signature base string and verify the sorting order. Test with the account action (simplest endpoint) before testing more complex ones.
API returns 403 Forbidden with 'Your credentials do not allow access to this resource'
Cause: Your X Developer account does not have Elevated access or your app has not been approved for X Ads API access specifically.
Solution: Go to developer.twitter.com and check your app's access level. The Ads API requires Elevated access at minimum. If you have Basic access, apply for Elevated access with your advertising use case. Additionally, verify your app has been specifically approved for the Ads API — some apps have Elevated general API access but not Ads API access.
Budget amounts in the campaigns response appear as very large numbers like 1000000
Cause: X Ads API returns monetary values in micro-cents (millionths of the local currency unit). A daily budget of $10.00 is stored as 10000000.
Solution: Divide all monetary values from X Ads API by 1,000,000 to get the actual currency amount. For display: (campaign.daily_budget_amount_local_micro / 1000000).toFixed(2). Apply this conversion everywhere monetary values appear — daily budgets, total spend, CPE, CPM.
1// Convert X Ads micro-currency to display currency2const formatBudget = (microCents: number): string => {3 return `$${(microCents / 1_000_000).toFixed(2)}`;4};OAuth nonce conflicts or 'Nonce already used' errors
Cause: The same OAuth nonce value was used in two requests within the nonce validity window. This can happen if the Edge Function is called rapidly with the same timestamp and random generation produces a collision.
Solution: Ensure the nonce generation in the sign() function produces sufficiently unique values. Using crypto.randomUUID().replace(/-/g, '') produces a 32-character hex string with very low collision probability. Avoid using simple timestamp-based nonces — they are more likely to collide under rapid API usage.
Best practices
- Store all four OAuth 1.0a credentials in Cloud → Secrets — the signing process requires all four values server-side and any single credential exposure compromises the entire signing chain
- Implement thorough percent-encoding in the OAuth signature function — even a single character encoded differently from X's specification will produce an invalid signature
- Convert X Ads monetary values from micro-cents to display currency by dividing by 1,000,000 before showing to users
- Cache campaign and line item data in Supabase — X Ads API has rate limits and the synchronous campaign/line-item endpoints are more reliable to cache than the asynchronous stats endpoints
- Test the OAuth signing implementation with the simple account endpoint before adding more complex endpoints — a valid 200 response from /accounts/{id} confirms the entire signing chain is working
- Verify X Ads API endpoint version numbers against current documentation — X has changed API versions and deprecated endpoints multiple times since the Twitter acquisition and rebrand
- Handle micro-currency values consistently throughout the application — define a single conversion function and use it everywhere rather than performing the division inline in multiple components
Alternatives
Choose LinkedIn Ads if your campaigns target B2B professionals and you want a simpler OAuth 2.0 integration rather than X's complex OAuth 1.0a signing requirement.
Choose Facebook Ads if you need broader consumer demographic targeting with a more mature and consistent API that uses standard OAuth 2.0 Bearer token authentication.
Choose TikTok Ads if your audience is younger consumers engaged with short-form video content and you prefer a simpler long-term access token authentication model.
Frequently asked questions
Why does X Ads API still use OAuth 1.0a instead of OAuth 2.0?
X's advertising API predates the widespread adoption of OAuth 2.0 and has not been updated to use the newer standard. OAuth 1.0a provides strong security through per-request signing (making replayed requests invalid) but is significantly more complex to implement than OAuth 2.0's Bearer token approach. X has offered OAuth 2.0 Bearer tokens for some of its newer API endpoints (v2 read-only access) but the Ads API specifically remains on OAuth 1.0a as of March 2026.
What is the difference between organic and promoted metrics in X Ads?
In X's advertising model, a tweet can receive both organic engagement (from users who see it naturally in their feed because they follow you or it was recommended) and promoted engagement (from users who see it as a paid ad). The API returns these separately: organic_metrics covers natural engagement on the tweet itself, and promoted_metrics covers engagement generated through paid promotion. For advertising ROI analysis, use promoted_metrics. For understanding total tweet performance including organic reach, sum both.
How do X Ads account IDs work?
X Ads account IDs are alphanumeric strings (not purely numeric like some other ad platforms) visible in the ads.twitter.com URL. They appear in the URL as /accounts/abc123. This account ID is required for all Ads API calls and is different from your X user ID or @username. One X account can have multiple Ads accounts, so if you are building for multiple clients, you need to handle multiple account IDs.
What happened to the Twitter Ads API after Elon Musk's acquisition?
Following the acquisition and rebranding from Twitter to X in 2022-2023, the Ads API has gone through multiple changes including endpoint version updates, access tier restructuring, and some endpoint deprecations. The core OAuth 1.0a signing requirement and advertising hierarchy (accounts, campaigns, line items, promoted tweets) remain intact. Always check developer.twitter.com for the current API version and endpoint documentation — endpoint URLs in older tutorials may reference deprecated versions. As of March 2026, v12 is the current Ads API version.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation