Your storefront can't see what buyers do, and the links you send in DMs come out bare. These fix both, they're written against the API your site already serves, and nothing a customer sees changes.
Link previews are a same day win because you're already sending product links and they're already landing flat. The pixel is slower and worth more, because an audience has to accumulate before any campaign can work, so the sooner it starts collecting the sooner spend becomes worth doing. Both are one afternoon.
The browser and the server both report the same action, and Meta only collapses them into
one event when the event_id matches on both sides. That shared id is the
entire trick. If your numbers ever look doubled, this is what broke.
META_PIXEL_ID=1234567890 META_CAPI_TOKEN=EAAG... META_TEST_EVENT_CODE=TEST12345 # remove for production
public/index.html, in the head. Note there's no
PageView call in it on purpose, step 3 handles that, and leaving it here as well
counts your first page twice.<script>
!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,
document,'script','https://connect.facebook.net/en_US/fbevents.js');
fbq('init','YOUR_PIXEL_ID');
</script>pixel.js to src/lib/, then mount the page view
hook inside your Router and call the trackers from the components that already have the
product in hand.import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { usePixelPageViews, trackViewContent, trackAddToCart,
trackInitiateCheckout, trackDeposit } from './lib/pixel';
function Analytics() {
usePixelPageViews(useLocation, useEffect);
return null;
}
// <BrowserRouter> ... <Analytics /> ... </BrowserRouter>
// product page: useEffect(() => { if (product) trackViewContent(product); }, [product]);
// add to cart: trackAddToCart(product, qty)
// checkout button: trackInitiateCheckout(items, total)
// "Make A Deposit": trackDeposit(product, product.price * 0.5)trackDeposit reports the full piece value, not the 50% deposit.
That's deliberate. Optimising toward half the basket teaches Meta to go find you the
customer who spends half as much.
/api.
pip install httpx and that's the whole dependency.from fastapi import FastAPI from capi import router as meta_router from og_prerender import OGPrerenderMiddleware app = FastAPI() app.add_middleware(OGPrerenderMiddleware) # before the static handler app.include_router(meta_router, prefix="/api")
orders/create webhook, so it lands whether the sale came from the site, the
Instagram shop, or a draft order you sent by hand.from capi import MetaEvent, send_event
await send_event(request, MetaEvent(
event_name="Purchase",
event_id=f"order-{order['id']}", # stable and unique per order
value=order["total"],
currency="USD",
order_id=str(order["id"]),
content_ids=[str(i["variant_id"]) for i in order["items"]],
contents=[{"id": str(i["variant_id"]), "quantity": i["qty"], "item_price": i["price"]}
for i in order["items"]],
email=order["email"],
phone=order.get("phone"),
))/og-default.jpg.
Make one at 1200x630 and drop it in public/.Drop them in as they are. Every value that needs changing is an environment variable, so there's nothing to hunt for in the code itself.
None of it exists until the events do. That's the whole reason this goes first.

Strengthen the athlete. Sharpen the scholar. Remove every excuse.