pixellint

Blog · Snap and Meta

Snap CAPI vs Meta CAPI wants PURCHASE and WEB, on tr.snapchat.com/v3.

Snap CAPI vs Meta CAPI is a Purchase that is legal on Graph and a retired body on Snap. Meta posts event_name Purchase, event_time as ten-digit Unix seconds, action_source website, and the pixel id in the Graph path. Snap v3 posts to https://tr.snapchat.com/v3/{PIXEL_ID}/events with the access token on the query. The pixel id is in the path. event_name is PURCHASE. action_source is WEB, OFFLINE, or MOBILE_APP.

v2 put pixel_id in the JSON and posted to /v2/conversion with timestamp, event_type, and hashed_email. Snap said that shape would be deprecated in early 2025. A worker that still sends it is posting into a retired envelope. Changing the host and leaving the v2 keys is the same miss as pointing a Meta body at the v3 path.

Pixellint is not affiliated with Snap or Meta. The v3 field list is Snap's Parameters page. The Graph field list is Meta's server event parameters. This post is which of those fields survive a copy.

PURCHASE is not Purchase, and WEB is not website

Snap's event_name enum is SCREAMING_SNAKE: PURCHASE, ADD_CART, START_CHECKOUT, VIEW_CONTENT, SIGN_UP, PAGE_VIEW, and CUSTOM_EVENT_1 through CUSTOM_EVENT_5. Meta's standard name is Purchase. A shared enum that emits Purchase is not in Snap's list. If event_name is PURCHASE, currency and value are required, and they live in custom_data. A root field named price with a string, which is the v2 example, is not a v3 purchase.

action_source on Snap is WEB, OFFLINE, or MOBILE_APP. Meta's website does not appear in that enum. Pinterest spells web in lower case. Copying Meta's spelling onto Snap is a different miss from copying it onto Pinterest. event_source_url is required when the event is a website event, and it has to include a protocol. That is the page, not the API host, and it is not the v2 page_url key.

hashed_email on v2 becomes em inside user_data. ph, sc_click_id, and sc_cookie1 sit there too. A Meta fbc or a raw fbclid does not fill sc_click_id. custom_data holds currency, value, contents, and order_id. app_data holds extinfo for app events. The App path uses {SNAP_APP_ID} in place of the pixel id.

Snap accepts seconds or milliseconds, and still has two age sentences

Snap says event_time can be seconds or milliseconds and encourages milliseconds. Date.now() is already milliseconds. Math.floor(Date.now() / 1000) is the Meta helper. Both integers can be legal on Snap. A string timestamp, the v2 example, is the old type. Meta rejects a 13-digit event_time because it reads those digits as seconds, far outside the 7-day window.

The Parameters page says event_time cannot be more than 7 days in the past for web and app events. The introduction page still says events can be a maximum of 37 days back. The older v2 reference said timestamp cannot be more than 37 days in the past. Do not pick a winner between those sentences. Send a recent event_time. Do not backfill a month of v2 leftovers onto v3 and assume the 37-day sentence still applies. Meta's published cap is 7 days, and one stale event fails the batch.

Dedup is event_id plus timestamp within a 48 hour window. Send event_id on CAPI and on the Pixel. For non-purchase pixel events, event_id maps to client_dedup_id. For purchase events it can map to transaction_id. Meta dedup is event_name plus event_id plus Pixel ID, and the first arrival keeps the value. A new Snap event_id with an old timestamp is a different event from the pixel fire you think you are pairing.

The v2 body people still send

A v2-shaped conversion. v3 wants data[], event_name PURCHASE, event_time as an integer, action_source WEB, and em inside user_data.

{
  "pixel_id": "67d34640-b7a4-42a8-b821-6434d70f08a4",
  "timestamp": "1642815764",
  "event_type": "PURCHASE",
  "page_url": "https://www.example.com",
  "hashed_email": "<sha256>"
}

Validate before you cut production. Web validate is POST https://tr.snapchat.com/v3/{pixel_id}/events/validate. A VALID status there is schema. It is not Events Manager attribution. The live events path is the one that trains. Meta test_event_code is the same class of split: the row shows up in a test tab and the production dataset stays empty.

A shared worker should keep one order instant and one order id, then serialize at the edge. Snap gets PURCHASE, WEB, and the clock Snap asked for. Meta gets Purchase, website, and ten-digit seconds. The pixel id in a Graph path is not the id in a Snap path.

The token sits on the query, and validate is not the live path

Web v3 is POST https://tr.snapchat.com/v3/{PIXEL_ID}/events?access_token={TOKEN}. The access token is a query parameter. A Graph-style Authorization header plus a v2 body is a different miss from a wrong event_name. App swaps {SNAP_APP_ID} into the path. The validate URL is the events path plus /validate. A VALID status is schema. The live events path is what trains. Meta test_event_code is the same split: the test tab moves and the production dataset does not.

user_data holds em, ph, sc_click_id, and sc_cookie1. custom_data holds currency, value, contents, and order_id. app_data holds extinfo for app events. PURCHASE requires currency and value in custom_data. The v2 root fields price and hashed_email and page_url do not fill those objects. Meta custom_data.value can be copied into Snap custom_data.value only after the event_name is PURCHASE and the currency is one Snap lists.

sc_click_id is Snap's click id. Capture it from the landing URL and store it on your session. fbclid will not match it. Do not hash it. Click ids stay plaintext on Meta, Snap, TikTok, and Pinterest.

Send a recent integer, and share event_id with the pixel

Snap encourages milliseconds and also accepts seconds. Pick one unit in the serializer and keep it. A string copied from the v2 example is the old type either way. Meta must stay on ten-digit seconds. If the shared store is Date.now(), Snap can take it and Meta must floor it. If someone floors first and Snap is configured to expect milliseconds, you have the 1970-class bug on the vendor that wanted thirteen digits. Write the unit next to the edge.

The Parameters page caps web and app event_time at 7 days in the past. Another Snap page still says 37 days. Send a recent timestamp and do not use the disagreement as a backfill plan. Meta's cap is 7 days, and one stale event fails the Graph batch. A month of v2 leftovers replayed onto v3 is how you discover which Snap sentence is enforced, in production.

Dedup is event_id plus timestamp inside 48 hours. Non-purchase pixel events map event_id to client_dedup_id. Purchase events can map it to transaction_id. Use the order id for PURCHASE on both the pixel and CAPI. A new UUID on the server and transaction_id on the pixel are two purchases. Meta's first arrival then keeps whichever value landed first, for its own 48 hours, under Purchase rather than PURCHASE.

What does not transfer

Sources

Contract pages

The dated argument is above. These pages are the field lists.

Read why one CAPI JSON cannot serve every vendor Docs