Container health score
One number, four weighted categories and a letter grade — integrity, efficiency, code quality and governance, each with the findings behind it.
Drop in a container.js build, paste a site URL, or point it at a Reactor
API export. You get a complete, readable workbook of every rule, condition, action and
data element — plus a health report that finds the things that quietly break tracking.
Drop your container file here
or browse for a file — container.js, launch-*.js or .json
Several containers? Select them all — Reactor API files are merged
into one property, and separate container.js builds are compared side by side.
This is the one place the app reaches the network. It requests a public URL you typed — never your container. It's tried in your browser first, with no server involved.
No container handy?
One number, four weighted categories and a letter grade — integrity, efficiency, code quality and governance, each with the findings behind it.
Rules that can never fire, rules that do nothing, data elements nobody references,
broken %references% and extensions installed for no reason.
Every trigger rewritten as a sentence. WHEN a user clicks ".add-to-cart" IF the path contains "/product/" THEN send a link beacon. No JSON archaeology.
Every inline block extracted with line counts and risk flags —
document.write, eval, jQuery dependencies, leftover logging.
See exactly which rules and actions consume each data element — before you delete something that turns out to be load-bearing.
Every object serialised and weighed, so you see which six rules are 40% of your container — and compare several properties side by side in one dashboard.
No upload, no server, no analytics on this page — and your container's code is read, never executed. Works offline.
Adobe has no one-click "export container" like Google Tag Manager. There are five routes that work — pick the one that matches the access you have.
Shortcut: try the From a URL tab above first. Paste the
launch-*.min.js URL and your browser can usually fetch it directly.
Pasting a site URL only works through the server resolver, because browsers
refuse to read another site's HTML — the tab explains this and offers the way round
if it happens.
Every published environment is served as one JavaScript file with the container inlined. This is the same file your site loads, so it is exactly what is live.
Go to experience.adobe.com → Data Collection → Tags, then select your property.
Pick Environments in the left nav. You'll see Development, Staging and Production.
Click the install icon </> on the row for the environment
you want to audit. A dialog shows the embed code.
It looks like this:
https://assets.adobedtm.com/<hash>/<hash>/launch-<id>-development.min.js
Either open the URL in a new tab and save the page, or:
curl -o container.js "https://assets.adobedtm.com/.../launch-....min.js"
container.js into the box aboveMinified builds are fine — the container is found and read inside them.
If Tags is deployed on a site you can visit, the library is already public. This is the quickest way to audit a production property when you only have access to the website.
Then open DevTools — F12, or Cmd+Option+I on a Mac.
The library is requested early in page load.
Type assets.adobedtm.com in the filter box — or your own
domain if the library is self-hosted. Look for launch-*.js.
Right-click the request → Save as… (or open the Response tab and copy it into a file).
Or paste the response straight into the Paste it tab — no file needed.
Reads the live _satellite.container and saves it
as a file. Handy when the library is self-hosted at a URL you can't easily find,
or sits behind authentication.
In Chrome you must type allow pasting once before it will
accept pasted code.
(function(){
if(!window._satellite||!_satellite.container){
console.error('No Adobe Tags container on this page.');return;
}
var stack=[],fns=0,cut=0;
var json=JSON.stringify(_satellite.container,function(k,v){
// Functions hold your custom code. JSON.stringify drops them silently,
// so convert each one to source text before it is lost.
if(typeof v==='function'){fns++;return Function.prototype.toString.call(v);}
if(v instanceof RegExp){return String(v);}
if(v&&typeof v==='object'){
// Only a value that contains ITSELF is a cycle. Tracking every object
// ever seen would also discard the second copy of a SHARED object, and
// a live container shares plenty — that silently shrinks the export.
while(stack.length&&stack[stack.length-1]!==this){stack.pop();}
if(stack.indexOf(v)!==-1){cut++;return'[circular]';}
stack.push(v);
}
return v;
},2);
var name=((_satellite.container.property||{}).name||'adobe-tags')
.replace(/[^\w.-]+/g,'-').slice(0,60);
var a=document.createElement('a');
a.href=URL.createObjectURL(new Blob([
'window._satellite = window._satellite || {};\n'+
'window._satellite.container = '+json+';\n'
],{type:'text/javascript'}));
a.download=name+'-container.js';
document.body.appendChild(a);a.click();document.body.removeChild(a);
// Compare these against the report. Silent shrinkage is the failure that
// matters here: a smaller container scores better than the real one.
console.log('Saved '+a.download+' — '+(_satellite.container.rules||[]).length+' rules, '+
Object.keys(_satellite.container.dataElements||{}).length+' data elements, '+
fns+' code blocks, '+json.length+' chars.');
if(cut){console.warn('[Tags export] '+cut+' self-referencing object(s) collapsed.');}
})();
container.js downloads — drop it above
Why not just JSON.stringify(_satellite.container)?
Because JSON.stringify drops functions silently — you would lose
every custom code block and never be told. The snippet converts them to source
text first. It also only collapses objects that genuinely contain themselves:
a live container reaches the same settings object by more than one path, and
discarding the second copy would quietly shrink the export — and inflate its
score. If a container you export here scores better than the same property's
container.js file, that gap is the thing to look at.
A runtime container only holds what was built into one environment — disabled rules are not in it at all. The Reactor API returns the whole property, including disabled rules, revision numbers and modified dates.
In the Adobe Developer Console, create a project and add the Adobe Experience Platform Launch API, using OAuth Server-to-Server. You'll get a client ID, a client secret and your org ID.
BASE=https://reactor.adobe.io
AUTH=(-H "Authorization: Bearer $TOKEN"
-H "x-api-key: $CLIENT_ID"
-H "x-gw-ims-org-id: $ORG_ID"
-H "Accept: application/vnd.api+json;revision=1")
curl -s "${AUTH[@]}" "$BASE/properties/$PROPERTY_ID/rules?page[size]=100" > rules.json
curl -s "${AUTH[@]}" "$BASE/properties/$PROPERTY_ID/data_elements?page[size]=100" > data_elements.json
curl -s "${AUTH[@]}" "$BASE/properties/$PROPERTY_ID/extensions?page[size]=100" > extensions.json
# rule_components hang off each rule
curl -s "${AUTH[@]}" "$BASE/rules/$RULE_ID/rule_components?page[size]=100" > components-$RULE_ID.json
Watch page[size] — the API paginates, so follow
links.next if you have more than 100 of anything.
Select every JSON file together and this app merges them for you — duplicates across files are removed. If you'd rather merge them yourself:
jq -s '{data: (map(.data) | add)}' rules.json data_elements.json \
extensions.json components-*.json > property-export.json
If an environment is configured to self-host — SFTP or your own CDN rather than Adobe's — the built library is offered as a downloadable archive.
Data Collection → Tags → your property → Environments, then open the self-hosted environment.
The environment offers the built library as an archive once a library has been published to it.
launch-*.jsThat file is the container build. Drop it above.
Already deploying self-hosted files through your own pipeline? The same
launch-*.js that lands on your CDN works — no need to re-download
it from Adobe.
| Route | Access needed | Scope | Disabled rules | Custom code |
|---|---|---|---|---|
| 1 · Hosted library URL | Tags UI | One environment, as built | No | Yes |
| 2 · From a live site | The website | One environment, as deployed | No | Yes |
| 3 · Browser console | The website | One environment, as running | No | Yes |
| 4 · Reactor API | API credentials | Whole property | Yes | Yes |
| 5 · Self-hosted archive | Tags UI + self-hosting | One environment, as built | No | Yes |
Routes 1, 2, 3 and 5 all produce the same kind of build artefact, so they audit identically — choose whichever matches your access. Reach for route 4 when you need the full property rather than one deployment, or want to run this in CI.
Files are read with the browser's FileReader and stay in page memory.
Your container is never sent anywhere. The one time this app touches the network is
the From a URL tab, and then it requests only the public URL you typed —
tried in your browser first, with no server in the middle. Confirm it in the
Network tab.
A container.js contains real JavaScript. Instead of running it, this app
reads it with a purpose-built parser: functions are captured as text, never
called. There is no eval anywhere in the app, so a hostile file has
nothing to execute.
Every dependency is first-party — even the .xlsx writer is local
JavaScript, not a CDN library. Disconnect your machine and everything except the
URL tab still works.
Every rule, data element and extension is serialised and weighed, so this is measured rather than estimated. Configuration weight is what you author and can shrink by editing the property; the library file is what visitors download, and also carries the Turbine runtime and each extension's module code.
Everything above is provable from the container file alone. These checks need a live page — they're generated from your rules, direct-call identifiers and data-layer paths, so you can work straight down the list. Ticking is local to this page and isn't saved anywhere.
Select any row to open its full report, findings and workbooks.