Integrate Bolt.new with Twitter (X) Ads using the X Ads API via Next.js API routes. Apply for Ads API access with an X Ads account that has spend history, use OAuth 1.0a for authentication via the twitter-api-v2 package, then fetch campaign analytics including impressions, engagements, spend, and conversions. Build an X Ads performance dashboard. API access is tiered and requires a paid subscription — budget for approval time and access fees before building.
Build a Twitter (X) Ads Analytics Dashboard in Bolt.new
Twitter became X in 2023 and its Ads API underwent significant access and pricing changes under new ownership. What was once freely available to approved developers now sits behind paid tiers, and the API surface has been consolidated. Understanding this context is important before starting: X Ads API access requires applying through the X Developer Portal, having an X Ads account with actual spend history, and potentially paying for a developer tier that grants sufficient rate limits for production use.
The X Ads API distinguishes itself from Facebook and Google Ads by its focus on real-time conversation engagement. Unlike search ads tied to intent keywords or display ads targeting demographics, X Ads reach people in the context of trending topics, breaking news, and real-time conversations. Promoted tweets receive organic engagement — replies, likes, retweets — alongside paid click activity, and X's analytics surface both paid and earned engagement metrics that show how your ads performed within the broader conversation.
OAuth 1.0a is X's authentication mechanism for the Ads API, and it is notably more complex than modern OAuth 2.0. Rather than a simple Bearer token, each request must be signed with a cryptographic signature calculated from the request URL, method, parameters, and your credentials. The twitter-api-v2 npm package handles this signing complexity automatically, so you do not need to implement the signature algorithm yourself. All four credentials — API key, API secret, access token, and access token secret — can be stored in your .env file and used directly in Bolt's WebContainer, making local development straightforward once you have the credentials.
Integration method
Bolt generates Next.js API routes that call X's Ads API using OAuth 1.0a authentication, keeping credentials server-side. The X Ads API is REST over HTTPS, so outbound data fetching works in Bolt's WebContainer during development. OAuth 1.0a requires all credentials upfront (no redirect flow needed) — your API key, API secret, access token, and access secret are all that is needed. Bolt's preview can fully read campaign analytics with these four credentials in your .env file.
Prerequisites
- A Bolt.new account with a Next.js project
- An X (Twitter) account with an active X Ads account at ads.x.com
- Demonstrated ad spend history on the X Ads account (required for API access approval)
- An X Developer account at developer.x.com with a project and app created
- X Ads API access approved — apply at developer.x.com under your project's app settings
Step-by-step guide
Apply for X Ads API Access and Create Developer App
Apply for X Ads API Access and Create Developer App
X Ads API access is not available automatically — you must apply and be approved. The approval process has tightened significantly since 2023 and requires a legitimate advertising use case. Start at developer.x.com. Sign in with your X account and create a Developer Project if you do not have one. Inside the project, create an App. Give it a clear, descriptive name that reflects your advertising use case (e.g., 'MyCompany Ads Dashboard'). Under the app's settings, look for 'Ads API access' or 'Advanced access' and click to apply. The application asks about your use case — be specific and honest about building an ad analytics dashboard for your own or your clients' accounts. X also requires that your X Ads account has actual spending history. A brand-new ads account with no spend is unlikely to be approved. If your ads account is new, make a small test campaign spend before applying. Once your app is created and Ads API access is approved, navigate to the app's 'Keys and Tokens' section. You need four credentials: API Key (also called Consumer Key), API Key Secret (Consumer Secret), Access Token, and Access Token Secret. The API Key and Secret identify your app. The Access Token and Secret represent the X account that will be making API calls — generate these by clicking 'Generate' under Access Token. Note the Ads Account ID (format: `18ce54xxxx`) from your X Ads dashboard at ads.x.com — it appears in the URL when you are in Ads Manager. Add all five values to your Bolt project's .env file.
Install the twitter-api-v2 npm package. Add .env with TWITTER_API_KEY, TWITTER_API_SECRET, TWITTER_ACCESS_TOKEN, TWITTER_ACCESS_SECRET, and TWITTER_ADS_ACCOUNT_ID. Create lib/twitter-ads.ts that exports a getTwitterAdsClient function returning a TwitterApi instance initialized with all four OAuth 1.0a credentials from process.env. Also export a twitterAdsGet helper that makes GET requests to the X Ads API base URL https://ads-api.x.com/12/accounts/{TWITTER_ADS_ACCOUNT_ID}/{endpoint} with the authenticated client.
Paste this in Bolt.new chat
1// lib/twitter-ads.ts2import { TwitterApi } from 'twitter-api-v2';34const ADS_API_BASE = 'https://ads-api.x.com/12';56export function getTwitterAdsClient(): TwitterApi {7 const apiKey = process.env.TWITTER_API_KEY;8 const apiSecret = process.env.TWITTER_API_SECRET;9 const accessToken = process.env.TWITTER_ACCESS_TOKEN;10 const accessSecret = process.env.TWITTER_ACCESS_SECRET;1112 if (!apiKey || !apiSecret || !accessToken || !accessSecret) {13 throw new Error(14 'Missing Twitter OAuth credentials. Set TWITTER_API_KEY, TWITTER_API_SECRET, ' +15 'TWITTER_ACCESS_TOKEN, and TWITTER_ACCESS_SECRET in .env'16 );17 }1819 return new TwitterApi({20 appKey: apiKey,21 appSecret: apiSecret,22 accessToken: accessToken,23 accessSecret: accessSecret,24 });25}2627export async function twitterAdsGet<T = unknown>(endpoint: string, params?: Record<string, string>): Promise<T> {28 const accountId = process.env.TWITTER_ADS_ACCOUNT_ID;29 if (!accountId) throw new Error('TWITTER_ADS_ACCOUNT_ID is not configured');3031 const client = getTwitterAdsClient();3233 const url = new URL(`${ADS_API_BASE}/accounts/${accountId}/${endpoint}`);34 if (params) {35 Object.entries(params).forEach(([key, value]) => url.searchParams.set(key, value));36 }3738 // Use the underlying OAuth1 client for raw requests39 const response = await client.v1.get(url.toString()) as T;40 return response;41}Pro tip: X API versioning changes frequently. The Ads API was at version 12 as of early 2026 but the base URL and version number may have changed. Check developer.x.com for the current Ads API base URL before building.
Expected result: The twitter-api-v2 package is installed and the Twitter Ads client helper is configured. The getTwitterAdsClient function initializes without errors when all four OAuth credentials are present in .env.
Fetch Campaign List and Performance Statistics
Fetch Campaign List and Performance Statistics
X Ads API organizes advertising into Campaigns, Line Items (equivalent to ad groups), and Promoted Tweets. Performance statistics are available through the `/stats/accounts/{account_id}` endpoint rather than embedded in campaign objects, which means you make separate calls for metadata and metrics. To list campaigns, call `GET /12/accounts/{account_id}/campaigns` with query parameters for filtering. The response includes campaign name, objective, status (ACTIVE, PAUSED, DRAFT, DELETED), budget type, and total budget. Active campaigns are filtered with `funding_instrument_ids` or by checking `entity_status=ACTIVE`. For performance statistics, call `GET /12/stats/accounts/{account_id}` with required parameters: `entity=CAMPAIGN`, `entity_ids` (comma-separated list of campaign IDs), `start_time` and `end_time` in ISO 8601 format, `placement=ALL_ON_TWITTER`, and `metric_groups=ENGAGEMENT,BILLING`. This endpoint returns an array of stat objects per campaign ID per day or as a total depending on the `granularity` parameter (DAY, TOTAL). Key metrics in the ENGAGEMENT metric group: `impressions`, `engagements`, `clicks`, `retweets`, `likes`, `replies`, `follows`, `url_clicks`, `app_clicks`, `card_engagements`. The BILLING group contains `billed_charge_local_micro` (spend in microdollars — divide by 1,000,000 for dollars), `billed_engagements`, and `billed_follows`. A performance nuance unique to X: promoted tweets receive both paid metric tracking and organic metric spillover. When your promoted tweet goes viral after someone retweets it organically, those organic impressions are tracked separately as `organic_impressions`. Surfacing this earned media value — how much organic reach your paid campaign generated — is a compelling dashboard feature that X Ads natively provides and Facebook does not.
Create a Next.js API route at app/api/twitter/campaigns/route.ts. First fetch all ACTIVE campaigns from X Ads API using twitterAdsGet from lib/twitter-ads.ts. Then fetch stats for those campaign IDs using the /stats endpoint with entity=CAMPAIGN, metric_groups=ENGAGEMENT,BILLING, start_time and end_time from query params (default last 30 days), granularity=TOTAL. Join campaigns with their stats by ID. Convert billed_charge_local_micro to dollars (divide by 1000000). Return sorted by spend descending with totals.
Paste this in Bolt.new chat
1// app/api/twitter/campaigns/route.ts2import { NextResponse } from 'next/server';3import { twitterAdsGet } from '@/lib/twitter-ads';45interface XCampaign {6 id: string;7 name: string;8 entity_status: string;9 objective: string;10 total_budget_amount_local_micro?: number;11}1213interface XStatEntry {14 id: string;15 id_data: Array<{16 segment?: unknown;17 metrics: {18 impressions: number[];19 engagements: number[];20 clicks: number[];21 url_clicks: number[];22 retweets: number[];23 likes: number[];24 replies: number[];25 billed_charge_local_micro: number[];26 };27 }>;28}2930function sum(arr?: number[]): number {31 if (!arr || arr.length === 0) return 0;32 return arr.reduce((a, b) => a + b, 0);33}3435export async function GET(request: Request) {36 const { searchParams } = new URL(request.url);37 const endDate = new Date();38 const startDate = new Date(endDate.getTime() - 30 * 24 * 60 * 60 * 1000);39 const startTime = searchParams.get('startTime') || startDate.toISOString();40 const endTime = searchParams.get('endTime') || endDate.toISOString();4142 try {43 const campaignsData = await twitterAdsGet<{ data: XCampaign[] }>('campaigns', {44 count: '100',45 with_deleted: 'false',46 });4748 const campaigns = campaignsData.data || [];49 if (campaigns.length === 0) {50 return NextResponse.json({ campaigns: [], totals: {} });51 }5253 const campaignIds = campaigns.map((c) => c.id).join(',');5455 const statsData = await twitterAdsGet<{ data: XStatEntry[] }>(56 `../../stats/accounts/${process.env.TWITTER_ADS_ACCOUNT_ID}`,57 {58 entity: 'CAMPAIGN',59 entity_ids: campaignIds,60 start_time: startTime,61 end_time: endTime,62 placement: 'ALL_ON_TWITTER',63 metric_groups: 'ENGAGEMENT,BILLING',64 granularity: 'TOTAL',65 }66 );6768 const statsMap = new Map<string, XStatEntry>();69 (statsData.data || []).forEach((s) => statsMap.set(s.id, s));7071 const result = campaigns.map((campaign) => {72 const stat = statsMap.get(campaign.id);73 const metrics = stat?.id_data?.[0]?.metrics;7475 const spendMicro = sum(metrics?.billed_charge_local_micro);7677 return {78 id: campaign.id,79 name: campaign.name,80 status: campaign.entity_status,81 objective: campaign.objective,82 totalBudget: campaign.total_budget_amount_local_micro83 ? campaign.total_budget_amount_local_micro / 1_000_00084 : null,85 impressions: sum(metrics?.impressions),86 engagements: sum(metrics?.engagements),87 clicks: sum(metrics?.url_clicks),88 retweets: sum(metrics?.retweets),89 likes: sum(metrics?.likes),90 replies: sum(metrics?.replies),91 spend: spendMicro / 1_000_000,92 cpe: spendMicro > 0 && sum(metrics?.engagements) > 093 ? (spendMicro / 1_000_000) / sum(metrics.engagements)94 : null,95 };96 });9798 result.sort((a, b) => b.spend - a.spend);99100 const totals = result.reduce(101 (acc, c) => ({102 spend: acc.spend + c.spend,103 impressions: acc.impressions + c.impressions,104 engagements: acc.engagements + c.engagements,105 clicks: acc.clicks + c.clicks,106 }),107 { spend: 0, impressions: 0, engagements: 0, clicks: 0 }108 );109110 return NextResponse.json({ campaigns: result, totals });111 } catch (err) {112 const message = err instanceof Error ? err.message : 'Failed to fetch X Ads campaigns';113 return NextResponse.json({ error: message }, { status: 500 });114 }115}Pro tip: X Ads API rate limits are stricter than Facebook or Reddit. At the Basic developer tier, you get 15 requests per 15-minute window for most endpoints. Cache your campaign data aggressively — a 15-minute TTL for campaign statistics is appropriate and keeps you well within rate limits.
Expected result: The campaigns API route returns X Ads campaign data with performance statistics joined. Spend should be in dollars (e.g., 43.21 not 43210000). Call /api/twitter/campaigns to verify real campaign data is returned.
Build the X Ads Analytics Dashboard
Build the X Ads Analytics Dashboard
An X Ads dashboard has unique metrics to surface compared to Facebook or Google Ads dashboards. Beyond the standard impressions, clicks, and spend columns, X Ads provides detailed engagement breakdowns: URL clicks, app clicks, retweets, likes, replies, and follows. The most valuable X-specific metric for most advertisers is cost-per-engagement (CPE) — how much each meaningful interaction cost — rather than just cost-per-click. For the dashboard layout, consider three sections: a summary header with total spend, total impressions, total engagements, and average engagement rate (engagements divided by impressions, expressed as a percentage); a campaign table with sortable columns; and optionally an engagement breakdown card showing the distribution of engagement types (what percentage of total engagements were URL clicks vs. retweets vs. likes). X Ads campaign objectives have specific names: TWEET_ENGAGEMENTS (maximize interactions), WEBSITE_CLICKS (drive traffic), APP_INSTALLS, VIDEO_VIEWS, FOLLOWERS (grow account), REACH, and PREROLL_VIEWS. Map these to readable display names in your UI. The engagement rate benchmark on X varies by industry and campaign type. Tweet engagement campaigns typically achieve 1-3% engagement rates; website click campaigns prioritize URL click rate over broader engagement. Display the metric most relevant to the campaign objective — highlight URL click rate for WEBSITE_CLICKS campaigns and total engagement rate for TWEET_ENGAGEMENTS campaigns. For date range selection, build presets that map to ISO 8601 datetime strings as X Ads API requires full datetime strings (not just date strings like Reddit's API). Use the JavaScript Date object to generate properly formatted start_time and end_time values.
Build an X Ads dashboard page at /twitter-ads that fetches from /api/twitter/campaigns with ISO datetime params. Show: (1) summary cards for total spend, total impressions, total engagements, and engagement rate percentage; (2) a Recharts horizontal bar chart ranking campaigns by spend; (3) a sortable table with Campaign Name, Status badge (color-coded), Objective, Spend, Impressions, Engagements, Engagement Rate, and CPE (cost per engagement). Add Last 7d / Last 30d date presets. Include loading skeleton and error state.
Paste this in Bolt.new chat
Pro tip: Display the engagement rate as a percentage with two decimal places (e.g., '2.34%'). Calculate it as (total_engagements / total_impressions) * 100. An engagement rate above 1% is generally considered good for promoted tweets; above 3% is excellent.
Expected result: The X Ads dashboard renders with campaign data, engagement metrics, and proper currency formatting. The engagement rate column shows percentage values and the date range presets correctly reload data when switched.
Deploy and Configure Production Credentials
Deploy and Configure Production Credentials
During development in Bolt's WebContainer, all outbound calls to X's Ads API work correctly — OAuth 1.0a request signing happens server-side in the Next.js API route, and the signed requests go out over HTTPS to X's servers. The WebContainer limitation does not affect the X Ads integration because OAuth 1.0a does not require a redirect callback. All four credentials are stored in .env and used directly without any authorization flow. This means the full X Ads integration — reading campaigns, fetching stats, displaying a dashboard — can be tested entirely in Bolt's WebContainer preview without deploying. There is no incoming webhook or OAuth callback that would require a public URL. To deploy, connect Netlify via Settings → Applications → Connect Netlify, or click Publish in Bolt to deploy to Bolt Cloud. After deployment, add environment variables in your hosting dashboard: `TWITTER_API_KEY`, `TWITTER_API_SECRET`, `TWITTER_ACCESS_TOKEN`, `TWITTER_ACCESS_SECRET`, and `TWITTER_ADS_ACCOUNT_ID`. These should not have the `NEXT_PUBLIC_` prefix — they are server-side only credentials that must never appear in client JavaScript bundles. Note about X API access tiers and future changes: X's API pricing and access tiers have changed multiple times since 2023 and may continue to change. The Basic tier ($100/month as of early 2026) provides enough rate limits for a single-account analytics dashboard. The Pro tier is needed for higher-volume applications. Before investing significant development time, verify the current access tier requirements at developer.x.com to ensure the API access level you have matches your application's needs.
Add a /api/twitter/health route that verifies all four OAuth credentials are configured and tests the connection by calling X Ads API GET /accounts with count=1. Return { connected: true, accountCount } on success or { connected: false, error } if credentials are missing or the API call fails. Show a connection status indicator on the dashboard header.
Paste this in Bolt.new chat
1# Production environment variables — add in Netlify or Bolt Cloud2# All four credentials are required for OAuth 1.0a request signing3# Do NOT add NEXT_PUBLIC_ prefix — these must stay server-side only45TWITTER_API_KEY=your_api_key6TWITTER_API_SECRET=your_api_secret7TWITTER_ACCESS_TOKEN=your_access_token8TWITTER_ACCESS_SECRET=your_access_token_secret9TWITTER_ADS_ACCOUNT_ID=18ce54your_account_idPro tip: X rotates API credentials when security issues are detected. Set up monitoring that alerts you if the /api/twitter/health endpoint returns a failed connection status, so you know immediately if credentials need to be regenerated in your X Developer Portal.
Expected result: The app is deployed with production X credentials configured as server-side environment variables. The health check confirms successful API authentication and the campaign data loads from the deployed URL.
Common use cases
X Ads Campaign Performance Dashboard
Build a real-time campaign performance dashboard pulling X Ads analytics including impressions, engagements, spend, clicks, and conversions. Display campaign-level metrics with the ability to drill down into promoted tweet performance. Compare engagement rates across different campaign objectives and creative formats.
Build an X Ads analytics dashboard. Create a Next.js API route at /api/twitter/campaigns that uses the twitter-api-v2 package with OAuth 1.0a credentials from process.env (TWITTER_API_KEY, TWITTER_API_SECRET, TWITTER_ACCESS_TOKEN, TWITTER_ACCESS_SECRET, TWITTER_ADS_ACCOUNT_ID) to fetch all campaigns with performance stats (impressions, engagements, spend, clicks, conversions) for the last 30 days. Build a React dashboard with a summary row of total spend, total impressions, average engagement rate, and a sortable campaign table. Show each campaign's objective and status with color-coded badges.
Copy this prompt to try it in Bolt.new
Promoted Tweet Engagement Analyzer
Analyze which promoted tweet formats and content types generate the highest organic engagement alongside paid clicks. Surface the ratio of paid clicks to earned engagements (organic likes, retweets, replies) to identify content that resonates authentically with X's audience versus content that only performs when boosted.
Create a promoted tweet analyzer. Build a Next.js API route at /api/twitter/promoted-tweets that fetches all active promoted tweets in an X Ads campaign with stats for impressions, paid_clicks, organic_impressions, engagements, retweets, likes, replies, and spend using twitter-api-v2 and OAuth 1.0a from process.env. Build a React component showing each promoted tweet's text, engagement rate (engagements/impressions), earned media ratio (organic_impressions/paid_impressions), and a cost-per-engagement metric. Highlight top performers by earned media ratio.
Copy this prompt to try it in Bolt.new
X Ads Audience Performance Tracker
Track how different audience segments and targeting configurations perform across X Ads campaigns. Analyze keyword targeting, follower targeting, and interest targeting effectiveness to optimize budget allocation toward the highest-converting audience segments.
Build an audience performance tracker for X Ads. Create a Next.js API route at /api/twitter/audience-stats that fetches line items (ad groups) with their targeting criteria and performance metrics using twitter-api-v2 with OAuth 1.0a from process.env. For each line item, show targeting type (keyword/follower/interest), reach estimate, impressions, click-through rate, and cost per result. Group by targeting type and show aggregate performance per targeting strategy. Use TWITTER_API_KEY, TWITTER_API_SECRET, TWITTER_ACCESS_TOKEN, TWITTER_ACCESS_SECRET, and TWITTER_ADS_ACCOUNT_ID from process.env.
Copy this prompt to try it in Bolt.new
Troubleshooting
Error 32 'Could not authenticate you' or 401 Unauthorized from X Ads API
Cause: One or more of the four OAuth 1.0a credentials is incorrect, misformatted, or expired. This error also occurs if the app's access level does not include Ads API permissions.
Solution: Verify all four credentials in .env exactly match what is shown in the X Developer Portal under your app's 'Keys and Tokens' section. Regenerate the Access Token and Access Token Secret if you are unsure — the portal lets you regenerate them at any time. Also confirm your app has been approved for X Ads API access, not just standard API access.
Error 403 'Client application is not permitted to access this endpoint'
Cause: Your X developer app has not been approved for Ads API access. Standard developer apps cannot call the Ads API endpoints at ads-api.x.com even with valid OAuth credentials.
Solution: Apply for Ads API access through your X Developer Portal app settings and wait for approval. X's review process has become stricter since 2023 and may take one to two weeks. Your X Ads account must have actual spend history to be eligible. While waiting, mock the API responses in your development environment to continue building the dashboard UI.
Stats endpoint returns empty data arrays even though campaigns have recent spend
Cause: The stats endpoint requires very specific parameter formatting. Common issues: start_time and end_time must be ISO 8601 with timezone (e.g., 2025-01-01T00:00:00Z not 2025-01-01), the placement parameter must be ALL_ON_TWITTER or a specific placement, and entity_ids must be comma-separated without spaces.
Solution: Verify your datetime strings include the 'Z' timezone suffix. Verify entity_ids are joined with commas without spaces. Log the exact URL being called and test it manually with a tool like Postman to identify which parameter is malformed.
1// Correct datetime format for X Ads stats:2const startTime = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString(); // includes Z3const endTime = new Date().toISOString();4// Correct entity_ids format:5const entityIds = campaigns.map(c => c.id).join(','); // '123,456,789' NOT '123, 456, 789'Rate limit errors (Error 88) — too many requests
Cause: X Ads API has strict rate limits, especially on the Basic developer tier. Fetching campaign list and stats in rapid succession or without caching quickly exhausts the 15-requests-per-15-minutes limit.
Solution: Implement response caching on your Next.js API routes using in-memory cache with a 15-minute TTL. Also consider using Next.js fetch cache: add cache: 'force-cache' with revalidate: 900 to automatically cache API responses at the Next.js level without managing cache manually.
1// Next.js fetch caching — add to twitterAdsGet:2const response = await fetch(url.toString(), {3 headers: { Authorization: authHeader },4 next: { revalidate: 900 }, // cache for 15 minutes5});Best practices
- Store all four OAuth 1.0a credentials as server-side environment variables without NEXT_PUBLIC_ prefix — any credential appearing in client-side code is visible in browser DevTools and can be used to make API calls on your behalf.
- Cache X Ads API responses for 15 minutes minimum since the rate limit is 15 requests per 15-minute window at Basic tier — a single uncached page load that calls both the campaigns and stats endpoints uses 2 of your 15 available requests.
- Verify X Ads API access tier requirements before starting development — as of 2026 the API requires a paid developer subscription and Ads API approval separate from general API access.
- Use the twitter-api-v2 npm package for OAuth 1.0a request signing rather than implementing the HMAC-SHA1 signature algorithm yourself — the signing logic has specific requirements around URL encoding and parameter ordering that are easy to get wrong.
- Display X-specific engagement metrics (retweets, replies, likes, follows) alongside standard CTR — these organic engagement signals distinguish X Ads performance data from other platforms and represent value beyond direct click traffic.
- Always divide billed_charge_local_micro values by 1,000,000 to convert from microdollars to dollars before displaying spend figures.
- Build a connection health check endpoint and monitor it in production — X occasionally rotates API credentials when security anomalies are detected, and a monitoring alert ensures you know immediately if the integration breaks.
- Consider the engagement rate (engagements/impressions) as the primary KPI for awareness campaigns and URL click rate (url_clicks/impressions) for traffic campaigns — X reports both but the relevant primary metric depends on the campaign objective.
Alternatives
Facebook Ads has more generous API access, more mature developer tooling, and broader audience reach, making it easier to build production analytics dashboards for consumer brands.
LinkedIn Ads provides professional demographic targeting with job title and company filters that X cannot match, making it a better choice for B2B advertising to specific professional roles.
Reddit Ads also targets niche communities but has simpler API access requirements and REST/OAuth 2.0 authentication, making the integration less complex than X's OAuth 1.0a signing.
Google Ads captures high-intent search traffic and provides significantly higher ad volume than X, making it a higher-priority integration for most performance-focused marketing dashboards.
Frequently asked questions
Does Bolt.new have a native Twitter (X) Ads integration?
No — Bolt.new does not include a native X Ads connector. The integration requires building Next.js API routes that call X's Ads API using OAuth 1.0a authentication via the twitter-api-v2 package. Bolt's AI can generate this code from a descriptive prompt, which handles the OAuth signing complexity automatically through the npm package.
Can I test the X Ads dashboard in Bolt's WebContainer preview?
Yes — X's OAuth 1.0a authentication does not require a redirect callback. All four credentials are stored directly in your .env file and used to sign outbound requests. This means the entire X Ads integration works in Bolt's WebContainer preview without deploying, unlike OAuth 2.0 flows that require a stable redirect URI.
How much does X Ads API access cost?
As of early 2026, X Ads API access requires a paid X developer subscription. The Basic tier costs approximately $100 per month and provides enough rate limits for a single-account analytics dashboard with 15 requests per 15-minute window. The Pro tier offers higher rate limits for multi-account or high-volume applications. Check developer.x.com for current pricing since X has changed API pricing multiple times since 2023.
What is OAuth 1.0a and why is it more complex than OAuth 2.0?
OAuth 1.0a requires each HTTP request to be signed with a cryptographic HMAC-SHA1 signature calculated from the request URL, method, all parameters, and your credentials. The signature calculation involves URL-encoding parameters in a specific order, concatenating them into a base string, and signing with a composite key. OAuth 2.0 is simpler — it just adds a Bearer token header. The twitter-api-v2 npm package handles all OAuth 1.0a signing automatically, so you provide the four credentials and it handles the rest.
What makes X Ads analytics different from Facebook or Google Ads analytics?
X Ads surfaces earned media metrics that other platforms do not — organic retweets, likes, and replies that your promoted tweets receive from users who saw them organically after someone in their network retweeted. These earned engagements represent free additional reach beyond what you paid for, and X's analytics track them separately from paid engagement. Building dashboards that calculate earned media ratio (organic impressions divided by paid impressions) gives advertisers insight unavailable from Facebook or Google analytics.
Why do X Ads spend values appear as very large numbers?
X Ads API returns spend in microdollars — one US dollar equals 1,000,000 microdollars. The billed_charge_local_micro field and related monetary fields all use this unit. Divide any monetary value from the stats endpoint by 1,000,000 to get the dollar amount. Budget fields from the campaigns endpoint are also in microdollars, so the same conversion applies throughout the API.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation