# TikTok Ads

- Tool: Bubble
- Difficulty: Intermediate
- Time required: 3–4 hours
- Last updated: July 2026

## TL;DR

Connect Bubble to TikTok Ads by configuring the API Connector with TikTok's non-standard `Access-Token` header (not `Authorization: Bearer`) stored as a Private shared header. Because TikTok access tokens expire every 24 hours, you must also build a Backend Workflow scheduled every 20 hours to refresh the token automatically — otherwise your reporting dashboard will break daily.

## TikTok Ads + Bubble: A Campaign Performance Dashboard

The TikTok Marketing API is one of the more technically demanding ad platform integrations for Bubble because of two non-obvious requirements: a non-standard header name (`Access-Token` instead of `Authorization: Bearer`) and a 24-hour token expiry that requires scheduled automation to avoid daily outages. Unlike Facebook Ads, which supports non-expiring System User tokens, every TikTok API call requires a fresh OAuth access token — making the Backend Workflow token refresh architecture mandatory, not optional.

The payoff is access to metrics you cannot get from any other platform: video thumb-stop rate (percentage of viewers who watch past the first second), 2-second video views, and 6-second hold rate. For brands running TikTok campaigns alongside Meta or Google, surfacing these in a unified Bubble dashboard gives a genuinely useful cross-channel view.

Bubble's API Connector handles the security correctly: the Private checkbox on the `Access-Token` shared header ensures the token never reaches the user's browser. The token itself lives in an AppConfig data type in Bubble's database, so the scheduled refresh workflow can update it without any code changes. Once configured, the dashboard can show campaign-level or ad-level performance for any date range, refreshed on demand or on a schedule.

## Before you start

- A TikTok for Business account with an active Ads Manager account and at least one advertiser account
- Access to TikTok's developer portal at developers.tiktok.com to create a Marketing API app
- A Bubble app on a paid plan (Starter or above) to use Backend Workflows for scheduled token refresh
- The API Connector plugin installed in your Bubble app (Plugins tab → Add plugins → 'API Connector' by Bubble)
- Your TikTok advertiser_id, which appears in the Ads Manager URL (e.g., ads.tiktok.com/i18n/dashboard?aadvid=123456789)

## Step-by-step guide

### 1. Create a TikTok Marketing API app and complete OAuth

Open developers.tiktok.com and sign in with your TikTok for Business account. Click 'Manage apps' → 'Create an app'. Under App Type select 'Web', enter your app name and description. In the Permissions section, enable the Marketing API scopes: `ads:read` (required for campaign data) and `campaign:read` (for campaign list). Add a redirect URI — for development this can be `https://yourapp.bubbleapps.io/oauth_redirect`, which you'll use to capture the authorization code.

Once the app is approved (usually instant for Marketing API), note your App ID and App Secret from the app detail page. To initiate the OAuth flow, construct the authorization URL: `https://business-api.tiktok.com/portal/auth?app_id=YOUR_APP_ID&state=YOUR_STATE&redirect_uri=YOUR_REDIRECT_URI&scope=ads:read,campaign:read`. Open this URL in a browser, log in with your TikTok Ads account, and authorize the app. TikTok redirects to your redirect URI with a `code` parameter in the URL — copy this authorization code immediately, as it expires in 10 minutes.

Exchange the authorization code for tokens by making a POST request to `https://business-api.tiktok.com/open_api/v1.3/oauth2/access_token/` with body containing app_id, secret, and auth_code. The response contains `access_token` (valid 24 hours), `refresh_token` (valid 365 days), and `advertiser_ids` (the list of advertiser accounts you authorized). Save both tokens and the advertiser_id — you need all three in the next step.

```
POST https://business-api.tiktok.com/open_api/v1.3/oauth2/access_token/
Content-Type: application/json

{
  "app_id": "YOUR_APP_ID",
  "secret": "YOUR_APP_SECRET",
  "auth_code": "AUTH_CODE_FROM_REDIRECT"
}

// Success response:
{
  "code": 0,
  "message": "OK",
  "data": {
    "access_token": "act.xxxxxxxxxxxx",
    "refresh_token": "rft.xxxxxxxxxxxx",
    "advertiser_ids": ["1234567890123"],
    "expires_in": 86400
  }
}
```

**Expected result:** You have an `access_token`, `refresh_token`, and `advertiser_id`. The access_token starts with `act.` and is 24+ characters long.

### 2. Store tokens in a Bubble AppConfig data type with Privacy rules

Before touching the API Connector, set up a Bubble database 'Thing' to hold your TikTok credentials. In Bubble, click the Data tab → Data types → 'Create a new type' → name it 'AppConfig'. Add four fields: `tiktok_access_token` (text), `tiktok_refresh_token` (text), `tiktok_token_expiry` (date), and `tiktok_advertiser_id` (text).

Create a single AppConfig record now: go to Data → App Data → All AppConfigs → 'New entry'. Paste the access_token into tiktok_access_token, the refresh_token into tiktok_refresh_token, set tiktok_token_expiry to the current datetime plus 24 hours, and enter your advertiser_id in tiktok_advertiser_id.

The reason for this architecture: when the API Connector runs, it needs to pull the current access_token value from the database dynamically. If you hardcode the token in the API Connector header, it becomes stale after 24 hours and you'd need to update it manually every day. Storing it in AppConfig lets the scheduled refresh workflow update it in place — the API Connector always reads the latest value.

Critical: set Privacy rules for AppConfig immediately. Go to Data tab → Privacy → AppConfig → 'Define rules'. Remove any default 'Everyone can view all fields' rule and replace it with a rule that requires the current user to be logged in and have an admin role. The access_token and refresh_token are secrets — they must never be readable by anonymous users through Bubble's Data API.

```
// Bubble Data Type: AppConfig
// Fields:
// tiktok_access_token   (text)
// tiktok_refresh_token  (text)
// tiktok_token_expiry   (date)
// tiktok_advertiser_id  (text)

// Privacy Rule (Data tab → Privacy → AppConfig):
// Condition: Current User is logged in [and role = admin]
// Access: View all fields = YES
// All other conditions: no access by default
```

**Expected result:** Data tab shows one AppConfig record with tiktok_access_token populated. Privacy rules on AppConfig restrict anonymous access to these fields.

### 3. Configure the API Connector with TikTok's Access-Token header

Click the Plugins tab in the left sidebar → Add plugins → search for 'API Connector' → Install the 'API Connector' plugin by Bubble (free, if not already installed). After installation, click 'API Connector' in your plugins list to open the configuration panel.

Click 'Add another API'. Name it 'TikTok Marketing API'. In the Shared headers section, click '+ Add a shared header'. Enter the header name exactly as: `Access-Token` — capital A, capital T, with a hyphen in the middle. In the value field, use a dynamic expression to reference your AppConfig: click the blue toggle, then search for AppConfig → 'Do a search for AppConfigs:first item's tiktok_access_token'. Enable the 'Private' checkbox on this header — this is critical. Private means Bubble never sends this value to the browser; it stays on Bubble's servers.

Do NOT use `Authorization: Bearer` — TikTok Marketing API ignores the standard Authorization header entirely and requires the custom `Access-Token` header. Using Authorization: Bearer results in error code 40001 ('Invalid access token') — a misleading error that makes users think the token itself is wrong, when the actual problem is just the header name.

Leave the Authentication dropdown set to 'None' — TikTok authentication is handled entirely by the `Access-Token` header. Set the base URL for this API to: `https://business-api.tiktok.com/open_api/v1.3`

```
// API Connector: TikTok Marketing API
{
  "api_name": "TikTok Marketing API",
  "base_url": "https://business-api.tiktok.com/open_api/v1.3",
  "authentication": "none",
  "shared_headers": [
    {
      "name": "Access-Token",
      "value": "<private: AppConfigs first item's tiktok_access_token>",
      "private": true
    }
  ]
}
```

**Expected result:** The API Connector shows 'TikTok Marketing API' with a Private Access-Token shared header. The header value displays as a masked field when Private is enabled.

### 4. Build the campaign reporting API call and initialize it

Inside the TikTok Marketing API configuration, click 'Add a call'. Name it 'Get Campaign Metrics'. Set the method to GET and the path to `/report/integrated/get/`.

Add the following URL parameters (click '+ Add parameter' for each):
- `advertiser_id` — value: dynamic reference to AppConfig's tiktok_advertiser_id
- `report_type` — value: `BASIC` (literal string)
- `dimensions` — value: `["campaign_id","stat_time_day"]` — enter this exact JSON array as a literal string in the parameter value field
- `metrics` — value: `["campaign_name","spend","impressions","clicks","cpm","video_play_actions","video_watched_2s","video_watched_6s"]` — literal JSON array string
- `start_date` — set as a dynamic parameter (pass date strings in YYYY-MM-DD format)
- `end_date` — dynamic parameter
- `page_size` — value: `100`

IMPORTANT: The `dimensions` and `metrics` parameters must be literal JSON array strings. Do not use Bubble's list format or dynamic list expressions for these — paste the exact JSON string as shown. TikTok parses these as JSON arrays from the URL query string.

Click 'Initialize call'. In the test input fields that appear, enter your advertiser_id, set start_date to 3 days ago (e.g., `2026-07-07`) and end_date to 2 days ago (`2026-07-08`). Avoid using today's date — TikTok has 12–24 hour data latency so today's data may be empty. Click 'Initialize'. If successful, Bubble detects the response structure and shows auto-detected fields from TikTok's response.

Set 'Use as' to 'Data' so the call can feed a Repeating Group. Expand the response to map fields like `data.list[].metrics.spend`, `data.list[].metrics.impressions`, and `data.list[].dimensions.campaign_id`.

```
// TikTok Reporting API call — GET /report/integrated/get/
{
  "method": "GET",
  "path": "/report/integrated/get/",
  "params": [
    { "name": "advertiser_id", "value": "<AppConfig first item's tiktok_advertiser_id>" },
    { "name": "report_type", "value": "BASIC" },
    { "name": "dimensions", "value": "[\"campaign_id\",\"stat_time_day\"]" },
    {
      "name": "metrics",
      "value": "[\"campaign_name\",\"spend\",\"impressions\",\"clicks\",\"cpm\",\"video_play_actions\",\"video_watched_2s\",\"video_watched_6s\"]"
    },
    { "name": "start_date", "value": "<dynamic: YYYY-MM-DD>" },
    { "name": "end_date", "value": "<dynamic: YYYY-MM-DD>" },
    { "name": "page_size", "value": "100" }
  ]
}
```

**Expected result:** Initialize call returns HTTP 200 with campaign data. Bubble shows auto-detected fields from the response including data.list, dimensions, and metrics sub-objects. 'Use as: Data' enables Repeating Group binding.

### 5. Build a scheduled Backend Workflow for automatic token refresh

This step is mandatory for production use. Without it, your dashboard will return error 40001 every 24 hours when the TikTok access token expires. Backend Workflows require a Bubble paid plan (Starter or above).

First, add a token refresh call in the API Connector. Inside your TikTok Marketing API configuration, click 'Add a call'. Name it 'Refresh Access Token'. Set method to POST, path to `/oauth2/refresh_token/`. In the Body section, add: `app_id` (your TikTok app ID as a static string), `secret` (your TikTok app secret — store this in AppConfig as `tiktok_app_secret` and reference it dynamically), and `refresh_token` (dynamic reference to AppConfig's tiktok_refresh_token). Initialize this call with test values to confirm it returns a new access_token.

Now build the Backend Workflow. In the Bubble editor, click 'Backend Workflows' in the left navigation. Click 'Add another workflow' → name it 'Refresh TikTok Token'. Click the trigger type dropdown and select 'Scheduled API Workflow'. Add these three actions in order:

Action 1: 'Plugins → TikTok Marketing API - Refresh Access Token' with the app_id, secret, and refresh_token values.
Action 2: 'Make changes to thing' → Do a search for AppConfigs:first item → set tiktok_access_token = result of Step 1's data.access_token → set tiktok_token_expiry = Current date/time + 24 hours.
Action 3: 'Schedule API Workflow' → workflow = Refresh TikTok Token → time = Current date/time + 20 hours. This makes the workflow self-scheduling — each run automatically schedules the next one.

To start the chain: go to any page in your Bubble editor, create a button 'Start Token Refresh', with action 'Schedule API Workflow' → Refresh TikTok Token → time = Current date/time + 20 hours. Press this button once in your live app. From that point the workflow fires automatically every 20 hours.

```
// Backend Workflow: Refresh TikTok Token
// Trigger: Scheduled API Workflow (paid Bubble plan required)

// Step 1: Call token refresh endpoint
API Call: TikTok Marketing API - Refresh Access Token
Body: {
  "app_id": "YOUR_APP_ID",
  "secret": "<AppConfig first item's tiktok_app_secret>",
  "refresh_token": "<AppConfig first item's tiktok_refresh_token>"
}

// Step 2: Save new token to database
Make changes to: Do a search for AppConfigs:first item
  tiktok_access_token = Step 1's result data.access_token
  tiktok_token_expiry = Current date/time + 24 hours

// Step 3: Schedule next refresh in 20 hours
Schedule API Workflow:
  Workflow: Refresh TikTok Token
  Time: Current date/time + 20 hours
```

**Expected result:** Backend Workflow 'Refresh TikTok Token' appears in the Scheduled workflows list. After pressing the start button once in the live app, the workflow fires every 20 hours and the AppConfig record shows updated tiktok_access_token values.

### 6. Build the Bubble dashboard with caching and video metrics

Create a new Bubble page called 'tiktok-dashboard'. Add date picker elements for start date and end date — set their default values to 'Current date/time - 2 days' and 'Current date/time - 1 day' respectively to avoid the reporting latency issue.

To avoid burning Workload Units on every page load, cache TikTok results in Bubble's database. Create a data type 'TikTokCampaignMetric' with fields: campaign_id (text), campaign_name (text), stat_date (date), spend (number), impressions (number), clicks (number), cpm (number), video_play_actions (number), video_watched_2s (number), video_watched_6s (number), fetched_at (date).

Add a 'Load Metrics' button. Its workflow should: call API Connector → TikTok Marketing API → Get Campaign Metrics with the date picker values, then for each item in the result list, create or update a TikTokCampaignMetric record (use 'Create a new TikTokCampaignMetric' or 'Make changes to' if you identify by campaign_id + stat_date).

Add a Repeating Group bound to 'Do a search for TikTokCampaignMetrics' with constraints for the selected date range, sorted by spend descending. In each cell, show: campaign_name, spend formatted as currency, impressions (formatted with commas), and two calculated text elements for video engagement:
- Thumb-stop rate: RG's current cell's TikTokCampaignMetric's video_play_actions / RG's current cell's TikTokCampaignMetric's impressions * 100, formatted as '0.0%'
- 6-second hold rate: RG's current cell's TikTokCampaignMetric's video_watched_6s / RG's current cell's TikTokCampaignMetric's video_play_actions * 100, formatted as '0.0%'

Set Privacy rules for TikTokCampaignMetric: Data tab → Privacy → TikTokCampaignMetric → restrict to logged-in admin users only. Campaign spend data is sensitive and should not be publicly readable via Bubble's Data API.

RapidDev's team has built Bubble dashboards integrating TikTok, Meta, and Google Ads across dozens of client projects — if you need help architecting the token refresh system or multi-platform reporting, book a free scoping call at rapidevelopers.com/contact.

```
// Bubble data type: TikTokCampaignMetric
// campaign_id         (text)
// campaign_name       (text)
// stat_date           (date)
// spend               (number)
// impressions         (number)
// clicks              (number)
// cpm                 (number)
// video_play_actions  (number)
// video_watched_2s    (number)
// video_watched_6s    (number)
// fetched_at          (date)

// Repeating Group data source:
// Search for TikTokCampaignMetrics
// Constraint: stat_date >= DatePicker_Start's value
// Constraint: stat_date <= DatePicker_End's value
// Sort: spend descending

// Thumb-stop rate text formula:
// (video_play_actions / impressions * 100):formatted as 0.0%

// 6-second hold rate text formula:
// (video_watched_6s / video_play_actions * 100):formatted as 0.0%
```

**Expected result:** Dashboard page shows a Repeating Group with campaign performance rows including TikTok-specific video metrics. The 'Load Metrics' button refreshes data from TikTok's API and populates the database cache.

## Best practices

- Always store TikTok access_token and refresh_token in a Bubble database data type with Privacy rules enabled — never in plain text App Data accessible without authentication or through Bubble's Data API.
- Set the scheduled token refresh to every 20 hours (not 24) to provide a 4-hour buffer before the hard 24-hour token expiry. Missing the refresh window locks out the dashboard until a manual intervention.
- Cache TikTok reporting results in a Bubble data type with a fetched_at timestamp. Read from the database on page load and only call the live API when the user explicitly clicks 'Refresh'. This dramatically reduces Workload Unit consumption.
- Request only the specific metrics you display using the `metrics` parameter. Requesting all available metrics increases response payload size and API processing time — define a minimal metrics list per use case.
- Default your date range to start 2 days ago when the dashboard first loads. TikTok has 12–24 hour data latency; showing today's numbers displays incomplete figures that may mislead campaign decisions.
- Set Privacy rules on your TikTokCampaignMetric data type to restrict access to authenticated admin users only. Campaign spend data is sensitive business intelligence that should not be readable through Bubble's public Data API.
- Log each token refresh run in a 'TokenRefreshLog' data type with timestamp and success or failure status. This audit trail helps you diagnose issues if the self-scheduling chain breaks, such as a failed refresh that stops future scheduled runs.
- If building a multi-client agency app, create a per-client credentials data type with separate TikTok tokens and advertiser IDs for each client. A single shared AppConfig is only appropriate for single-account deployments.

## Use cases

### TikTok Campaign Performance Dashboard

Pull spend, impressions, clicks, CPM, and video engagement metrics for active TikTok campaigns into a Bubble dashboard. Filter by date range, campaign, or ad group. Ideal for agencies managing TikTok budgets for clients who need self-serve reporting without TikTok Ads Manager access.

Prompt example:

```
Build a Bubble page with a date range picker and a Repeating Group showing TikTok campaign name, spend, impressions, CPM, thumb-stop rate, and 6-second hold rate for the selected period. Add a refresh button that calls the TikTok Reporting API and updates the cached data.
```

### Cross-Channel Ad Spend Comparison

Combine TikTok campaign data with Facebook Ads or Google Ads metrics in a single Bubble dashboard. Store normalized data (impressions, spend, CPC, conversions) from each platform in a shared 'AdMetrics' data type, then display side-by-side comparisons with conditional formatting that highlights the best-performing channel each week.

Prompt example:

```
Create a Bubble reporting page comparing TikTok, Facebook, and Google ad spend and CPA for the past 7 days. Store metrics from each platform in a shared data type and show a summary card for each channel with color-coded performance indicators.
```

### Video Creative Performance Tracker

Track which TikTok video creatives perform best by pulling ad-level data including video_watched_2s, video_watched_6s, and video_views. Store results in a Bubble 'Creative' data type and surface the top 5 videos by thumb-stop rate each week for creative team review.

Prompt example:

```
Build a Bubble page listing TikTok ad creatives ranked by thumb-stop rate, showing a thumbnail preview URL, 2-second view rate, 6-second hold rate, and total spend. Allow filtering by campaign and date range.
```

## Troubleshooting

### API returns error code 40001 'Invalid access token' on every call

Cause: Either the header name is wrong (should be `Access-Token` not `Authorization: Bearer`) or the 24-hour token has expired. TikTok's error code 40001 covers both a wrong header name and an expired token, making it difficult to distinguish the two causes without checking both.

Solution: First verify the header name in API Connector → TikTok Marketing API → shared headers. Confirm it reads exactly `Access-Token` (capital A, capital T, hyphen). If the header name is correct, the token has likely expired. Manually run the 'Refresh TikTok Token' Backend Workflow from the Backend Workflows panel using 'Run now', then re-test your API call.

### Initialize call fails with 'There was an issue setting up your call' or returns empty data

Cause: The `dimensions` and `metrics` URL parameters are not formatted as literal JSON array strings, or the test date range includes today's date where TikTok has no finalized data yet due to 12–24 hour reporting latency.

Solution: Ensure `dimensions` and `metrics` are entered as literal strings in the parameter value field: `["campaign_id","stat_time_day"]` and the metrics array respectively. For initialization, use a date range from 3 days ago to 2 days ago to guarantee complete data exists. Avoid today or yesterday.

### Backend Workflow 'Refresh TikTok Token' is not available or cannot be created

Cause: Scheduled API Workflows (Backend Workflows) are only available on paid Bubble plans. The free plan does not support Backend Workflows at all.

Solution: Upgrade to Bubble's Starter plan or above to unlock Backend Workflows. As a temporary workaround on a free plan, manually paste a fresh access_token into your AppConfig record each day after running the OAuth flow manually — but this is not a viable production solution.

### API returns error code 40100 'No permission' or advertiser_id mismatch errors

Cause: The advertiser_id stored in AppConfig does not match an account that was authorized in the OAuth flow, or the TikTok developer app is missing required Marketing API scopes (`ads:read`, `campaign:read`).

Solution: Go to developers.tiktok.com → your app → Permissions and verify `ads:read` and `campaign:read` are enabled and approved. Cross-reference the advertiser_id in your AppConfig against the `data.advertiser_ids` array returned by the original OAuth token exchange — they must match exactly.

## Frequently asked questions

### Why does TikTok use `Access-Token` instead of `Authorization: Bearer`?

TikTok Marketing API was designed before widespread adoption of the Authorization: Bearer standard and uses a custom header name `Access-Token`. This is an intentional design choice by TikTok, not a bug. Always use exactly `Access-Token` as the header name — any variation, including using `Authorization`, returns error code 40001.

### Can I use TikTok Ads API on Bubble's free plan?

The API Connector calls themselves work on any Bubble plan, but the scheduled token refresh Backend Workflow requires a paid plan (Starter or above). Without the scheduled refresh, the access token expires every 24 hours and the dashboard breaks unless you manually update the token each day. For any production deployment, a paid Bubble plan is effectively required.

### What is thumb-stop rate and how do I calculate it in Bubble?

Thumb-stop rate measures the percentage of users who stopped scrolling to watch your TikTok ad past the first second. In the TikTok API, it equals video_play_actions divided by impressions times 100. In Bubble, create a text element in your Repeating Group and set its value to: Current cell's TikTokCampaignMetric's video_play_actions / Current cell's TikTokCampaignMetric's impressions * 100, formatted as '0.0%'.

### The Reporting API returns empty data for today or yesterday. Is this a bug?

No — TikTok has a 12–24 hour data latency for its Reporting API. Data for 'today' is always incomplete, and yesterday's data may still be finalizing. Always set your default date range to start at least 2 days ago to consistently display complete, accurate campaign data. This is documented behavior from TikTok, not a Bubble integration issue.

### Can I write data back to TikTok Ads from Bubble — for example, pause a campaign or update a budget?

Yes. TikTok Marketing API supports write operations including updating campaign status (active/paused) and budget via POST to `/campaign/update/`. The same `Access-Token` header applies. Campaign management write operations require the `campaign:write` scope — if you only authorized read scopes initially, you'll need to re-run the OAuth flow with the additional scope and obtain a new token pair.

### How do I handle TikTok's 1,000-row limit per Reporting API response?

The Reporting API returns a maximum of 1,000 rows per request. For large accounts with many campaigns over a wide date range, implement pagination using the `page` and `page_size` parameters. Check the response's `page_info.total_page` field to know how many pages exist. In Bubble, use a 'Schedule API Workflow' with a page counter parameter to loop through all pages and store each batch in your TikTokCampaignMetric data type.

---

Source: https://www.rapidevelopers.com/bubble-integrations/tiktok-ads
© RapidDev — https://www.rapidevelopers.com/bubble-integrations/tiktok-ads
