Ads
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.
#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-... |
| Insight Tag | insight_tag |
Partner id | Same as tagId |
|
| 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.
#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.
curl "https://api.pozy.ai/v1/accounts/66b2e19d8c3f5a7e9d0b1c2d/tracking-tags?adAccountId=act_1234567890" \
-H "Authorization: Bearer $POZY_API_KEY"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);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.
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.
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:
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 | 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 | 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 | One script per site loads each tag's base code. |
| 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.
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 |
| A conversion rule | Not idempotent | Yes | Disables; enabled: true turns it back on |
|
| 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 | 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.
- 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.
#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.
{
"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}.
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. |
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. |
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. |