Pixels and Tracking Tags
One API for every ad platform's pixel or site tag: list, create, update, read stats, share, manage users and conversion events, and install on Shopify or WordPress.
A tracking tag is the measurement tag an ad platform gives you to put on a website: the Meta Pixel, the TikTok Pixel, the Google tag, the LinkedIn Insight Tag, the Pinterest tag, the X Pixel or the OpenAI Ads pixel. It is what a site fires events to, what server-side conversions deduplicate against, and what conversion campaigns and website audiences point at. Zernio exposes every platform's tag under one platform-neutral API at /v1/accounts/{accountId}/tracking-tags, and can install any of them on a connected Shopify store or WordPress site without theme edits.
These are not the click-URL parameters an ad appends to its link (utm_*, {{ad.id}}), which several platforms also call "tracking tags". Those live on each ad: see the URL tracking tags pages for Meta, Google and LinkedIn.
The id model
| Field | What it is |
|---|---|
accountId (path) | The ads connection: the SocialAccount the Ads connect flow created (metaads, tiktokads, googleads, linkedinads, pinterestads, xads, openaiads), never a posting account. The platform of that account picks the adapter. |
tagId (path) / tag.id | The id the platform's API uses for the tag. Pass it to every per-tag call. |
tag.siteTagId | The id the on-site code carries. It differs from id on TikTok, Google, X and OpenAI (table below). Store installs and the tag's code use this one. |
adAccountId | The ad account inside the connection. Required on create. On per-tag calls it is an optional scope for platforms whose tags or events live inside an ad account (TikTok advertisers, LinkedIn conversion rules); the others ignore it. |
event.id / event.siteEventId | A conversion event's API id, and the per-event id the site sends to fire it (Google conversion label, LinkedIn conversion rule id, TikTok ttq.track() event name, Pinterest custom event name, X tw- event id, OpenAI event name). |
| Platform | accountId platform | tagId | siteTagId | adAccountId |
|---|---|---|---|---|
| Meta | metaads | Pixel id (numeric) | Same as tagId | act_<n> |
| TikTok | tiktokads | pixel_id (numeric) | pixel_code (for example D00IKHRC77UE0J0RTNHG) | Advertiser id; without it the pixel is searched across the connection's advertisers |
googleads | Customer id (10 digits, dashes allowed) | AW-<conversion tracking id> (the manager's under cross-account tracking) | The customer id | |
linkedinads | Insight Tag partner id (numeric) | Same as tagId | Ad account id, numeric or urn:li:sponsoredAccount:<id>; defaults to the tag's owner | |
pinterestads | Tag id (numeric) | Same as tagId | Ad account id; without it the tag's owner is found across the readable ad accounts | |
| X | xads | Ad account id (one X Pixel per ad account) | The pixel id (the ad account's universal website tag) | The ad account id |
| OpenAI | openaiads | cds_... API id (the pixel_id is accepted too) | pixel_id (also returned as pixelId) | Ignored: one API key is one ad account |
Capability matrix
Yes means the platform's API allows it and Zernio implements it. No means the platform's API cannot do it: the next table gives the evidence. 501 means Zernio does not expose it for that platform. Both No and 501 answer 501 platform_not_supported, and the message says why and what to do instead, so you can surface it to a user as is.
| Operation | Meta | TikTok | X | OpenAI | |||
|---|---|---|---|---|---|---|---|
| List tags | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
Get tag (with code) | Yes | Yes (with events) | Yes (with events) | Yes (with events) | Yes | Yes (with events) | Yes (with events) |
| Create tag | Yes, not idempotent | Yes, not idempotent | Yes, idempotent (switches conversion tracking on) | Yes, idempotent (returns the account's tag) | Yes, not idempotent (enhanced match optional) | Yes, one per ad account (409 when it exists) | Yes, one per account, not idempotent |
| Update tag | Yes | Yes | autoTagging only | First-party tracking only | No | First-party cookies only | No |
| Delete tag | No | No | No | No | No | No | No |
| Stats | Aggregated counts | Per event (7 days default, 30 max) | Per conversion action (30 days default) | Last callback per domain and rule | Last fired, seen events, Event Quality Score | Status and last tracked time (no counts) | Last 15 minutes only |
| Diagnostics | Yes (Events Manager checks) | No | No | 501 | 501 | 501 | 501 |
| Share with ad accounts | Yes (Business Manager pixels) | Yes (Business Center pixels) | No | Yes (USE_ONLY) | No | No | No |
| List and assign users | Yes (Business Manager pixels) | 501 | 501 | 501 | 501 | 501 | 501 |
| Remove a user | No | 501 | 501 | 501 | 501 | 501 | 501 |
| List partners | Yes (read only) | 501 | 501 | 501 | 501 | 501 | 501 |
| List events | Yes (custom conversions) | Yes (pixel events) | Yes (conversion actions, archived included) | Yes (conversion rules) | Yes (advertiser defined events) | Yes (web event tags) | Yes (event settings) |
| Create event | Yes, idempotent by name | Yes, not idempotent | Yes (duplicate names refused) | Yes, not idempotent | Yes (keyed by name) | Yes, not idempotent | Yes, not idempotent |
| Update event | Name and default value | Name, value and currency | Yes | Yes | Type only | Name, type and windows | No |
| Delete event | Archives | Deletes | Archives (restorable) | Disables the rule | Stops tracking | Deletes | No |
| Install on Shopify | Yes | Yes | Mapped conversions only | Page views plus mapped rules | Yes | Page views plus mapped events | Yes |
| Install on WordPress | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
No platform's API deletes a tag, so there is no DELETE on a tag. Retire a tag by uninstalling it from the site.
What the platforms cannot do
| Platform | Operation | Evidence |
|---|---|---|
| Meta | Delete tag | AdsPixel has no delete in Meta's Business SDK, and DELETE /{pixel} answers "Unsupported delete request" (code 100, subcode 33). |
| Meta | Remove a user | The SDK has no delete on the pixel's /assigned_users; DELETE answers code 100, subcode 33, and re-assigning with no tasks answers "The parameter tasks is required". Remove the user in Business Settings, Data sources, Pixels. |
| Meta | Share with a partner business | POST /{pixel}/agencies answers "(#3) Application does not have the capability to make this API call"; existing partners are readable. |
| TikTok | Delete tag | Absent from TikTok's pixel endpoint index and SDK specs; POST /pixel/delete/ answers HTTP 404. |
| TikTok | Diagnostics | No endpoint in the index or SDK specs; /pixel/diagnostic/, /pixel/diagnostics/ and /pixel/health/ answer HTTP 404. Use the stats or Events Manager. |
| TikTok | Change an event's type | /pixel/event/update/ takes only the name, currency and value. Delete the event and create a new one. |
| Delete tag | The tag is the customer's own AW- id (conversion_tracking_id, output only in API v25). | |
| Share (cross-account conversion tracking) | Writing google_ads_conversion_customer answers 403 SERVICE_ACCESS_DENIED: Google allowlists it. Set it up from the manager account in the Google Ads UI. | |
Tag settings other than autoTagging | Every other conversion_tracking_setting write answers SERVICE_ACCESS_DENIED, and enhanced conversions for leads is output only. | |
| Diagnostics | ConversionAction (v25) has no tag status or last-fired field, and there is no test-event service. Use the stats or Google Tag Assistant. | |
| Disable an event without removing it | Status HIDDEN answers 400 BLOCKED_VALUE on WEBPAGE actions, so enabled: false removes (archives) the action and enabled: true restores it. | |
| Delete tag | LinkedIn has no delete for Insight Tags; an ad account holds one. | |
| Delete event | No delete for conversions in LinkedIn's docs, and DELETE /rest/conversions/{id} answers 404 "No virtual resource found". Delete disables the rule. | |
| Update tag | API v5 lists only POST and GET on conversion tags; PATCH and PUT on /conversion_tags/{id} answer 405. Enhanced match can be set on create. | |
| Delete tag | DELETE /conversion_tags/{id} answers 405. | |
| Share | A tag belongs to one ad account and v5 has no sharing endpoint. Use Pinterest Business Access. | |
| Rename an event | Pinterest keys advertiser defined events by name. Create the new name and delete the old one. | |
| X | Delete tag | DELETE /12/accounts/:id/website_tags/:id answers 405 METHOD_NOT_ALLOWED; X's reference and Ads SDKs have no delete. |
| X | Share | A website tag belongs to one ad account; no share endpoint exists, and /pixels, /event_sources and /universal_website_tags answer 404 ROUTE_NOT_FOUND. |
| X | Event counts | None per pixel or event in X's reference, only campaign WEB_CONVERSION metrics. |
| X | Edit an auto-created event | The events X creates with the pixel share its id, so a site cannot fire them one by one. They are left out of the list, and changing or deleting one answers 400. |
| OpenAI | Update or delete tag | The conversion-setup API documents only list and create; POST, PATCH, PUT and DELETE on /v1/conversions/pixels/{id} answer 405. |
| OpenAI | Share | A pixel belongs to one ad account. |
| OpenAI | Update or delete event | /v1/conversions/event_settings/{id} answers 404 "Invalid URL" to updates, DELETE and .../archive. Archive the event in OpenAI Ads Manager. |
An operation a platform lacks answers:
{
"error": "Updating a tracking tag is not supported for platform \"pinterestads\". Pinterest API v5 has no endpoint to edit a tag: its spec lists only POST and GET on /conversion_tags and GET on /conversion_tags/{id}, and PATCH or PUT on /conversion_tags/{id} answer 405 \"Method not allowed\" (code 5). Rename a tag or change its enhanced match settings in Pinterest Ads Manager (Conversions); enhanced match can be set when creating it.",
"type": "invalid_request_error",
"code": "platform_not_supported",
"platform": "pinterestads"
}List and read tags
GET /v1/accounts/{accountId}/tracking-tags lists every tag the connection can see, and ?adAccountId= scopes it to one ad account. The list view leaves out code and events; GET .../tracking-tags/{tagId} returns them.
import Zernio from '@zernio/node';
const zernio = new Zernio();
const { data } = await zernio.trackingtags.getTrackingTag({
path: { accountId: '66b2e19d8c3f5a7e9d0b1c2d', tagId: '7412345678901234567' },
query: { adAccountId: '7301234567890123456' }
});Response (200), a TikTok Pixel:
{
"platform": "tiktokads",
"tag": {
"id": "7412345678901234567",
"siteTagId": "D00IKHRC77UE0J0RTNHG",
"name": "Storefront pixel",
"platform": "tiktokads",
"kind": "pixel",
"status": "active",
"creationTime": 1804064400,
"ownerAdAccountId": "7301234567890123456",
"code": "<script>!function (w, d, t) {...}</script>",
"events": [
{ "id": "1804567", "name": "Purchase", "type": "SHOPPING", "siteEvent": "purchase" }
]
}
}kind is pixel on Meta, TikTok, X and OpenAI, tag on Google and Pinterest and insight_tag on LinkedIn. status is active or inactive, lastFiredTime (Unix seconds, null when it never fired) and installed appear where the platform reports a last fire (Meta, LinkedIn, Pinterest, X), and ownerBusinessId is Meta's Business Manager owner.
Create and update
POST /v1/accounts/{accountId}/tracking-tags takes adAccountId and name, plus defaultEventType on OpenAI and automaticMatchingFields (enhanced match at creation) on Pinterest; other platforms answer 400 to automaticMatchingFields on a create. It answers 201 with the tag.
Creating a tag is not idempotent on Meta, TikTok, Pinterest and OpenAI: every call makes another tag, and none of them can be deleted afterwards. LinkedIn and Google are the exceptions: a LinkedIn ad account holds one Insight Tag and a create returns it, and a Google create returns the account's tag, creating a first conversion action only when the account does not track conversions yet. X allows one pixel per ad account and answers 409 without writing when the account already has one. Never auto-retry a create. After a timeout or a 502, list the tags before sending it again.
PATCH /v1/accounts/{accountId}/tracking-tags/{tagId} takes at least one of name, enableAutomaticMatching, automaticMatchingFields, firstPartyCookieStatus, enableFirstPartyCookies, dataUseSetting and autoTagging. Each platform accepts a subset and answers 400 naming the supported fields for the rest. Pinterest and OpenAI have no update at all (501).
| Field | Meta | TikTok | X | ||
|---|---|---|---|---|---|
name | Yes | Yes (40 characters, no emojis) | No | No (tags have no name) | No (pixels have no name) |
enableAutomaticMatching | Advanced Matching | Automatic advanced matching | No | No | No |
automaticMatchingFields | Meta codes (em, ph, fn, ...) | Folded onto TikTok's email, phone, name, address and external id switches | No | No | No |
firstPartyCookieStatus | Yes | First-party cookies on or off (empty answers 400) | No | First-party tracking on or off (empty answers 400) | The pixel's allow_1p_cookie (empty answers 400) |
enableFirstPartyCookies | No | First-party cookies as a boolean | No | No | No |
dataUseSetting | Yes | No | No | No | No |
autoTagging | No | No | gclid auto-tagging on or off | No | No |
Stats
GET /v1/accounts/{accountId}/tracking-tags/{tagId}/stats takes optional startTime and endTime (Unix seconds) and returns { platform, stats: { startTime, endTime, rows } }. Rows are the platform's own data, so read the keys you get:
| Platform | Rows | Window |
|---|---|---|
| Meta | AdsPixelStatsResult for aggregation (default event; Meta only) | Meta's default when omitted |
| TikTok | Per-event counts from /pixel/event/stats | 7 days by default, at most 30, whole UTC days |
conversionActionId, conversionActionName, category, allConversions, allConversionsValue per conversion action | Last 30 days by default, in the account's time zone | |
One row per domain (domainName, lastFiredTime, blocked) and per conversion rule (status, lastFiredTime); no counts | None | |
The tag's lastFiredTime, status and enhancedMatchStatus, the events seen on it, and the ad account's Event Quality Score (1 and 14 days) | None: startTime/endTime answer 400 | |
| X | Each web event tag's status, lastTrackedTime and eventId (X has no counts) | None |
| OpenAI | The latest Pixel events received (at most 50) | Fixed to the last 15 minutes; startTime/endTime answer 400 |
Diagnostics
GET /v1/accounts/{accountId}/tracking-tags/{tagId}/diagnostics returns the platform's health checks for the tag as checks[] (key, title, description, result, actionUrl). Only Meta has them (Meta diagnostics); the others answer 501. TikTok and Google have no diagnostics API at all (evidence): read their stats instead.
Share with other ad accounts
GET, POST and DELETE on .../tracking-tags/{tagId}/shared-accounts list, add and remove the ad accounts a tag is shared with. The body of POST and the ?adAccountId= of DELETE name the ad account to share with or unshare from.
| Platform | How it shares | Condition |
|---|---|---|
| Meta | Shared ad accounts of the pixel | The pixel has a Business Manager owner (ownerBusinessId not null) |
| TikTok | Business Center pixel links; sharedAccounts[].businessId is the Business Center | The pixel is an asset of a Business Center the connection manages; otherwise 400 asking to transfer it into Business Center first |
USE_ONLY access for another ad account | The target account does not already use another Insight Tag, and the tag keeps at least one account |
Google, Pinterest, X and OpenAI cannot share a tag (evidence).
Conversion events
.../tracking-tags/{tagId}/events manages a tag's conversion events on every platform. What an event is, and what a delete does, follows the platform:
| Platform | Event object | siteEventId | Fields on create and update | DELETE state |
|---|---|---|---|---|
| Meta | Custom conversion (lives on the ad account, filtered by pixel) | None | name, type, siteEvent, defaultValue, urlContains | archived |
| TikTok | Pixel event | The ttq.track() name (Purchase, AddToCart, ...) | name, type, siteEvent, defaultValue, currency (USD, JPY or INR); the type is fixed after create | deleted |
WEBPAGE conversion action | The conversion label | name, type, siteEvent, enabled, defaultValue, currency, alwaysUseDefaultValue, clickWindowDays, viewWindowDays, primary, countingType | archived (enabled: true restores) | |
| Conversion rule | The rule id (Insight Tag rules only) | name, type, siteEvent, enabled, defaultValue, currency, clickWindowDays, viewWindowDays | disabled | |
| Advertiser defined event (a custom event name mapped to a standard type, shared by every tag of the ad account) | The event name | name and type or siteEvent; only the type changes on update | disabled (Pinterest stops tracking it) | |
| X | Web event tag (auto-created ones are left out) | tw-<pixel>-<event> | name, type, siteEvent, clickWindowDays, viewWindowDays | deleted |
| OpenAI | Event setting | The event name | name, type, siteEvent; no update or delete | None |
| Method | Path | Operation |
|---|---|---|
GET | .../tracking-tags/{tagId}/events | List conversion events |
POST | .../tracking-tags/{tagId}/events | Create a conversion event (201) |
PATCH | .../tracking-tags/{tagId}/events/{eventId} | Update a conversion event |
DELETE | .../tracking-tags/{tagId}/events/{eventId} | Delete a conversion event |
The body takes name (required on create), the platform's own event type in type or a neutral siteEvent, and the optional fields in the table. A field the platform does not store answers 400 naming the ones it does. DELETE answers { platform, eventId, state }. Creating an event is not idempotent on TikTok, LinkedIn, X and OpenAI: never retry it blindly.
A few platform rules:
- TikTok returns no id on create and lists new events only every few hours, so a create can come back with
id: ""andstatus: "pending"; list the events later to get the id. TikTok refuses to delete an event an ad group still uses. - Pinterest allows 15 custom events per ad account, names of 1 to 100 letters, digits,
_or-, compared case-insensitively. The eventidis its name, so an event cannot be renamed: create the new name and delete the old one. The events belong to the ad account, so every tag of that account lists the same ones. A site fires one withpintrk('track', '<name>'), andPOST /v1/ads/conversionsmatches it byeventName. - X click windows are 1, 7, 14, 30, 60 or 90 days and view windows 0, 1, 7, 14, 30, 60 or 90 days, no longer than the click window. A create defaults to 30 and 1 days with retargeting on, as Events Manager does.
siteEvent is Zernio's neutral storefront vocabulary, the same one the store installs speak: page_view, view_content, add_to_cart, search, initiate_checkout, add_payment_info and purchase.
const { data } = await zernio.trackingtags.createTrackingTagEvent({
path: { accountId: '66b2e19d8c3f5a7e9d0b1c2f', tagId: 'cds_68e4f0a1b2c3d4e5f6a7b8c9' },
body: { name: 'Purchases', siteEvent: 'purchase' }
});Response (201), an OpenAI event setting:
{
"platform": "openaiads",
"event": {
"id": "ces_68e4f0c9d8e7",
"name": "Purchases",
"type": "order_created",
"siteEvent": "purchase",
"siteEventId": "order_created",
"status": "active",
"clickWindowDays": 30
}
}Users and partners
Meta pixels have people with access and partner businesses. Other platforms answer 501.
| Method | Path | Operation |
|---|---|---|
GET | .../tracking-tags/{tagId}/users | List the tag's users |
POST | .../tracking-tags/{tagId}/users | Assign a user with { userId, tasks }; assigning an assigned user replaces their tasks |
DELETE | .../tracking-tags/{tagId}/users/{userId} | Remove a user: 501 on Meta, which has no API for it |
GET | .../tracking-tags/{tagId}/partners | List partner businesses, read only |
See Meta pixel users and partners for the tasks and the permission it needs.
Install on a store
POST /v1/accounts/{accountId}/tracking-tags/{tagId}/install with a connected store's storeAccountId puts the tag on the site, GET with ?storeAccountId= reads the install status and DELETE with ?storeAccountId= removes it. All three are idempotent.
- Shopify: Zernio's web pixel app extension fires one tag per platform from a single Zernio web pixel per store, mapping Shopify's customer events to each platform's events. See Shopify tracking pixels for the event mapping per platform.
- WordPress: one marked Custom HTML widget per tag with the platform's base code, read back to confirm the script survived. See WordPress tracking tags.
The install responses carry tagPlatform and siteTagId (the tag you asked about), installed and tags, every Zernio tag on the store across platforms.
Common errors
| Error | Cause | Fix |
|---|---|---|
400 naming accountId | The account is a posting account, or a platform without tracking tags | Pass the ads connection's account id. |
400 naming the supported fields | A field this platform does not store was sent on update or on an event | Send only the fields the message lists. |
403 insufficient_permissions | The platform refused the token, or the connection predates a permission (TikTok Pixel Management, Meta business_management for pixel users) | Reconnect the ads account. |
404 tracking_tag_not_found | The tag is not on this connection, or not in adAccountId | List the tags to find a current id. |
400 on sharing | LinkedIn: the target ad account already uses another Insight Tag, or it is the tag's last account. TikTok: the pixel is not a Business Center asset | LinkedIn: revoke the other tag first, or share before revoking. TikTok: transfer the pixel into your Business Center. |
409 | X or OpenAI: the account already has a pixel. Shopify: the store must re-approve (reconnect_required), or fires a different tag of the platform on uninstall (invalid_resource_state) | See the platform page. |
422 feature_not_available | OpenAI: the ad account is not enabled for pixel management or the event stream | Ask the OpenAI partner representative. |
422 tracking_tag_install_blocked | WordPress cannot run the script | See the reasons. |
501 platform_not_supported | The platform's API has no such operation | The message names the alternative. |
Related
- Meta Pixels, TikTok Pixel, Google tag, LinkedIn Insight Tag, Pinterest tag, X Pixel and OpenAI pixel: each platform's ids, operations and limits.
- Send conversions: server-side events that deduplicate against the browser events.
- Pixels & Tracking Tags reference: every field and response.
Rate Limits
Requests per minute by connected account count, the per-second window on analytics, posting velocity caps, and how to handle a 429.
Commerce
One API over Shopify and WooCommerce stores: products, variants, inventory, collections, discounts, channels, markets, metafields, pages and menus, plus syncing a store into a Meta catalog.