> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt # Linking X Ads as a source Canonical URL: https://posthog.com/docs/data-pipelines/sources/twitter-ads > **Cloud region:** This page uses US Cloud hosts. For an EU Cloud project, use `https://eu.posthog.com` instead of `https://us.posthog.com` or `https://app.posthog.com` (app host for links). ## On this page - [Prerequisites](#prerequisites) - [Adding a data source](#adding-a-data-source) - [Sync modes](#sync-modes) - [Configuration](#configuration) - [Supported tables](#supported-tables) - [Troubleshooting](#troubleshooting) ![](https://res.cloudinary.com/dmukukwp6/image/upload/texture_tan_9608fcca70) ![](https://res.cloudinary.com/dmukukwp6/image/upload/texture_tan_dark_a92b0e022d) Let AI connect your sources for you Skip the manual setup — run this in your project and the wizard auto-detects your databases and APIs and connects them to PostHog. `npx @posthog/wizard warehouse` ![PostHog Wizard hedgehog](https://res.cloudinary.com/dmukukwp6/image/upload/wizard_3f8bb7a240.png) ![](https://res.cloudinary.com/dmukukwp6/image/upload/wizard_3f8bb7a240.png)Let AI connect your sources for you The X Ads connector syncs one X ad account's campaigns, line items, promoted posts and daily performance stats into PostHog. Use it to put ad spend next to the signups and revenue it produced, instead of reading the two in separate tools. For organic account data such as posts, mentions and followers, use the Twitter (X) source instead. This source is rolling out gradually. If you do not see X Ads in the source list, [contact support](https://us.posthog.com/#panel=support%3Asupport%3Adata_warehouse%3A%3Atrue) to be enabled. ## Prerequisites - An X ad account you can log into. - An X user with permission on that ad account. PostHog syncs through the account you connect, so it can only read ad accounts that user can already see. You do not need your own X developer app or API keys. PostHog connects through its own X application, so there is nothing to register and no API credits to buy. ## Adding a data source 1. In PostHog, go to the [Sources tab](https://app.posthog.com/data-management/sources) of the data pipeline section. 2. Click **\+ New source** and click **Link** next to this source. 3. Enter your credentials (see [Configuration](#configuration) below) and click **Next**. 4. Select the tables you want to sync, choose a sync method and frequency, then click **Import**. Once the syncs are complete, you can start querying this data in PostHog. Connecting X Ads takes two steps rather than a set of credentials: 1. Click **Connect X account**. X asks you to authorize PostHog, then returns you to the setup form. 2. Pick the **Ad account** to sync. The dropdown lists every ad account the connected X user can access, by name. To sync more than one ad account, add one source per account and give each a different table prefix. ## Sync modes Each table can be synced in one of several modes, depending on what the source supports: - **Webhook** (when available) – the source pushes changes to PostHog in real time. Fastest freshness, lowest ongoing cost, and the only mode that reliably captures updates and deletes. - **Incremental** – only new or updated rows are synced on each run, using a cursor field (such as an `updated_at` timestamp). Cheaper than a full refresh, but deletes aren't captured. - **Append only** – new rows are appended using a cursor field; existing rows are never updated. Ideal for immutable, append-only tables like event logs. - **Full refresh** – the whole table is reloaded on every sync. Use it when a table has no reliable cursor or when you need deletions reflected. See [sync methods](https://posthog.com/docs/data-pipelines/sources.md#sync-methods) for a full explanation of how each mode works and how to choose between them. The two stats tables, `campaign_stats` and `line_item_stats`, sync incrementally on `date`. Each run re-reads the last three days as well as any new ones, because X keeps revising recent figures as it reconciles billing. A row that changes inside that window is corrected in place. The five entity tables, `campaigns`, `line_items`, `promoted_tweets`, `funding_instruments` and `media_creatives`, are full refresh. They are small, and X offers no reliable change timestamp on them. The first sync of a stats table walks from the ad account's creation date to today, one week at a time, so a long-running account takes a while to backfill. Later runs only cover new days plus the three-day window. ## Configuration | Option | Type | Required | | --- | --- | --- | | `X Ads account` | oauth | Yes | | `Ad account` | oauth-account-select | Yes | ## Supported tables | Table | Description | Sync method | Incremental field | Primary key | | --- | --- | --- | --- | --- | | `campaigns` | An X advertising campaign containing line items and funded by a funding instrument. | Full refresh | — | id | | `line_items` | An ad group within an X campaign, with its objective, targeting, and bid settings. | Full refresh | — | id | | `promoted_tweets` | An association between an X Post and the line item that promotes it. | Full refresh | — | id | | `funding_instruments` | A payment or credit arrangement funding X advertising campaigns. | Full refresh | — | id | | `media_creatives` | A media creative associated with an X advertising line item. | Full refresh | — | id | | `lead_forms` | A native lead form that Lead Gen ads attach to a card to collect leads. | Full refresh | — | id | | `campaign_stats` | Daily unsegmented X advertising metrics for a campaign, separated by placement. | Incremental, Full refresh | date | entity\_id, date, placement | | `line_item_stats` | Daily unsegmented X advertising metrics for a line item, separated by placement. | Incremental, Full refresh | date | entity\_id, date, placement | ### Reading the stats tables Three things about `campaign_stats` and `line_item_stats` catch people out: **One row per entity, per day, per placement.** X reports placements separately, so a single campaign on a single day produces up to three rows: `ALL_ON_TWITTER`, `SPOTLIGHT` and `TREND`. Sum across `placement` for a campaign's daily total, and never join on `entity_id` and `date` alone. **Spend is in micros.** `billed_charge_local_micro` is one millionth of a currency unit, so divide by 1,000,000 to get an amount. The unit is in the `currency` column, which comes from the campaign's funding instrument. **Dates follow the ad account's timezone.** The `date` column is a day in whatever timezone the ad account is set to, not UTC. Comparing it to a UTC timestamp from another table shifts the boundary by the account's offset. ## Troubleshooting **"Your X Ads connection is no longer valid"**: the authorization was revoked, or you changed your X password. Reconnect your X account from the source settings, then re-enable the sync. **"The connected X account cannot access this ad account"**: the X user you connected lost permission on the ad account. Restore it in X Ads Manager, or reconnect with a user who has it. **"The connected X Ads integration is missing or disconnected"**: the stored connection was deleted. Reconnect your X account. **"The PostHog X Ads app is not configured"**: this is on our side, not yours. [Contact support](https://us.posthog.com/#panel=support%3Asupport%3Adata_warehouse%3A%3Atrue). **Stats rows missing for recent days**: X publishes a day's figures after that day closes in the ad account's timezone, so the most recent day is often incomplete until the next sync. If your sync is failing or data looks wrong, see the [Data warehouse troubleshooting guide](https://posthog.com/docs/data-warehouse/troubleshooting.md). If that doesn't help, [contact support](https://us.posthog.com/#panel=support%3Asupport%3Adata_warehouse%3A%3Atrue) – we're happy to help.