DEV Community

Aleksander Sekowski
Aleksander Sekowski

Posted on

Reddit CAPI v3 Wants Milliseconds. Meta's Seconds Helper Lands in 1970.

A purchase handler posts to Reddit's Conversions API. The request returns 200. Reddit Ads stays empty, or the conversion sits in 1970.

The access token is fine. The Pixel ID in the path is fine. The payload is the Meta helper with the host changed.

{
  "data": {
    "events": [
      {
        "event_at": 1770000000,
        "action_source": "website",
        "type": { "tracking_type": "PURCHASE" },
        "user": { "email": "buyer@example.com" }
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

That JSON is legal. It is also the wrong clock and the wrong action_source enum.

event_at is milliseconds, not Meta seconds

Reddit CAPI v3 documents event_at as Unix time in milliseconds. Thirteen digits. Date.now() is already that unit.

Meta's Conversions API wants the inverse. event_time is seconds, ten digits, at most seven days old. The Meta fix is Math.floor(Date.now() / 1000). Copy that line into a Reddit worker and 1770000000 is January 1970 as milliseconds.

Reddit will still accept the POST. A 1970 conversion is older than any attribution window you care about. The status line does not mention the unit.

v2 used an ISO event_at. v3 does not. A string timestamp that used to work is a type error on the new envelope. Do not mix the two.

// Wrong on Reddit CAPI v3 (right on Meta)
event_at: Math.floor(Date.now() / 1000)

// Right on Reddit CAPI v3 (wrong on Meta)
event_at: Date.now()
Enter fullscreen mode Exit fullscreen mode

LinkedIn's conversionHappenedAt is also thirteen digits. Pinterest's event_time is ten, like Meta. There is no shared helper that is correct for all four without a branch.

action_source is WEBSITE, not website

v3 makes action_source required. Reddit documents WEBSITE, APP, PHYSICAL_STORE, and OTHER. Meta writes website. Pinterest writes web. Snap writes WEB.

"action_source": "website"
Enter fullscreen mode Exit fullscreen mode

That is Meta's spelling. On Reddit it is not in the enum. A shared action_source column without a per-vendor map is a silent invalid on at least one hop.

type.tracking_type is the event name, in UPPER_SNAKE_CASE: PAGE_VISIT, VIEW_CONTENT, SEARCH, ADD_TO_CART, ADD_TO_WISHLIST, PURCHASE, LEAD, SIGN_UP, or CUSTOM plus custom_event_name. Meta's Purchase and Pinterest's checkout are different strings. Purchase copied into tracking_type is a custom name here, not a purchase, unless you also send CUSTOM.

The pixel on alb.reddit.com/rp.gif is a different pipe. id is the advertiser ID. event is a name like PageVisit or Purchase. That image GET is not the CAPI JSON. Dedup between them is weaker than Meta's event_id pair. Mint a stable id for retries of the same Reddit CAPI event so a 500 does not become two PURCHASE rows. Replays keep the original event_at.

Hash email if you want. Do not hash the IP.

Reddit CAPI v3 allows email and phone raw or hashed. That is not permission to send a raw address to Meta, Pinterest, or TikTok. Convert at the edge.

user.ip_address and user.user_agent stay in the clear. A SHA-256 looking digest in those fields is an unmatchable event. The "hash everything in user_*" helper that made Meta's em valid is how Reddit matching dies.

"user": {
  "email": "buyer@example.com",
  "ip_address": "17af1cf3d1b5332c53349fc789abdc853bbeea7ed33eff727ff794ab741ccac9",
  "user_agent": "Mozilla/5.0"
}
Enter fullscreen mode Exit fullscreen mode

metadata.currency, when present, is an ISO 4217 code (USD), not $.

The path is /api/v3/pixels/{pixel_id}/conversion_events on ads-api.reddit.com. The Pixel ID lives in the path, not in a JSON field named pixel_id next to Meta's Graph token. Pointing a Meta client at that host with a data[] of event_name / event_time objects is a different miss from the clock bug. The envelopes do not overlap.

How you catch it before Ads Manager is empty

I maintain Pixellint, an open source linter for pixels and conversion API payloads. The Reddit CAPI pack (vendor/reddit-conversions-api) contracts data.events[] against Reddit's v3 parameter docs. This is what it prints on the seconds-and-website payload above:

$ pixellint validate json @reddit-capi.json
rulepack: vendor/reddit-conversions-api (vendor: reddit)
  error   vendor.reddit-conversions-api.body.event_at.invalid
          `event_at` must be at least 13 digits, but `1770000000` has 10.
          Reddit CAPI v3 documents `event_at` as Unix time in milliseconds.
          A 10-digit value is seconds and lands in 1970.
    fix:  Send milliseconds: a JavaScript `Date.now()` is already in the
          right unit.
  error   vendor.reddit-conversions-api.body.action_source.invalid
          `action_source` is `website`, which is not one of the documented
          values: WEBSITE, APP, PHYSICAL_STORE, OTHER.
    fix:  Send `WEBSITE`, `APP`, `PHYSICAL_STORE`, or `OTHER`.
Enter fullscreen mode Exit fullscreen mode

A hashed IP prints vendor.reddit-conversions-api.body.hashed_plaintext_field. A Reddit Pixel URL with no advertiser id prints vendor.reddit.param.id.missing on vendor/reddit, which is a different pack.

cargo install pixellint
# or: npm install pixellint
pixellint validate json @reddit-capi.json --rulepack vendor/reddit-conversions-api
Enter fullscreen mode Exit fullscreen mode

Paste the body into the playground if you do not want a local install. Artifacts you test may be stored; see privacy.

The Reddit CAPI playbook is how both pipes share a clock and an event name. The Reddit CAPI pack page is the field table. The Reddit Pixel pack is the image GET.

Where to go next

A conversion API is a POST the browser never made. What is a conversion API is the envelope, the clock, and the match keys across vendors. One conversion API JSON cannot serve every vendor is the adapter argument: store one UTC instant, convert at the edge.

Event time units maps Meta seconds, Reddit and LinkedIn milliseconds, GA4 microseconds, and TikTok ISO 8601 so you are not guessing from one vendor's sample. Pinterest is seconds again, with lowercase web and lowercase standard events (checkout, not Checkout); that contrast lives in the Pinterest standard events playbook.

The short version

Reddit CAPI v3 event_at is Date.now() in milliseconds, not Meta's seconds. action_source is WEBSITE. tracking_type is PURCHASE, not Purchase. Email may be raw or hashed. IP and user-agent must not be digests. The Pixel ID is in the path. Do not POST Graph-shaped JSON to ads-api.reddit.com.

Pixellint is independent of Reddit. The rule ids above cite Reddit's Conversions API docs because that is where the requirements live, not because this is an official tool.

Top comments (0)