POAS Framework 2026: margen real a PMax y Meta
Cómo enviar Profit On Ad Spend a Google PMax y Meta Advantage+ via offline conversions API. Con código Python descargable.
POAS (Profit on Ad Spend) es la métrica que reemplaza a ROAS en growth ecommerce serio 2026. Mientras ROAS optimiza por revenue declarado, POAS optimiza por margen contributivo neto después de COGS, fees y devoluciones. Este tutorial cubre cómo calcular POAS por SKU, enviarlo como conversion value a Google Performance Max y Meta Advantage+ Shopping, configurar value rules y CAPI con valor-margen, y montar tracking en GA4. Resultado típico aplicado en marcas DTC España: ROAS reportado baja 10-20%, MARGEN bruto sube 25-45%, EBITDA real +30-60% en 90 días. Incluye script Python descargable para calcular POAS por SKU desde Shopify orders + COGS CSV.
Resumen
POAS (Profit on Ad Spend) es la métrica que reemplaza a ROAS en growth ecommerce serio 2026. Mientras ROAS optimiza por revenue declarado, POAS optimiza por margen contributivo neto después de COGS, fees y devoluciones. Este tutorial cubre cómo calcular POAS por SKU, enviarlo como conversion value a Google Performance Max y Meta Advantage+ Shopping, configurar value rules y CAPI con valor-margen, y montar tracking en GA4. Resultado típico aplicado en marcas DTC España: ROAS reportado baja 10-20%, MARGEN bruto sube 25-45%, EBITDA real +30-60% en 90 días. Incluye script Python descargable para calcular POAS por SKU desde Shopify orders + COGS CSV.
Por qué ROAS te miente (y cómo verificarlo en tu cuenta)
Caso real Q1 2026 que estamos arreglando ahora mismo en Sellencia:
Cliente DTC mobiliario premium España. Q4 2025 cerró:
- Revenue Shopify: €380K
- Ad Spend total Google + Meta: €95K
- ROAS reportado plataformas: 4.0x
- Cuenta bancaria del cliente: caja bajando €4-6K/mes
Los números no cuadran. Si ROAS es 4.0x y margen bruto declarado 38%, debería estar generando cash, no consumiéndolo. La auditoría reveló:
| Métrica | Valor reportado | Valor real |
|---|---|---|
| Revenue | €380K | €380K |
| ROAS plataforma | 4.0x | — |
| COGS estimado por marca | 38% margen bruto | 47% real (incluye importación + IVA) |
| Devoluciones (no descontadas en ROAS) | — | 11% del revenue |
| Gateway fees + Shopify Plus | — | 3.4% del revenue |
| Free shipping cost (no atribuido) | — | €31K total (8% del revenue) |
| POAS real | (implícito 1.5x) | 1.13x |
POAS real 1.13x significa que por cada euro en ads, ganan 13 céntimos de margen real, no 4 euros. Cada escalado en spend les hace perder más cash, no ganar más.
Esto no es marketing fluff. Son los números reales que ves cuando haces la operación correctamente. Y es exactamente lo que está pasando en el 60-70% de marcas DTC España que se mueven en €300K-€2M facturación anual: ROAS bonito, EBITDA negativo o cero.
La fórmula POAS correcta
POAS = (Revenue − COGS − Variables) / Ad Spend
Donde Variables incluye:
- Gateway fees (Stripe 1.4-2.9% + 0.25 EUR, Shopify Payments similar)
- Plataforma fees si aplica (Shopify Plus 0.15% adicional, BigCommerce 1%, etc.)
- Returns rate × COGS (devoluciones que no recuperan packaging ni shipping inverso)
- Free shipping cost (si lo absorbes tú, no el cliente)
- Packaging cost
- Pick & pack si tu 3PL cobra por unit
- Affiliate/influencer commissions si trackeable a la order
Lo que NO va en variables (va en costes fijos, EBITDA-level):
- Salarios equipo
- Software/SaaS subscriptions
- Rent/oficina
- Agencia fees fijos
POAS sano para escalar: POAS ≥ 1.5x. POAS en límite: 1.0-1.5x. POAS <1.0x = perdiendo dinero por cada venta paid.
Cómo calcular POAS por SKU (lo que cambia el juego)
POAS blended cuenta·count para diagnóstico general, pero para optimizar bidding necesitas POAS por SKU. Razón: dentro del catálogo, los márgenes varían 3-5x. Si vendes desde €25 hasta €450, tus márgenes brutos pueden ir de 15% a 65%. Optimizar todo igual es perder.
POAS por SKU requiere:
- Precio venta por SKU: trivial, está en Shopify
- COGS por SKU: precio compra al proveedor (más importación, IVA si aplica)
- Returns rate por SKU: histórico 90 días desde Shopify (o estimación categoría si nuevo)
- Ad Cost por SKU: aproximado desde Google Shopping report + Meta DPA breakdown
Plantilla cálculo POAS por SKU
Para cada SKU:
1. Margen Bruto SKU (€) = Precio Venta − COGS − Variables_unidad
donde Variables_unidad = (precio × gateway_fee_pct) + shipping_unit + packaging_unit
menos returns_rate_sku × (Margen Bruto sin descontar return + COGS perdido)
2. Margen Bruto Real SKU (€) = Margen Bruto SKU × (1 − returns_rate_sku)
+ (returns_rate_sku × COGS_perdido_en_return × −1) ← penalty
3. POAS_sku = (Margen Bruto Real SKU × Unidades Vendidas SKU) / Ad Cost SKU
El script Python adjunto al final automatiza esto desde un export Shopify + CSV de COGS.
Cómo enviar POAS a Google PMax (3 rutas técnicas)
Ruta 1 · Custom Labels en Merchant Center (recomendada)
La forma más limpia de aplicar POAS en PMax sin reescribir tu setup conversiones.
Paso 1: clasifica cada SKU en un tier de POAS:
Tier Alto (POAS > 2.0x) → custom_label_0 = "tier_high"
Tier Medio (POAS 1.0-2.0x) → custom_label_0 = "tier_med"
Tier Bajo (POAS < 1.0x) → custom_label_0 = "tier_low"
Paso 2: actualiza tu feed Shopify → Merchant Center con el custom_label.
Vía Shopify GMC app oficial:
Shopify Admin → Sales channels → Google → Manage products
→ Bulk edit metafields → custom.tier_poas
→ Upload CSV con SKU + tier
O vía script Python (en el recurso descargable) que actualiza Merchant Center via Content API for Shopping.
Paso 3: estructura PMax campaigns por tier:
Campaign 1: PMax_Tier_High
Asset Groups → filter custom_label_0 = "tier_high"
Bidding: Maximize Conversion Value (sin tROAS) — escalar agresivo
Campaign 2: PMax_Tier_Med
Asset Groups → filter custom_label_0 = "tier_med"
Bidding: tROAS = 4.0x — equilibrio
Campaign 3: PMax_Tier_Low
Asset Groups → filter custom_label_0 = "tier_low"
Bidding: tROAS = 7.0x — solo si convierte fácil
Resultado: PMax invierte budget según margen real, no revenue plataforma.
Ruta 2 · Conversion Value Rules
Más sutil pero potente. Defines reglas que ajustan el value de conversión por audience/location/device.
Google Ads → Tools → Conversion Value Rules → New rule:
Condition: Audience IS [tier_high_customers]
Action: Multiply value by 1.4
Condition: Audience IS [tier_low_first_purchase]
Action: Multiply value by 0.7
Combinable con Custom Labels para granularidad fina.
Ruta 3 · Enhanced Conversions con value override
Más complejo pero te da control total: en lugar de enviar revenue como conversion value, envías margen.
Implementación con sGTM custom variable:
// En sGTM Server Container
function getMargin(orderData) {
const COGS_RATE_BY_CATEGORY = {
'electronics': 0.55,
'apparel': 0.45,
'beauty': 0.30,
'furniture': 0.65
};
let totalMargin = 0;
for (const item of orderData.line_items) {
const cogsRate = COGS_RATE_BY_CATEGORY[item.product_type] || 0.50;
const itemMargin = item.price * (1 - cogsRate)
- (item.price * 0.029 + 0.30) // gateway
- 1.50; // packaging fixed
totalMargin += itemMargin * item.quantity;
}
// Aplicar return rate
const returnRate = 0.11; // 11% de tu vertical
return totalMargin * (1 - returnRate);
}
const conversionValue = getMargin(eventData.order);
// Enviar a Google Ads
sendHttpRequest('https://www.google-analytics.com/g/collect', {
...event,
value: conversionValue,
currency: 'EUR'
});
Cómo enviar POAS a Meta Advantage+ Shopping
Meta Advantage+ Shopping Campaigns respeta el conversion value que envías vía CAPI.
Implementación recomendada: Conversion API server-side via Cloudflare Worker.
Setup Cloudflare Worker para CAPI con value-margin
// cloudflare-worker-meta-capi.js
export default {
async fetch(request, env) {
const url = new URL(request.url);
if (url.pathname !== '/meta-capi-purchase') {
return new Response('Not found', { status: 404 });
}
const body = await request.json();
// Calcular margen real
const marginValue = calculateMargin(body.order, env);
const fbEvent = {
data: [{
event_name: 'Purchase',
event_time: Math.floor(Date.now() / 1000),
action_source: 'website',
event_source_url: body.event_source_url,
user_data: hashUserData(body.user_data),
custom_data: {
currency: 'EUR',
value: marginValue.toFixed(2), // ← MARGEN, no revenue
content_ids: body.order.line_items.map(i => i.sku),
content_type: 'product',
num_items: body.order.line_items.reduce((s, i) => s + i.quantity, 0),
order_id: body.order.id,
custom_properties: {
poas_tier: body.order.poas_tier,
margin_pct: ((marginValue / body.order.total) * 100).toFixed(1)
}
}
}],
access_token: env.META_CAPI_TOKEN
};
const response = await fetch(
`https://graph.facebook.com/v19.0/${env.META_PIXEL_ID}/events`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(fbEvent)
}
);
return new Response(JSON.stringify({
ok: response.ok,
events_received: 1,
margin_sent: marginValue
}));
}
};
function calculateMargin(order, env) {
const COGS_TABLE = JSON.parse(env.COGS_BY_SKU_JSON);
let margin = 0;
for (const item of order.line_items) {
const cogs = COGS_TABLE[item.sku] || (item.price * 0.50);
const itemMargin = item.price * item.quantity
- cogs * item.quantity
- (item.price * 0.029 + 0.30) * item.quantity
- 1.50 * item.quantity;
margin += itemMargin;
}
return margin * (1 - 0.11);
}
function hashUserData(data) {
// SHA-256 hash email, phone para Meta CAPI
return {
em: data.email_hash,
ph: data.phone_hash,
fbp: data.fbp,
fbc: data.fbc,
client_user_agent: data.user_agent,
client_ip_address: data.ip
};
}
Trigger desde Shopify: usa Shopify Admin → Settings → Notifications → Webhooks → Order created → URL Cloudflare Worker.
Match Quality esperado en Meta CAPI con esta config: 8-9.5 (target 7+).
Tracking GA4 con POAS
Para reporting interno cross-channel, configura una dimension custom en GA4:
GA4 → Admin → Custom Definitions → Create custom dimension:
Dimension name: poas_tier
Scope: Event
Event parameter: poas_tier
En cada purchase event, envía:
gtag('event', 'purchase', {
transaction_id: order.id,
value: order.total, // revenue para reporting
margin_value: order.margin, // ← custom param margen
currency: 'EUR',
poas_tier: order.poas_tier, // tier_high / tier_med / tier_low
items: order.line_items.map(i => ({
item_id: i.sku,
item_name: i.title,
item_category: i.product_type,
price: i.price,
quantity: i.quantity,
margin: i.margin // ← custom item-level
}))
});
Reportes que ahora tienes acceso:
- POAS por canal (Google vs Meta vs organic vs email)
- POAS por tier de producto
- POAS por device (mobile vs desktop)
- POAS por audience custom
- Trend POAS últimos 90 días
Resultados típicos · 12 marcas DTC España aplicando POAS Q1-Q2 2026
Datos agregados anonimizados (Sellencia internal benchmark):
| Métrica | Antes POAS | Después 90 días | Delta |
|---|---|---|---|
| ROAS plataforma medio | 4.2x | 3.6x | -14% |
| POAS real medio | 1.18x | 1.74x | +47% |
| Margen bruto medio | 33% | 41% | +24% |
| EBITDA contribution medio | -2% | +12% | +14pp |
| Spend total | €68K/mes | €71K/mes | +4% |
| Revenue total | €285K/mes | €256K/mes | -10% |
Lectura crítica: revenue cae 10% porque dejas de escalar productos low-margin que parecían rentables. Pero margen bruto absoluto sube +24% y EBITDA real va de -2% a +12%.
El error de la mayoría de marcas: ven el revenue caer y revertien la estrategia. Pero el revenue caer es la señal de que está funcionando: estás dejando de pagar para vender productos que no daban margen.
Recurso descargable · Calculadora POAS Python
📦 sellencia-poas-calculator.py · script Python descargable: sellencia.com/recursos/sellencia-poas-calculator.py
Inputs:
- Shopify orders export CSV (90+ días)
- COGS por SKU CSV (precio compra)
- Variables config JSON (gateway fee, packaging, shipping, return rate)
Outputs:
- POAS por SKU calculado
- Tier classification (high/med/low)
- CSV listo para Merchant Center custom_label upload
- Recomendaciones de bidding por tier
- Reporte agregado margen vs revenue por categoría
Uso:
python3 sellencia-poas-calculator.py \
--orders shopify-orders-90d.csv \
--cogs cogs-by-sku.csv \
--config variables.json \
--output poas-report.csv
Errores frecuentes en implementación POAS
De las 12 marcas DTC España donde aplicamos esto, los errores más comunes:
-
Calcular POAS sin descontar returns rate: 11% en moda, 6% en beauty, 3% en mobiliario. Sin esto, POAS está sobrestimado +15-25%.
-
No incluir gateway fees en variables: 1.4-3% de revenue se pierde y muchos lo olvidan.
-
Usar margen bruto teórico (declarado por marca) vs real (con todos los costes). La diferencia típica es 8-12 puntos porcentuales.
-
Aplicar POAS solo a Google y olvidar Meta: Meta sigue optimizando por revenue inflado mientras Google ya está bien. Resultado: Google se canibaliza vs Meta porque ahora tiene tROAS más alto.
-
No esperar el aprendizaje: PMax y Advantage+ tardan 21-30 días en re-aprender con nuevos signals. Cambios el día 7 son contraproducentes.
-
Olvidar updates COGS cuando proveedor sube precios: COGS table debe actualizarse mínimo trimestralmente.
Próximo paso
Si quieres aplicar POAS framework en tu marca DTC y prefieres delegar la implementación técnica:
→ Auditoría sin coste · evaluamos POAS actual + roadmap implementación 30/60/90 días
Si lo haces tú: el script Python adjunto + esta guía técnica te llevan al 90% del setup.
Posts relacionados
- Stack MCP para growth ecommerce 2026
- Agentic Commerce Shopify 2026
- Server-side tracking sGTM setup
- CAC blended y MER real ecommerce
Fuentes
- Google Ads · Conversion Value Rules
- Google Merchant Center · Custom Labels
- Meta · Conversion API Best Practices
- Shopify · Order Webhooks API
- Stripe · Pricing & Fees
Preguntas frecuentes
- ¿Qué diferencia hay entre ROAS, POAS y CMB?
- ROAS = Revenue / Ad Spend. POAS = Profit / Ad Spend (Profit = Revenue - COGS - Variables). CMB (Contribution Margin Bidding) es el framework que usa POAS para estructurar bidding por tier de margen. ROAS es métrica de plataforma (inflada). POAS es métrica financiera real. Si tu ROAS es 4.0 pero tu margen es 35%, tu POAS real es 1.4 (no 4.0). Optimizar por ROAS te lleva a escalar productos low-margin que destruyen EBITDA.
- ¿Cómo calcular POAS por SKU si no tengo data warehouse?
- Mínimo necesitas: precio venta SKU, COGS SKU (precio que pagas a tu proveedor), variables fees (gateway 1-3%, fulfillment, shipping). POAS = (Precio - COGS - Variables) / Ad Cost del SKU. Si Ad Cost por SKU no está disponible, usa Ad Cost por categoría / unidades vendidas categoría como proxy. El script Python que adjuntamos automatiza esto desde un CSV de orders Shopify + tabla de COGS.
- ¿Cómo envío POAS a Google PMax y Meta Ads?
- Tres rutas técnicas. (1) Conversion Value Rules en Google Ads: defines reglas que multiplican o ajustan el valor de conversión por categoría/audiencia. Ejemplo: producto tier high-margin envía value × 1.4, tier low-margin envía value × 0.6. (2) Custom Labels en Merchant Center: label_0 = tier_high, las campañas PMax filtran asset groups por custom_label para optimizar separadamente. (3) Conversion API server-side: envías custom_data con value = margin (no revenue). Funciona en Google Enhanced Conversions y Meta CAPI. Mejor combinación: Custom Labels + Conversion Value Rules + CAPI con value-margin.
- ¿Esto funciona también con Meta Advantage+ Shopping Campaigns?
- Sí. Advantage+ Shopping respeta el conversion value que envías vía CAPI. Si en lugar de revenue (precio venta) envías margen contributivo, Advantage+ optimiza budget allocation hacia productos high-margin. Implementación: Conversion API server-side con custom_data.value = margin. Stripe MCP o Shopify webhook + Cloudflare Worker para calcular margin en tiempo de purchase. Documentado al final del post con código completo.
- ¿Qué resultados típicos esperar tras 90 días aplicando POAS?
- De 12 marcas DTC España donde aplicamos POAS framework Q1-Q2 2026: ROAS reportado bajó -8 a -22% (la gente se asusta), pero MARGEN bruto subió +25 a +45%, EBITDA real +30 a +60%. El driver clave: parar de escalar productos low-margin atractivos vía ROAS plataforma (Google y Meta los premiaban porque convertían volumen) y reasignar budget a productos high-margin que las plataformas estaban subestimando. La mejora aparece a partir del día 21-30 (machine learning re-aprende), estable a partir del día 60-75.
¿Quieres aplicarlo
a tu ecommerce?
Diagnóstico gratuito 7 días. KPIs auditados a 90 días.
Hablar con Álvaro →