Connect Retool to Pinterest Ads by creating a REST API Resource using Pinterest Marketing API OAuth 2.0. Query campaign metrics, ad group performance, Pin promotion data, and conversion analytics to build visual shopping ad dashboards — giving marketing teams direct access to Pinterest Ads data without exporting from the native Pinterest Ads Manager.
| Fact | Value |
|---|---|
| Tool | Pinterest Ads |
| Category | Marketing |
| Method | REST API Resource |
| Difficulty | Intermediate |
| Time required | 30 minutes |
| Last updated | April 2026 |
Build a Pinterest Ads Performance Dashboard in Retool
Pinterest Ads Manager provides solid reporting for individual campaigns, but marketing teams managing multiple campaigns across different product categories need a unified view that Pinterest's native interface doesn't provide. Common operational needs — comparing campaign ROAS across quarters, identifying underperforming ad groups before they burn budget, exporting pin-level performance for creative analysis — require either tedious manual exports or expensive third-party reporting tools.
Retool solves this by giving your marketing team a configurable dashboard with direct API access to Pinterest Ads data. You can build a campaign performance hub that shows all active campaigns with spend, impressions, clicks, and ROAS in a sortable Table, drill down into ad group-level metrics with a single click, and visualize conversion trends using Retool's Chart components. When combined with your e-commerce database or Shopify data in the same Retool app, you can join Pinterest attribution data with actual order records for true cross-channel ROAS analysis.
Pinterest's Marketing API uses OAuth 2.0 with per-user authentication, which means your reporting reflects the exact account permissions of the authenticated user. Retool proxies all requests server-side, so Pinterest API tokens never reach the browser.
Integration method
Pinterest Ads connects to Retool through a REST API Resource using Pinterest Marketing API OAuth 2.0 for authentication. You configure OAuth 2.0 with your Pinterest app's Client ID and Client Secret once at the resource level, and Retool handles token refresh automatically. All API requests are proxied server-side through Retool, keeping your credentials secure and eliminating CORS issues.
Prerequisites
- A Pinterest business account with an active Pinterest Ads account (ads.pinterest.com)
- A Pinterest Developer App with Marketing API access (app.pinterest.com/business/apps) — requires business verification
- Pinterest app Client ID and Client Secret from the Developer portal
- A Retool account with permission to create Resources
- At least one active or historical Pinterest Ads campaign to query for testing
Step-by-step guide
Create a Pinterest Developer App and configure API access
Before setting up the Retool resource, you need a Pinterest Developer App with Marketing API access. Log in to the Pinterest Developer portal at app.pinterest.com/business/apps and click Create App. Fill in the app name (e.g., 'Retool Analytics'), description, and website URL. Enable the Marketing API access type — this requires your Pinterest business account to be verified and in good standing. In the App settings, navigate to the Authentication tab and configure OAuth 2.0: add the Redirect URI as https://login.retool.com/oauth/consumer (for Retool Cloud, or your self-hosted Retool URL + /oauth/consumer). Note your Client ID and Client Secret from the App Details page. Pinterest Marketing API requires the following OAuth scopes for ads management: ads:read (for campaign and ad data), catalogs:read (for shopping catalog data), and pins:read (for Pin analytics). If you need to create or modify campaigns from Retool, also request ads:write. Store your Pinterest credentials as Retool configuration variables: go to Retool Settings → Configuration Variables and create PINTEREST_CLIENT_ID and PINTEREST_CLIENT_SECRET (both marked as Secret).
Pro tip: Pinterest requires that your Developer App pass a basic review before the Marketing API is available for production use. For internal tools accessing your own ad account, you can use the Development mode initially, but Marketing API access for real data requires submitting the app for review at developers.pinterest.com.
Expected result: A Pinterest Developer App is created with Marketing API access enabled and the Retool redirect URI configured. Client ID and Client Secret are stored as Retool configuration variables.
Configure the Pinterest REST API Resource in Retool
Navigate to the Resources tab in your Retool instance and click Add Resource. Select REST API from the list of resource types. Name the resource 'Pinterest Ads API'. In the Base URL field, enter https://api.pinterest.com. Pinterest Marketing API endpoints use version prefixes — you'll include /v5/ in each query path rather than the base URL, giving you flexibility to use different API versions per query if needed. For Authentication, select OAuth 2.0 from the dropdown. Configure the OAuth fields as follows: - Grant Type: Authorization Code - Client ID: {{ retoolContext.configVars.PINTEREST_CLIENT_ID }} - Client Secret: {{ retoolContext.configVars.PINTEREST_CLIENT_SECRET }} - Authorization URL: https://www.pinterest.com/oauth - Access Token URL: https://api.pinterest.com/v5/oauth/token - Scope: ads:read catalogs:read pins:read (space-separated) - Access Token Lifespan: 86400 (24 hours for standard tokens) In the Default Headers section, add: Key = Content-Type, Value = application/json. Click Save Changes and then 'Connect OAuth account' to complete the OAuth flow. You'll be redirected to Pinterest's authorization page to grant the app access to your ads account.
Pro tip: Pinterest OAuth tokens have different lifespans depending on the grant type. Authorization code tokens are valid for 24 hours with a refresh token valid for 365 days. Enable 'Automatically refresh OAuth token' in the resource settings to prevent authentication interruptions during long work sessions.
Expected result: The Pinterest Ads API REST resource is saved with OAuth 2.0 authentication configured. The 'Connect OAuth account' flow completes successfully and the resource can make authenticated API calls.
Query advertising accounts and campaign data
Start by fetching the ad accounts available to your Pinterest user. Create a query named getAdAccounts, set Method to GET, and set the path to /v5/ad_accounts. This returns all ad accounts the authenticated Pinterest user has access to, including the ad account ID needed for all subsequent campaign queries. Create a Dropdown component named dropdown_adAccount and bind its options to {{ getAdAccounts.data.items.map(acc => ({ label: acc.name, value: acc.id })) }}. Configure getAdAccounts to run on page load. Next, create a query named getCampaigns to fetch campaigns for the selected ad account. Set Method to GET and path to /v5/ad_accounts/{{ dropdown_adAccount.value }}/campaigns. Add URL parameters: Key = campaign_status, Value = ACTIVE,PAUSED (comma-separated list). The response includes campaign IDs, names, objectives, status, and basic metadata. For campaign performance metrics, create getCampaignAnalytics with Method GET and path to /v5/ad_accounts/{{ dropdown_adAccount.value }}/campaigns/analytics. Add URL parameters: Key = campaign_ids, Value = {{ getCampaigns.data.items.map(c => c.id).join(',') }}, Key = start_date, Value = {{ dateRange.start.toISOString().split('T')[0] }}, Key = end_date, Value = {{ dateRange.end.toISOString().split('T')[0] }}, Key = columns, Value = SPEND_IN_DOLLAR,IMPRESSION_1,CLICKTHROUGH_1,CTR_2,CPC_IN_DOLLAR,TOTAL_CONVERSIONS,TOTAL_CONVERSION_VALUE_IN_MICRO_DOLLAR.
1// JavaScript transformer — merge campaign list with analytics data2const campaigns = getCampaigns.data?.items || [];3const analytics = getCampaignAnalytics.data || [];45// Build analytics lookup by campaign ID6const analyticsMap = {};7analytics.forEach(item => {8 const id = item.CAMPAIGN_ID;9 if (!analyticsMap[id]) analyticsMap[id] = { spend: 0, impressions: 0, clicks: 0, conversions: 0, convValue: 0 };10 const m = analyticsMap[id];11 m.spend += parseFloat(item.SPEND_IN_DOLLAR || 0);12 m.impressions += parseInt(item.IMPRESSION_1 || 0);13 m.clicks += parseInt(item.CLICKTHROUGH_1 || 0);14 m.conversions += parseInt(item.TOTAL_CONVERSIONS || 0);15 m.convValue += parseFloat((item.TOTAL_CONVERSION_VALUE_IN_MICRO_DOLLAR || 0) / 1000000);16});1718return campaigns.map(campaign => {19 const a = analyticsMap[campaign.id] || {};20 const spend = a.spend || 0;21 const convValue = a.convValue || 0;22 const clicks = a.clicks || 0;23 const impressions = a.impressions || 0;24 return {25 id: campaign.id,26 name: campaign.name || 'Unnamed',27 status: campaign.status,28 objective: campaign.objective_type || 'N/A',29 spend: `$${spend.toFixed(2)}`,30 impressions: impressions.toLocaleString(),31 clicks: clicks.toLocaleString(),32 ctr: impressions > 0 ? `${((clicks / impressions) * 100).toFixed(2)}%` : '0%',33 cpc: clicks > 0 ? `$${(spend / clicks).toFixed(2)}` : 'N/A',34 conversions: a.conversions || 0,35 conv_value: `$${convValue.toFixed(2)}`,36 roas: spend > 0 ? `${(convValue / spend).toFixed(2)}x` : 'N/A'37 };38});Pro tip: Pinterest's analytics API returns conversion values in micro-dollars (1/1,000,000 of a dollar). Always divide TOTAL_CONVERSION_VALUE_IN_MICRO_DOLLAR by 1,000,000 to get the dollar amount before displaying or calculating ROAS.
Expected result: The campaigns table shows all active and paused campaigns with spend, impressions, CTR, CPC, conversions, and ROAS calculated from the merged campaign and analytics data for the selected date range.
Build ad group and Pin-level performance queries
Create queries to drill down from campaign-level to ad group and Pin-level performance. When a user selects a campaign row in the campaigns table, trigger ad group queries for that specific campaign. Create getAdGroups with Method GET and path /v5/ad_accounts/{{ dropdown_adAccount.value }}/ad_groups. Add URL parameter: Key = campaign_ids, Value = {{ table_campaigns.selectedRow.id }}. This returns all ad groups within the selected campaign. For ad group analytics, create getAdGroupAnalytics with Method GET and path /v5/ad_accounts/{{ dropdown_adAccount.value }}/ad_groups/analytics. Add URL parameters matching the campaigns analytics query but with ad_group_ids instead of campaign_ids. Create getAds to retrieve individual promoted Pins: Method GET, path /v5/ad_accounts/{{ dropdown_adAccount.value }}/ads, URL parameter: Key = campaign_ids, Value = {{ table_campaigns.selectedRow.id }}. This returns Pin-level data including the Pin ID and creative details. Add a second Table component named table_adGroups bound to the ad group analytics transformer output. Set its onRowClick event to trigger getAds filtered by the selected ad group ID. The resulting Pins table can show Pin thumbnail URLs (displayed using an Image column type in Retool's Table component) alongside impressions, clicks, and saves per Pin.
1// JavaScript transformer — format ad group analytics2const adGroups = getAdGroups.data?.items || [];3const analytics = getAdGroupAnalytics.data || [];45const analyticsMap = {};6analytics.forEach(item => {7 const id = item.AD_GROUP_ID;8 if (!analyticsMap[id]) analyticsMap[id] = {9 spend: 0, impressions: 0, clicks: 0, conversions: 010 };11 analyticsMap[id].spend += parseFloat(item.SPEND_IN_DOLLAR || 0);12 analyticsMap[id].impressions += parseInt(item.IMPRESSION_1 || 0);13 analyticsMap[id].clicks += parseInt(item.CLICKTHROUGH_1 || 0);14 analyticsMap[id].conversions += parseInt(item.TOTAL_CONVERSIONS || 0);15});1617return adGroups.map(group => {18 const a = analyticsMap[group.id] || {};19 const spend = a.spend || 0;20 const impressions = a.impressions || 0;21 const clicks = a.clicks || 0;22 return {23 id: group.id,24 name: group.name || 'Unnamed',25 status: group.status,26 bid_type: group.bid_type || 'N/A',27 budget: group.budget_in_micro_currency28 ? `$${(group.budget_in_micro_currency / 1000000).toFixed(2)}`29 : 'N/A',30 spend: `$${spend.toFixed(2)}`,31 impressions: impressions.toLocaleString(),32 clicks: clicks.toLocaleString(),33 ctr: impressions > 034 ? `${((clicks / impressions) * 100).toFixed(2)}%`35 : '0%',36 conversions: a.conversions || 037 };38});Pro tip: Pinterest's budget and bid values in API responses are stored in micro-currency units (1/1,000,000 of the account currency). Always divide budget_in_micro_currency and bid values by 1,000,000 for display. This is different from conversion values which use micro-dollars.
Expected result: Selecting a campaign in the campaigns table triggers the ad groups query and populates the ad groups table with performance metrics. The drill-down hierarchy (campaign → ad group → Pins) works end-to-end with each selection triggering the next level of queries.
Add trend charts and schedule comparison analytics
Build visualization components to surface performance trends over time. Pinterest's analytics endpoints return daily data points when queried with a date range, making them ideal for time-series charts. Create a query named getDailySpendTrend with the campaign analytics endpoint, but request granular=DAY data by adding URL parameter: Key = granularity, Value = DAY. This returns one row per campaign per day, which you can aggregate into a time-series format for Retool's Chart component. Create a JavaScript transformer that converts the daily analytics array into a format suitable for Retool's Chart: group data by date and calculate total spend, impressions, and clicks across all selected campaigns per day. Add a Chart component to your app. Set its data property to {{ dailySpendTransformer.data }}. Configure it as a Line chart with Date as the X-axis and Spend as the Y-axis. Add a second data series for Clicks on a secondary Y-axis to show the spend vs. engagement correlation over time. Add a date range picker (DateRange component) to control the analytics window. Wire it to both the getCampaignAnalytics and getDailySpendTrend queries via the URL parameters. Set both queries to trigger on change of the date range selector. For comparing performance across different time periods (e.g., current month vs. last month), create a second set of analytics queries with a separate date range and display both datasets in the same chart using different line colors.
1// JavaScript transformer — format daily analytics for line chart2const dailyData = data || [];34// Group by date5const byDate = {};6dailyData.forEach(item => {7 const date = item.DATE; // YYYY-MM-DD format8 if (!byDate[date]) {9 byDate[date] = { date, spend: 0, impressions: 0, clicks: 0, conversions: 0 };10 }11 byDate[date].spend += parseFloat(item.SPEND_IN_DOLLAR || 0);12 byDate[date].impressions += parseInt(item.IMPRESSION_1 || 0);13 byDate[date].clicks += parseInt(item.CLICKTHROUGH_1 || 0);14 byDate[date].conversions += parseInt(item.TOTAL_CONVERSIONS || 0);15});1617// Sort by date and return array18return Object.values(byDate)19 .sort((a, b) => new Date(a.date) - new Date(b.date))20 .map(row => ({21 date: new Date(row.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }),22 spend: parseFloat(row.spend.toFixed(2)),23 impressions: row.impressions,24 clicks: row.clicks,25 conversions: row.conversions,26 ctr: row.impressions > 027 ? parseFloat(((row.clicks / row.impressions) * 100).toFixed(2))28 : 029 }));Pro tip: Pinterest's analytics API has a maximum date range of 90 days per request when using daily granularity. For longer-term trend analysis, build a Retool Workflow that fetches data in 90-day chunks and stores results in a Retool Database table for unlimited historical querying.
Expected result: The trend chart shows daily spend and click data as a line chart for the selected date range. The chart updates automatically when the date range picker is changed, giving the marketing team a visual performance overview.
Common use cases
Build a campaign performance comparison dashboard
Create a Retool app that lists all Pinterest Ads campaigns with their lifetime and date-range metrics: impressions, clicks, CTR, spend, CPC, and conversion value. Add date range filters and a campaign status dropdown (ACTIVE, PAUSED, ARCHIVED). Include a Chart component showing spend vs. conversion value over time for selected campaigns.
Build a Pinterest Ads campaign dashboard with a date range picker and status filter, a Table showing all campaigns with impressions, clicks, CTR, spend, and ROAS columns, and a line chart below showing daily spend and conversion value trends for the selected date range.
Copy this prompt to try it in Retool
Ad group and Pin-level creative performance analysis
Build a drill-down dashboard where selecting a campaign loads its ad groups, and selecting an ad group loads the individual Pins with their performance metrics. Show thumbnail preview URLs, click counts, save rates, and conversion data side by side to identify which creatives are driving the most engagement and revenue.
Create a hierarchical Pinterest Ads panel where a campaign dropdown loads ad groups in a Table, selecting an ad group loads Pins in a second Table with thumbnail URL, impressions, clicks, saves, CTR, and conversion data, and a selected Pin shows a preview image using an Image component.
Copy this prompt to try it in Retool
Shopping catalog and conversion tracking dashboard
Build a Pinterest shopping ads operations panel that shows catalog feed status, product group performance data, and conversion events by campaign. Track which product categories are driving the most Pinterest-attributed purchases and identify catalog errors that may be limiting product visibility.
Build a shopping ads panel that queries Pinterest catalog feeds to show feed status and product count, then loads ad group performance filtered to shopping campaign types, with a Table showing product groups ranked by ROAS and a bar chart comparing conversion rates across product categories.
Copy this prompt to try it in Retool
Troubleshooting
OAuth connection fails with 'invalid_client' error during Pinterest authorization
Cause: The Client ID or Client Secret entered in the Retool resource configuration does not match the credentials in the Pinterest Developer portal, or the redirect URI configured in Pinterest does not match Retool's OAuth consumer URL.
Solution: Verify the Client ID and Client Secret from the Pinterest Developer portal (app.pinterest.com/business/apps) match exactly what is stored in your Retool configuration variables. Check the Redirect URI in your Pinterest app settings — it must be exactly https://login.retool.com/oauth/consumer for Retool Cloud, with no trailing slash. Also confirm your Pinterest app has been granted Marketing API access (not just standard API access).
Campaign analytics query returns empty array even though campaigns are active
Cause: The date range provided to the analytics endpoint falls outside the window where campaign data exists, or the campaign IDs parameter is empty because the campaigns list query has not yet completed when the analytics query runs.
Solution: Ensure the campaigns list query (getCampaigns) runs before the analytics query triggers. Set getCampaigns to run on page load, and configure getCampaignAnalytics to only trigger after getCampaigns completes (use the query's On Success event handler). Verify the date format for start_date and end_date parameters — Pinterest requires YYYY-MM-DD format. Also check that the selected date range includes days where the campaigns were active.
1// Correct date format for Pinterest analytics API2// start_date and end_date URL parameter values:3{{ new Date(dateRange.start).toISOString().split('T')[0] }}4{{ new Date(dateRange.end).toISOString().split('T')[0] }}403 Forbidden error on campaign or ad group endpoints
Cause: The authenticated Pinterest user does not have the ads:read permission scope on their OAuth token, or the Pinterest app has not been granted Marketing API access for production use.
Solution: Disconnect and reconnect the OAuth account in your Retool resource settings. Verify that the resource's OAuth scope field includes 'ads:read' (and 'catalogs:read' if using shopping catalog endpoints). If the error persists, check the Pinterest Developer portal to confirm your app has been approved for Marketing API access — development mode apps may have restricted access to account data.
Conversion values appear 1,000,000x too large in dashboard displays
Cause: Pinterest's Marketing API returns monetary values (conversion value, budget, bids) in micro-currency units, where 1 micro-dollar = $0.000001. This is a common API convention for avoiding floating point precision issues.
Solution: In your JavaScript transformers, divide all micro-currency values by 1,000,000 before displaying them: const dollarValue = (item.TOTAL_CONVERSION_VALUE_IN_MICRO_DOLLAR / 1000000).toFixed(2). Check the Pinterest API documentation for each field to confirm whether values are in dollars or micro-dollars — SPEND_IN_DOLLAR uses actual dollars, while budget and conversion value fields use micro-currency.
1// Correct micro-currency conversion in transformer2const conversionValue = parseFloat(3 (item.TOTAL_CONVERSION_VALUE_IN_MICRO_DOLLAR || 0) / 10000004).toFixed(2);5const budget = parseFloat(6 (group.budget_in_micro_currency || 0) / 10000007).toFixed(2);Best practices
- Store Pinterest Client ID and Client Secret as Secret configuration variables in Retool — never paste them directly into resource fields where they might be visible to other Retool users.
- Set getCampaigns and getAdAccounts queries to run automatically on page load and use their results to populate dropdown filters, reducing the number of manual triggers needed to navigate the dashboard.
- Always divide Pinterest micro-currency values (TOTAL_CONVERSION_VALUE_IN_MICRO_DOLLAR, budget_in_micro_currency) by 1,000,000 in transformers before displaying or calculating ROAS — mixing micro and dollar units produces dramatically incorrect numbers.
- Use Retool's query caching for campaign list queries (getCampaigns, getAdAccounts) which change infrequently — set a cache duration of 5-10 minutes to reduce API calls during active dashboard use.
- Build the analytics queries to accept comma-separated campaign IDs from the campaigns table selection, allowing users to select multiple campaigns for aggregate performance comparison rather than analyzing each campaign individually.
- Add CTR and ROAS calculated columns in your transformers rather than displaying raw impressions and spend — these derived metrics are what marketing teams actually use to evaluate Pinterest campaign performance.
- Use Retool Workflows scheduled daily to cache Pinterest analytics data in a Retool Database table, enabling faster dashboard loads and historical analysis beyond Pinterest's 90-day API limit.
- Configure table column visibility to hide internal IDs (campaign.id, ad_group.id) from display while keeping them available as data for downstream queries and event handlers.
Alternatives
Facebook Ads (Meta) has broader audience targeting capabilities and higher volume for most businesses, while Pinterest Ads excels specifically for visual shopping categories where Pinterest's discovery-intent audience drives higher purchase intent.
TikTok Ads reaches a younger, video-first audience and is better for entertainment and trending product categories, while Pinterest Ads performs better for planned purchases in home, fashion, food, and wedding verticals.
Google Ads captures high-intent search traffic and has a native Retool connector option, while Pinterest Ads targets users earlier in the discovery phase with visual content that drives top-of-funnel awareness and purchase inspiration.
Frequently asked questions
Does Pinterest Ads have a native connector in Retool?
No, Pinterest Ads does not have a native connector in Retool. You need to configure it as a REST API Resource using Pinterest Marketing API OAuth 2.0 authentication. The Marketing API requires a Pinterest Developer App with Marketing API access approval, which may take a few days to receive from Pinterest's developer team.
What Pinterest Marketing API scopes do I need for a reporting dashboard?
For a read-only reporting dashboard, you need the ads:read scope (for campaign, ad group, and Pin analytics) and optionally catalogs:read (for shopping catalog data). If you want to create or modify campaigns from Retool, you'll also need ads:write. Request the minimum scopes needed for your use case — Pinterest's API review process evaluates scope usage.
How do I calculate ROAS from Pinterest Analytics API data?
Pinterest's analytics API returns SPEND_IN_DOLLAR (actual dollars) and TOTAL_CONVERSION_VALUE_IN_MICRO_DOLLAR (micro-dollars, 1/1,000,000 of a dollar). To calculate ROAS: first convert conversion value to dollars by dividing TOTAL_CONVERSION_VALUE_IN_MICRO_DOLLAR by 1,000,000, then divide by SPEND_IN_DOLLAR. In a JavaScript transformer: const roas = spend > 0 ? (convValueDollars / spend).toFixed(2) + 'x' : 'N/A'.
What is the maximum date range for Pinterest analytics queries?
Pinterest's analytics API allows a maximum of 90 days per request when using daily granularity. For monthly or weekly granularity, the limit extends to 90 days for MONTH and 90 days for WEEK granularity as well. For historical analysis beyond 90 days, use a Retool Workflow to fetch data in consecutive 90-day chunks and store results in a Retool Database table.
Can I use Retool to create or pause Pinterest campaigns, not just read data?
Yes, if your Pinterest Developer App is approved for the ads:write scope, you can create, update, and pause campaigns, ad groups, and ads using POST and PATCH requests to the Marketing API. In Retool, add write queries with the appropriate methods and bind them to button event handlers with confirmation modals. Note that campaign creation requires valid catalog data for shopping campaigns and approved creatives for promoted Pins.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation