Pixels and tags
Methods for Facebook pixels and Google tags: add a new one, change the token or conversion labels, archive and restore, attach to a PWA. For what pixels and tags do, see the "Metrics: pixels & tags" section of the guide.
| Method | Address | What it does |
|---|---|---|
| GET | /fb_pixels | List Facebook pixels |
| GET | /fb_pixels/{id} | One pixel |
| POST | /fb_pixels | Add a pixel |
| PUT | /fb_pixels/{id} | Update a pixel or restore it from archive |
| DELETE | /fb_pixels/{id} | Archive a pixel |
| GET | /google_tags | List Google tags |
| GET | /google_tags/{id} | One tag |
| POST | /google_tags | Add a tag |
| PUT | /google_tags/{id} | Update a tag or restore it from archive |
| DELETE | /google_tags/{id} | Archive a tag |
Replace YOUR_KEY and panel.example.com with your own.
List
GET/fb_pixels
GET/google_tags
Pixels and tags available to you, recently changed first. Archived ones are not shown.
| Parameter | What it filters |
|---|---|
search | Part of the pixel or tag number |
archived | 1 - show archived too |
team_id | Team |
page, per_page | Page and its size (100 by default) |
curl "https://panel.example.com/api/fb_pixels?search=1234" \
-H "Authorization: Bearer YOUR_KEY" \
-H "Accept: application/json"<?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$api = new GuzzleHttp\Client([
'base_uri' => 'https://panel.example.com/api/',
'headers' => ['Authorization' => 'Bearer YOUR_KEY', 'Accept' => 'application/json'],
'timeout' => 30,
]);
$response = $api->get('fb_pixels', ['query' => ['search' => '1234']]);
$pixels = json_decode($response->getBody(), true)['data']['fb_pixels'];
foreach ($pixels as $pixel) {
echo "{$pixel['id']}: pixel {$pixel['pixel_id']}" . PHP_EOL;
}# pip install requests
import requests
API = "https://panel.example.com/api"
HEADERS = {"Authorization": "Bearer YOUR_KEY", "Accept": "application/json"}
response = requests.get(f"{API}/fb_pixels", headers=HEADERS, params={"search": "1234"}, timeout=30)
pixels = response.json()["data"]["fb_pixels"]
for pixel in pixels:
print(pixel["id"], "pixel", pixel["pixel_id"])// Node.js 18+, no dependencies; save as script.mjs
const API = 'https://panel.example.com/api';
const headers = { Authorization: 'Bearer YOUR_KEY', Accept: 'application/json', 'Content-Type': 'application/json' };
const params = new URLSearchParams({ search: '1234' });
const response = await fetch(`${API}/fb_pixels?${params}`, {
method: 'GET',
headers,
signal: AbortSignal.timeout(30000),
});
const { data } = await response.json();
for (const pixel of data.fb_pixels) {
console.log(pixel.id, 'pixel', pixel.pixel_id);
}Response
{
"data": {
"meta": { "current_page": 1, "per_page": 100, "last_page": 1, "total": 1 },
"fb_pixels": [
{
"id": "WZAX",
"pixel_id": "1234567890123456",
"access_token": "EAAB...",
"archived": false,
"team": { "id": "WQby", "name": "Team A" },
"groups": [],
"created_at": "05 Oct 2026 14:23",
"updated_at": "05 Oct 2026 14:23"
}
]
}
}The tag list comes the same way, but under the google_tags key, and instead of pixel_id and access_token each tag has tag_id, install_label, registration_label and deposit_label.
Only the common errors - see "API introduction".
Responses contain the pixel token
access_token is returned in every pixel response. Don't write these responses to shared logs and don't forward them - it is as much a secret as the API key.
One pixel or tag
GET/fb_pixels/{id}
GET/google_tags/{id}
The response is in data.fb_pixel or data.google_tag, the fields are the same as in the list.
curl https://panel.example.com/api/google_tags/Lm3T \
-H "Authorization: Bearer YOUR_KEY" \
-H "Accept: application/json"<?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$api = new GuzzleHttp\Client([
'base_uri' => 'https://panel.example.com/api/',
'headers' => ['Authorization' => 'Bearer YOUR_KEY', 'Accept' => 'application/json'],
'timeout' => 30,
]);
$tag = json_decode($api->get('google_tags/Lm3T')->getBody(), true)['data']['google_tag'];
echo "{$tag['id']}: tag {$tag['tag_id']}" . PHP_EOL;# pip install requests
import requests
API = "https://panel.example.com/api"
HEADERS = {"Authorization": "Bearer YOUR_KEY", "Accept": "application/json"}
tag = requests.get(f"{API}/google_tags/Lm3T", headers=HEADERS, timeout=30).json()["data"]["google_tag"]
print(tag["id"], "tag", tag["tag_id"])// Node.js 18+, no dependencies; save as script.mjs
const API = 'https://panel.example.com/api';
const headers = { Authorization: 'Bearer YOUR_KEY', Accept: 'application/json', 'Content-Type': 'application/json' };
const response = await fetch(`${API}/google_tags/Lm3T`, {
method: 'GET',
headers,
signal: AbortSignal.timeout(30000),
});
const { data } = await response.json();
console.log(data.google_tag.id, 'tag', data.google_tag.tag_id);Errors
| Code | message | When |
|---|---|---|
403 | This action is unauthorized. | A pixel or tag of another team |
404 | Not found | No such identifier (for tags - GoogleTag) |
Adding a pixel
POST/fb_pixels
| Field | Required | What it is |
|---|---|---|
pixel_id | yes | Pixel number from Facebook, 5 to 20 digits. Unique within a team |
access_token | no | Conversions API token. Without it conversions are not sent to Facebook from the server |
team_id | for team_lead and admin | Team; for a customer it is filled in automatically |
curl -X POST https://panel.example.com/api/fb_pixels \
-H "Authorization: Bearer YOUR_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"pixel_id": "1234567890123456", "access_token": "EAAB...", "team_id": "WQby"}'<?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$api = new GuzzleHttp\Client([
'base_uri' => 'https://panel.example.com/api/',
'headers' => ['Authorization' => 'Bearer YOUR_KEY', 'Accept' => 'application/json'],
'timeout' => 30,
]);
$response = $api->post('fb_pixels', ['json' => [
'pixel_id' => '1234567890123456',
'access_token' => 'EAAB...',
'team_id' => 'WQby',
]]);
$pixelId = json_decode($response->getBody(), true)['data']['fb_pixel']['id'];
echo "Pixel added, id for the PWA: {$pixelId}" . PHP_EOL;# pip install requests
import requests
API = "https://panel.example.com/api"
HEADERS = {"Authorization": "Bearer YOUR_KEY", "Accept": "application/json"}
response = requests.post(f"{API}/fb_pixels", headers=HEADERS, json={
"pixel_id": "1234567890123456",
"access_token": "EAAB...",
"team_id": "WQby",
}, timeout=30)
pixel_id = response.json()["data"]["fb_pixel"]["id"]
print("Pixel added, id for the PWA:", pixel_id)// Node.js 18+, no dependencies; save as script.mjs
const API = 'https://panel.example.com/api';
const headers = { Authorization: 'Bearer YOUR_KEY', Accept: 'application/json', 'Content-Type': 'application/json' };
const response = await fetch(`${API}/fb_pixels`, {
method: 'POST',
headers,
body: JSON.stringify({
pixel_id: '1234567890123456',
access_token: 'EAAB...',
team_id: 'WQby',
}),
signal: AbortSignal.timeout(30000),
});
const { data } = await response.json();
console.log('Pixel added, id for the PWA:', data.fb_pixel.id);The response is the created pixel in data.fb_pixel. Remember its id (WZAX) - that's what attaches the pixel to a PWA, the pixel number won't work for that.
Errors
| Code | message | When |
|---|---|---|
422 | The pixel id field format is invalid. | pixel_id is not 5-20 digits |
422 | The pixel id has already been taken. | The team already has this pixel (including in the archive) |
422 | The team id field is required. | A team_lead or admin didn't pass team_id |
422 | You do not have access to this team | team_id of another team |
422 | The access token field must not be greater than 512 characters. | The token is longer than 512 characters |
Adding a tag
POST/google_tags
| Field | Required | What it is |
|---|---|---|
tag_id | yes | Identifier: AW-… (Google Ads), G-… (Analytics) or GTM-… (Tag Manager). Unique within a team |
install_label, registration_label, deposit_label | no | Google Ads conversion labels for install, registration and deposit |
team_id | for team_lead and admin | Team; for a customer it is filled in automatically |
curl -X POST https://panel.example.com/api/google_tags \
-H "Authorization: Bearer YOUR_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"tag_id": "AW-123456789", "install_label": "AbC-install", "team_id": "WQby"}'<?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$api = new GuzzleHttp\Client([
'base_uri' => 'https://panel.example.com/api/',
'headers' => ['Authorization' => 'Bearer YOUR_KEY', 'Accept' => 'application/json'],
'timeout' => 30,
]);
$response = $api->post('google_tags', ['json' => [
'tag_id' => 'AW-123456789',
'install_label' => 'AbC-install',
'team_id' => 'WQby',
]]);
$tagId = json_decode($response->getBody(), true)['data']['google_tag']['id'];
echo "Tag added, id for the PWA: {$tagId}" . PHP_EOL;# pip install requests
import requests
API = "https://panel.example.com/api"
HEADERS = {"Authorization": "Bearer YOUR_KEY", "Accept": "application/json"}
response = requests.post(f"{API}/google_tags", headers=HEADERS, json={
"tag_id": "AW-123456789",
"install_label": "AbC-install",
"team_id": "WQby",
}, timeout=30)
tag_id = response.json()["data"]["google_tag"]["id"]
print("Tag added, id for the PWA:", tag_id)// Node.js 18+, no dependencies; save as script.mjs
const API = 'https://panel.example.com/api';
const headers = { Authorization: 'Bearer YOUR_KEY', Accept: 'application/json', 'Content-Type': 'application/json' };
const response = await fetch(`${API}/google_tags`, {
method: 'POST',
headers,
body: JSON.stringify({
tag_id: 'AW-123456789',
install_label: 'AbC-install',
team_id: 'WQby',
}),
signal: AbortSignal.timeout(30000),
});
const { data } = await response.json();
console.log('Tag added, id for the PWA:', data.google_tag.id);The response is the created tag in data.google_tag.
Errors
| Code | message | When |
|---|---|---|
422 | The tag id field format is invalid. | tag_id doesn't look like AW-…, G-… or GTM-… |
422 | The tag id has already been taken. | The team already has this tag (including in the archive) |
422 | The team id field is required. | A team_lead or admin didn't pass team_id |
Updating
PUT/fb_pixels/{id}
PUT/google_tags/{id}
Change only the fields passed, accept the same fields as adding. Changes take effect right away in every PWA with this pixel or tag - the PWAs themselves don't need to be re-saved.
curl -X PUT https://panel.example.com/api/fb_pixels/WZAX \
-H "Authorization: Bearer YOUR_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"access_token": "EAAB-new"}'<?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$api = new GuzzleHttp\Client([
'base_uri' => 'https://panel.example.com/api/',
'headers' => ['Authorization' => 'Bearer YOUR_KEY', 'Accept' => 'application/json'],
'timeout' => 30,
]);
$response = $api->put('fb_pixels/WZAX', ['json' => ['access_token' => 'EAAB-new']]);
$pixel = json_decode($response->getBody(), true)['data']['fb_pixel'];
echo "Token of pixel {$pixel['pixel_id']} updated" . PHP_EOL;# pip install requests
import requests
API = "https://panel.example.com/api"
HEADERS = {"Authorization": "Bearer YOUR_KEY", "Accept": "application/json"}
response = requests.put(f"{API}/fb_pixels/WZAX", headers=HEADERS, json={"access_token": "EAAB-new"}, timeout=30)
pixel = response.json()["data"]["fb_pixel"]
print("Token of pixel", pixel["pixel_id"], "updated")// Node.js 18+, no dependencies; save as script.mjs
const API = 'https://panel.example.com/api';
const headers = { Authorization: 'Bearer YOUR_KEY', Accept: 'application/json', 'Content-Type': 'application/json' };
const response = await fetch(`${API}/fb_pixels/WZAX`, {
method: 'PUT',
headers,
body: JSON.stringify({ access_token: 'EAAB-new' }),
signal: AbortSignal.timeout(30000),
});
const { data } = await response.json();
console.log('Token of pixel', data.fb_pixel.pixel_id, 'updated');The response is the pixel or tag after the change.
Errors
| Code | message | When |
|---|---|---|
403 | This action is unauthorized. | A pixel or tag of another team |
422 | The pixel id field format is invalid. | The same field checks as on adding |
Archive
DELETE/fb_pixels/{id}
DELETE/google_tags/{id}
Archive instead of deleting. PWAs keep referring to it, but events don't go to an archived pixel or tag. To restore it - PUT with "archived": false: the PWAs start using it again without reconfiguring.
# Archive
curl -X DELETE https://panel.example.com/api/fb_pixels/WZAX \
-H "Authorization: Bearer YOUR_KEY" -H "Accept: application/json"
# Restore from archive
curl -X PUT https://panel.example.com/api/fb_pixels/WZAX \
-H "Authorization: Bearer YOUR_KEY" -H "Accept: application/json" \
-H "Content-Type: application/json" -d '{"archived": false}'<?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$api = new GuzzleHttp\Client([
'base_uri' => 'https://panel.example.com/api/',
'headers' => ['Authorization' => 'Bearer YOUR_KEY', 'Accept' => 'application/json'],
'timeout' => 30,
]);
// Archive
$archived = json_decode($api->delete('fb_pixels/WZAX')->getBody(), true)['data']['fb_pixel'];
echo 'Archived: ' . var_export($archived['archived'], true) . PHP_EOL;
// Restore from archive
$restored = json_decode($api->put('fb_pixels/WZAX', ['json' => ['archived' => false]])->getBody(), true)['data']['fb_pixel'];
echo 'Archived: ' . var_export($restored['archived'], true) . PHP_EOL;# pip install requests
import requests
API = "https://panel.example.com/api"
HEADERS = {"Authorization": "Bearer YOUR_KEY", "Accept": "application/json"}
# Archive
archived = requests.delete(f"{API}/fb_pixels/WZAX", headers=HEADERS, timeout=30).json()["data"]["fb_pixel"]
print("Archived:", archived["archived"])
# Restore from archive
restored = requests.put(f"{API}/fb_pixels/WZAX", headers=HEADERS, json={"archived": False}, timeout=30).json()["data"]["fb_pixel"]
print("Archived:", restored["archived"])// Node.js 18+, no dependencies; save as script.mjs
const API = 'https://panel.example.com/api';
const headers = { Authorization: 'Bearer YOUR_KEY', Accept: 'application/json', 'Content-Type': 'application/json' };
// Archive
const archiveResponse = await fetch(`${API}/fb_pixels/WZAX`, {
method: 'DELETE',
headers,
signal: AbortSignal.timeout(30000),
});
const archived = (await archiveResponse.json()).data.fb_pixel;
console.log('Archived:', archived.archived);
// Restore from archive
const restoreResponse = await fetch(`${API}/fb_pixels/WZAX`, {
method: 'PUT',
headers,
body: JSON.stringify({ archived: false }),
signal: AbortSignal.timeout(30000),
});
const restored = (await restoreResponse.json()).data.fb_pixel;
console.log('Archived:', restored.archived);The response is the pixel or tag with archived: true (after DELETE) or false (after restoring). The DELETE response comes without team and groups.
Errors
| Code | message | When |
|---|---|---|
403 | This action is unauthorized. | A pixel or tag of another team |
Attaching to a PWA
A PWA has one Facebook pixel and one Google tag. They are set in config.metrics when updating the PWA (see the API section "PWA"):
| Field | What it is |
|---|---|
facebook.pixelId | The pixel's id from the API response (WZAX), not the pixel number |
facebook.events | Which Facebook event to send on install, registration and deposit: Lead, CompleteRegistration, Purchase, Subscribe, StartTrial, PageView and other standard events |
google.tagId | The tag's id from the API response |
The pixel and tag must be from the same team as the PWA.
config is replaced as a whole
As with any config change, first get the PWA's current config, change metrics in it and send it back as a whole - see the API section "PWA".
curl https://panel.example.com/api/pwas/WZAX \
-H "Authorization: Bearer YOUR_KEY" -H "Accept: application/json" > pwa.json
jq '{team_id: .data.pwa.team.id, config: (.data.pwa.config
| .metrics.facebook.pixelId = "WZAX"
| .metrics.facebook.events = {"install": "Lead", "registration": "CompleteRegistration", "deposit": "Purchase"}
| .metrics.google.tagId = "Lm3T")}' pwa.json \
| curl -X PUT https://panel.example.com/api/pwas/WZAX \
-H "Authorization: Bearer YOUR_KEY" -H "Accept: application/json" \
-H "Content-Type: application/json" -d @-<?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$api = new GuzzleHttp\Client([
'base_uri' => 'https://panel.example.com/api/',
'headers' => ['Authorization' => 'Bearer YOUR_KEY', 'Accept' => 'application/json'],
'timeout' => 30,
]);
$pwa = json_decode($api->get('pwas/WZAX')->getBody(), true)['data']['pwa'];
$config = $pwa['config'];
$config['metrics']['facebook']['pixelId'] = 'WZAX';
$config['metrics']['facebook']['events'] = [
'install' => 'Lead',
'registration' => 'CompleteRegistration',
'deposit' => 'Purchase',
];
$config['metrics']['google']['tagId'] = 'Lm3T';
$api->put('pwas/WZAX', ['json' => ['team_id' => $pwa['team']['id'], 'config' => $config]]);
echo "Pixel and tag attached to {$pwa['name']}" . PHP_EOL;# pip install requests
import requests
API = "https://panel.example.com/api"
HEADERS = {"Authorization": "Bearer YOUR_KEY", "Accept": "application/json"}
pwa = requests.get(f"{API}/pwas/WZAX", headers=HEADERS, timeout=30).json()["data"]["pwa"]
config = pwa["config"]
config["metrics"]["facebook"]["pixelId"] = "WZAX"
config["metrics"]["facebook"]["events"] = {
"install": "Lead",
"registration": "CompleteRegistration",
"deposit": "Purchase",
}
config["metrics"]["google"]["tagId"] = "Lm3T"
requests.put(f"{API}/pwas/WZAX", headers=HEADERS, json={"team_id": pwa["team"]["id"], "config": config}, timeout=30)
print("Pixel and tag attached to", pwa["name"])// Node.js 18+, no dependencies; save as script.mjs
const API = 'https://panel.example.com/api';
const headers = { Authorization: 'Bearer YOUR_KEY', Accept: 'application/json', 'Content-Type': 'application/json' };
const getResponse = await fetch(`${API}/pwas/WZAX`, {
method: 'GET',
headers,
signal: AbortSignal.timeout(30000),
});
const pwa = (await getResponse.json()).data.pwa;
const config = pwa.config;
config.metrics.facebook.pixelId = 'WZAX';
config.metrics.facebook.events = {
install: 'Lead',
registration: 'CompleteRegistration',
deposit: 'Purchase',
};
config.metrics.google.tagId = 'Lm3T';
await fetch(`${API}/pwas/WZAX`, {
method: 'PUT',
headers,
body: JSON.stringify({ team_id: pwa.team.id, config }),
signal: AbortSignal.timeout(30000),
});
console.log('Pixel and tag attached to', pwa.name);Errors
| Code | message | When |
|---|---|---|
422 | The selected resource does not exist | No such id - for example, the pixel number was passed instead of the id |
422 | You do not have access to this resource | A pixel or tag of a team you have no access to |
422 | The selected install is invalid. | Unknown Facebook event in events (the same for registration and deposit) |
PWA clones get the same pixel and tag as the original.