# Pixels and tracking tags

Create, install and verify each ad network's pixel or site tag through one API, and set the click-URL parameters (UTMs) that ads carry to your site.

A pixel is the small script an ad network runs on your site to see what people do after they click: page views, add-to-carts, purchases. Conversion campaigns, website audiences and server-side conversions all point at one. Pozy manages every network's pixel through the same endpoints under `/v1/accounts/{accountId}/tracking-tags`, and installs it on a connected Shopify, WordPress, Wix or Webflow site with one call, without editing a theme.

This page also covers the other kind of "tracking tag": the parameters an ad adds to its click URL, such as `utm_source`. Those are set per ad. Jump to [Click-URL parameters](#click-url-parameters).

## What each network calls it

`accountId` in the path is always the **ads connection** (`metaads`, `tiktokads`, `googleads`, `linkedinads`, `pinterestads`, `xads`, `openaiads`), never a posting account.

| Network | Name | `kind` | `tagId` | `siteTagId` (what the site loads) |
| --- | --- | --- | --- | --- |
| Meta | Meta Pixel | `pixel` | Numeric pixel id | Same as `tagId` |
| TikTok | TikTok Pixel | `pixel` | Numeric pixel id | Alphanumeric pixel code |
| Google Ads | Google tag | `tag` | The customer id | `AW-...` |
| LinkedIn | Insight Tag | `insight_tag` | Partner id | Same as `tagId` |
| Pinterest | Pinterest tag | `tag` | Numeric tag id | Same as `tagId` |
| X | X Pixel | `pixel` | The X ad account id | The pixel id |
| ChatGPT Ads | Pixel | `pixel` | `cds_...` | The pixel's site id |

Reddit pixels are created in Reddit Ads Manager and are not managed here. Reddit's server-side events still go through [Conversions](https://pozy.ai/docs/ads/conversions).

## Set up a pixel end to end

### Look for one that already exists

Most ad accounts already have a pixel, and most creates cannot be undone. List first.

```bash cURL
curl "https://api.pozy.ai/v1/accounts/66b2e19d8c3f5a7e9d0b1c2d/tracking-tags?adAccountId=act_1234567890" \
  -H "Authorization: Bearer $POZY_API_KEY"
```
```ts Node
const res = await fetch("https://api.pozy.ai/v1/accounts/66b2e19d8c3f5a7e9d0b1c2d/tracking-tags?adAccountId=act_1234567890", {
  headers: { Authorization: `Bearer ${process.env.POZY_API_KEY}` },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
console.log(data.tags);
```
```python Python
import os, requests

resp = requests.get(
    "https://api.pozy.ai/v1/accounts/66b2e19d8c3f5a7e9d0b1c2d/tracking-tags",
    headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
    params={"adAccountId": "act_1234567890"},
)
print(resp.json())
```

`GET .../tracking-tags/{tagId}` returns one tag with its install `code` and its conversion `events`. `status` is `active` or `inactive`.

### Create one if you need to

`POST /v1/accounts/{accountId}/tracking-tags` takes `adAccountId` and `name`.

```bash
curl -X POST https://api.pozy.ai/v1/accounts/66b2e19d8c3f5a7e9d0b1c2d/tracking-tags \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "adAccountId": "act_1234567890", "name": "Storefront pixel" }'
```

Two optional fields are network-specific: `defaultEventType` on ChatGPT Ads (creates the conversion event a conversions campaign needs) and `automaticMatchingFields` on Pinterest (enhanced match, only settable at creation). Other networks return `400` if you send them.

> **Creates are not reversible:**
> 
> No network lets you delete a pixel, and on Meta, TikTok, Pinterest and ChatGPT Ads every create makes a new one. Never retry a create automatically. After a timeout or a `502`, list the tags and check before sending it again.

### Install it on your site

`POST .../tracking-tags/{tagId}/install` with the `storeAccountId` of a connected store or site:

```bash
curl -X POST https://api.pozy.ai/v1/accounts/66b2e19d8c3f5a7e9d0b1c2d/tracking-tags/1729525464415281/install \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "storeAccountId": "66c3d4e5f6a7b8c9d0e1f2a3" }'
```

The response says whether it is `installed`, with `siteTagId` and every Pozy-installed tag on that site in `tags`. Installs are idempotent, so repeating one changes nothing. `GET` on the same path reads the status and `DELETE` removes it, both with `?storeAccountId=`.

### Check it fires

Open your site, then read `GET .../tracking-tags/{tagId}`. Where the network reports it, `lastFiredTime` (Unix seconds) and `installed` turn on after the first event. For more detail, `GET .../tracking-tags/{tagId}/stats` returns the network's own numbers (see the table below), and Meta also offers `GET .../diagnostics` with a list of health checks and links to fix them.

## How installs work per site

| Site | How the tag goes on |
| --- | --- |
| [Shopify](https://pozy.ai/docs/platforms/shopify) | One Pozy web pixel per store fires each network's tag from Shopify's customer events (page view, product view, add to cart, checkout, payment info, purchase). Browser events carry an event id so server-side conversions can be matched. |
| [WordPress](https://pozy.ai/docs/platforms/wordpress) | One Custom HTML widget per tag, read back to confirm the script survived. `verifyHomepage` (default `true`) checks the home page. If the site cannot run scripts, the call returns `422 tracking_tag_install_blocked`. |
| [Wix](https://pozy.ai/docs/platforms/wix) | One script per site loads each tag's base code. |
| [Webflow](https://pozy.ai/docs/platforms/webflow) | One script in the site's custom code, live after the next publish. `publishRequired: true` in the response means you still need to publish. |

On WordPress, Wix and Webflow the tag loads its base code and reports page views. Shopify maps store events to each network's conversion events, so create those events first (next section).

## Conversion events

A tag's conversion events are what a site fires and what campaigns optimize for. Manage them on `.../tracking-tags/{tagId}/events`: list with `GET`, create with `POST`, change with `PATCH .../events/{eventId}`, retire with `DELETE`.

The body takes a `name` and either the network's own `type` or a neutral `siteEvent`. `siteEvent` is the store vocabulary the installs use: `page_view`, `view_content`, `add_to_cart`, `search`, `initiate_checkout`, `add_payment_info`, `purchase`. Networks that store more also take `defaultValue` and `currency`, `clickWindowDays` and `viewWindowDays`, `urlContains` and `enabled`.

```bash
curl -X POST https://api.pozy.ai/v1/accounts/66b2e19d8c3f5a7e9d0b1c2d/tracking-tags/7412345678901234567/events \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Purchase", "siteEvent": "purchase" }'
```

| Network | What an event is | Create | Update | Delete |
| --- | --- | --- | --- | --- |
| Meta | A custom conversion | Idempotent by name | Name, value | Archives |
| TikTok | A pixel event | Not idempotent; may come back `pending` with no id for a few hours | Name, value, currency | Not available |
| Google Ads | A website conversion action | Duplicate names refused | Yes, including windows, value, counting and primary | Archives; `enabled: true` restores |
| LinkedIn | A conversion rule | Not idempotent | Yes | Disables; `enabled: true` turns it back on |
| Pinterest | An advertiser-defined event (up to 15 per ad account) | Keyed by name | Type only | Stops tracking |
| X | A web event | Not idempotent | Name, type, windows | Permanent |
| ChatGPT Ads | An event setting (fixed 30-day window) | Not idempotent | Not available | Not available |

## What each network supports

| Operation | Meta | TikTok | Google | LinkedIn | Pinterest | X | ChatGPT |
| --- | --- | --- | --- | --- | --- | --- | --- |
| Create a tag | Not idempotent | Not idempotent | Idempotent (turns on conversion tracking) | Idempotent, one per ad account | Not idempotent | One per ad account (`409`) | One per connection (`409`) |
| Update the tag | Name, advanced matching, first-party cookies, data use | Name, automatic matching, first-party cookies | `autoTagging` only | First-party tracking only | No | First-party cookies only | No |
| `lastFiredTime`, `installed` | Yes | No | No | Yes | Yes | Yes | No |
| Stats | Aggregated counts, many `aggregation` options | Per event, 7 days by default, 30 at most | Per conversion action, 30 days by default | Last fire per domain and rule | Last fired, Event Quality Score | Last tracked per event, no counts | Events from the last 15 minutes |
| Diagnostics | Yes | No | No | No | No | No | No |
| Share with other ad accounts | Business Manager pixels | Business Center pixels | No | `USE_ONLY` access | No | No | No |
| Users and partner businesses | Yes | No | No | No | No | No | No |
| Delete the tag | No | No | No | No | No | No | No |

An operation a network does not offer returns `501 platform_not_supported`, and the message names where to do it instead (usually the network's own ads manager).

`PATCH .../tracking-tags/{tagId}` takes any of `name`, `enableAutomaticMatching`, `automaticMatchingFields`, `firstPartyCookieStatus`, `enableFirstPartyCookies`, `dataUseSetting` and `autoTagging`. A field the network does not store returns `400` listing the ones it does.

### Sharing

`.../tracking-tags/{tagId}/shared-accounts` lists (`GET`), adds (`POST` with `adAccountId`) and removes (`DELETE ?adAccountId=`) the other ad accounts that can use a tag. On Meta the pixel must belong to a Business Manager; on TikTok it must be a Business Center asset; on LinkedIn the grant is use-only and the tag cannot be left with no ad account.

### Users and partners (Meta)

`GET` and `POST .../tracking-tags/{tagId}/users` list people with access and assign one with `{ userId, tasks }`. Tasks are `AA_ANALYZE`, `ADVERTISE`, `ANALYZE`, `EDIT` and `UPLOAD`. `GET .../partners` lists partner businesses, read only.

## Network notes

- **Meta.** A new pixel accepts server events right away, even before the script is on the site. A pixel created on an ad account outside a Business Manager cannot be shared.
- **TikTok.** Names are up to 40 characters with no emoji. A connection made before TikTok's pixel permission answers every call with `403`; reconnect TikTok Ads.
- **Google Ads.** Each customer has exactly one Google tag. The "create" call turns conversion tracking on and returns the existing tag if it is already on. Its events are the account's website conversion actions.
- **LinkedIn.** One Insight Tag per ad account. It has no name, so Pozy shows it as `Insight Tag <id>`.
- **Pinterest.** The connected user needs a Business Access role on the ad account. Stats take no time range.
- **X.** One pixel per ad account. Events X creates with the pixel are hidden and cannot be changed. See [X Ads](https://pozy.ai/docs/ads/x-ads#x-pixel).
- **ChatGPT Ads.** Creating the pixel also creates its Conversions API key. Some ad accounts need pixel management enabled by their ChatGPT Ads representative (`422 feature_not_available`). See [ChatGPT Ads](https://pozy.ai/docs/ads/chatgpt-ads#the-pixel).

## Click-URL parameters

Click-URL parameters are added to the landing page URL when someone clicks an ad, so your analytics can tell where the visit came from. They live on each ad, not on the pixel.

### Set them at launch

`tracking.urlTags` on `POST /v1/ads/create` and `POST /v1/ads/boost` takes `{ key, value }` pairs. `tracking.pixelId` sets the pixel the ad measures with.

```json
{
  "tracking": {
    "pixelId": "1729525464415281",
    "urlTags": [
      { "key": "utm_source", "value": "meta" },
      { "key": "utm_campaign", "value": "{{campaign.id}}" },
      { "key": "utm_content", "value": "{{ad.id}}" }
    ]
  }
}
```

Network macros pass through unescaped so the network expands them at click time; everything else is percent-encoded. Meta's macros look like `{{ad.id}}` and `{{placement}}`. ChatGPT Ads uses `{campaign_id}`, `{ad_group_id}`, `{ad_id}` and `{oppref}`.

> **Note:**
> 
> Tracking sits on the ad, not the ad set, so it is not inherited. When you add a new ad to an existing ad set, send `tracking` again.

### Read or change them later

`GET /v1/ads/{adId}/tracking-tags` reads what an ad carries and `PATCH` changes it. What you can set depends on the network:

| Network | Fields | Level | Notes |
| --- | --- | --- | --- |
| Meta | `urlTags` | Creative | Meta creatives cannot be edited, so Pozy rebuilds the creative with the same copy and image and points the ad at it. The new creative goes through review again. For creatives Pozy cannot copy, also send `creative` with `headline`, `body`, `callToAction`, `linkUrl` and `imageUrl`. |
| Google Ads | `trackingUrlTemplate` (must contain `{lpurl}`), `finalUrlSuffix` | Campaign | Pass any ad in the campaign. An empty string clears a field. |
| LinkedIn | `dynamicValueParameters` (keys mapped to `CAMPAIGN_ID`, `CAMPAIGN_NAME`, `CAMPAIGN_GROUP_ID`, `CAMPAIGN_GROUP_NAME`, `CREATIVE_ID`, `ACCOUNT_ID`, `ACCOUNT_NAME`), `customValueParameters` (static values) | Campaign | Applies to every creative without review. Conversation, message and lead form ads do not support it. |
| TikTok, X, Pinterest | None | | Returns `405`. Put parameters in `linkUrl` instead. |

```bash
curl -X PATCH https://api.pozy.ai/v1/ads/66f0a1b2c3d4e5f6a7b8c9d0/tracking-tags \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "finalUrlSuffix": "utm_source=google&utm_medium=cpc&utm_campaign={campaignid}" }'
```

## Errors you will see

| Status and code | Cause | Fix |
| --- | --- | --- |
| `400` naming `accountId` | A posting account, or a network without tags (Reddit) | Use the ads connection's `accountId`. |
| `400` listing supported fields | A field this network does not store | Send only the fields the message lists. |
| `403 insufficient_permissions` | The network refused the token, or the connection predates a permission | Reconnect the ads connection. |
| `404 tracking_tag_not_found` | The tag is not on this connection or ad account | List the tags for a current id. |
| `409` | One tag per account already exists (X, ChatGPT Ads), or the store needs attention | Use the existing tag, or reconnect the store. |
| `422 tracking_tag_install_blocked` | WordPress cannot run the script | Check the site's script and widget settings. |
| `501 platform_not_supported` | The network has no such operation | Use the network's own ads manager, as the message says. |
| `405` on ad tracking tags | The network has no click-URL parameters | Put them in the ad's link. |

## Endpoints

| Endpoint | What it does |
| --- | --- |
| [`GET /v1/ads/{adId}/tracking-tags`](https://pozy.ai/docs/api/getAdTrackingTags) | Get ad tracking tags |
| [`PATCH /v1/ads/{adId}/tracking-tags`](https://pozy.ai/docs/api/updateAdTrackingTags) | Set ad tracking tags |
| [`GET /v1/accounts/{accountId}/tracking-tags`](https://pozy.ai/docs/api/listTrackingTags) | List tracking tags |
| [`POST /v1/accounts/{accountId}/tracking-tags`](https://pozy.ai/docs/api/createTrackingTag) | Create a tracking tag |
| [`GET /v1/accounts/{accountId}/tracking-tags/{tagId}`](https://pozy.ai/docs/api/getTrackingTag) | Get a tracking tag |
| [`PATCH /v1/accounts/{accountId}/tracking-tags/{tagId}`](https://pozy.ai/docs/api/updateTrackingTag) | Update a tracking tag |
| [`GET /v1/accounts/{accountId}/tracking-tags/{tagId}/shared-accounts`](https://pozy.ai/docs/api/listTrackingTagSharedAccounts) | List accounts it is shared with |
| [`POST /v1/accounts/{accountId}/tracking-tags/{tagId}/shared-accounts`](https://pozy.ai/docs/api/addTrackingTagSharedAccount) | Share with an ad account |
| [`DELETE /v1/accounts/{accountId}/tracking-tags/{tagId}/shared-accounts`](https://pozy.ai/docs/api/removeTrackingTagSharedAccount) | Stop sharing with an account |
| [`GET /v1/accounts/{accountId}/tracking-tags/{tagId}/install`](https://pozy.ai/docs/api/getTrackingTagStoreInstall) | Get store install status |
| [`POST /v1/accounts/{accountId}/tracking-tags/{tagId}/install`](https://pozy.ai/docs/api/installTrackingTagOnStore) | Install on a Shopify, WordPress, Wix or Webflow site |
| [`DELETE /v1/accounts/{accountId}/tracking-tags/{tagId}/install`](https://pozy.ai/docs/api/removeTrackingTagFromStore) | Remove from a Shopify, WordPress, Wix or Webflow site |
| [`GET /v1/accounts/{accountId}/tracking-tags/{tagId}/events`](https://pozy.ai/docs/api/listTrackingTagEvents) | List conversion events |
| [`POST /v1/accounts/{accountId}/tracking-tags/{tagId}/events`](https://pozy.ai/docs/api/createTrackingTagEvent) | Create a conversion event |
| [`PATCH /v1/accounts/{accountId}/tracking-tags/{tagId}/events/{eventId}`](https://pozy.ai/docs/api/updateTrackingTagEvent) | Update a conversion event |
| [`DELETE /v1/accounts/{accountId}/tracking-tags/{tagId}/events/{eventId}`](https://pozy.ai/docs/api/deleteTrackingTagEvent) | Delete a conversion event |
| [`GET /v1/accounts/{accountId}/tracking-tags/{tagId}/users`](https://pozy.ai/docs/api/listTrackingTagUsers) | List tag users |
| [`POST /v1/accounts/{accountId}/tracking-tags/{tagId}/users`](https://pozy.ai/docs/api/assignTrackingTagUser) | Assign a user to a tag |
| [`DELETE /v1/accounts/{accountId}/tracking-tags/{tagId}/users/{userId}`](https://pozy.ai/docs/api/removeTrackingTagUser) | Remove a user from a tag |
| [`GET /v1/accounts/{accountId}/tracking-tags/{tagId}/partners`](https://pozy.ai/docs/api/listTrackingTagPartners) | List partner businesses of a tag |
| [`GET /v1/accounts/{accountId}/tracking-tags/{tagId}/diagnostics`](https://pozy.ai/docs/api/getTrackingTagDiagnostics) | Get tag diagnostics |
| [`GET /v1/accounts/{accountId}/tracking-tags/{tagId}/stats`](https://pozy.ai/docs/api/getTrackingTagStats) | Get aggregated event stats |
