pixellint

Time · engineers

Seconds, milliseconds, microseconds, ISO. Pick one.

There is no shared timestamp field across these APIs, only a unit per vendor. Copying Date.now() into every client is how one pipe lands in 1970 and another lands in the year 56000.

Count the digits

Unix seconds around 2026 are ten digits (1.7 billion). Unix milliseconds are thirteen digits (1.7 trillion). Unix microseconds are sixteen digits. ISO 8601 is a string, not a digit count. If you remember nothing else: count the digits before you POST.

Meta CAPI event_time is Unix time in seconds, exactly 10 digits. Date.now() is milliseconds, 13 digits. Send Math.floor(Date.now() / 1000). A 13-digit value is tens of thousands of years in the future, outside the attribution window, and does not count. Meta documents a maximum age of about 7 days when the event arrives. Pixellint flags the digit miss as vendor.meta-conversions-api.body.event_time.invalid.

GA4 Measurement Protocol timestamp_micros is microseconds, exactly 16 digits. Thirteen digits is milliseconds. Ten digits is seconds. Pixellint flags that as vendor.google-analytics.body.timestamp_micros.invalid. Multiply Date.now() by 1000, or a 10-digit seconds value by 1_000_000. Google documents that you can backdate roughly 72 hours. A 16-digit value from last month is the right unit and still too old for MP to apply as event time.

Milliseconds are correct on several ads APIs

Amplitude HTTP V2 time is milliseconds since epoch, 13 digits. Date.now() is already right. A 10-digit seconds value is January 1970 as milliseconds, which Amplitude will store as a prehistoric event. Pixellint: vendor.amplitude.body.time.invalid. user_id and device_id still need 5 characters. The clock and the id fail independently.

LinkedIn CAPI conversionHappenedAt is the same millisecond clock, 13 digits. LinkedIn rejects events older than 90 days. A seconds value looks older than 90 days because it reads as 1970. Do not divide Date.now() by 1000 because the Meta snippet did. Pixellint: vendor.linkedin-conversions-api.body.conversionHappenedAt.invalid.

Reddit CAPI v3 event_at is 13 digits milliseconds. The v2 envelope used ISO 8601 event_at and is a different shape. Do not send 10-digit seconds on v3. Pixellint: vendor.reddit-conversions-api.body.event_at.invalid. OpenAI conversion timestamp_ms is also 13 digits. Same Date.now(), different field name.

Seconds again: Pinterest. Epoch: Snap.

Pinterest CAPI event_time is Unix seconds, like Meta. Exactly 10 digits. A 13-digit value is invalid. Pixellint: vendor.pinterest-conversions-api.body.event_time.invalid. Copying an Amplitude body onto Pinterest without dividing by 1000 is the usual miss.

Snap event_time is an epoch timestamp. Snap only accepts events from the last 7 days. The pack checks the field as an integer (vendor.snapchat.body.event_time.invalid) and does not invent a digit-length rule beyond that integer check. Send the conversion's epoch, not the forwarder's. Pixellint is not affiliated with Snap. Read Snap's parameter doc for the unit they currently publish, then keep the worker honest about event time versus drain time.

ISO 8601 is a different type

PostHog capture timestamp, Segment timestamp, TikTok Events API timestamp, and Braze events[].time want ISO 8601 strings such as 2026-07-26T06:00:00Z. An epoch number is a different type. On PostHog and TikTok it is accepted and then treated as arrival time, so a backfill looks like it happened just now. Pixellint: vendor.posthog.body.timestamp.invalid, vendor.tiktok-events-api.body.timestamp.invalid, vendor.segment.body.timestamp.invalid, vendor.braze.body.time.invalid.

Adjust created_at is ISO 8601 when present (created_at_unix is the 10-digit seconds alternative). AppsFlyer eventTime is UTC as yyyy-mm-dd hh:mm:ss.sss with a space, not a T. An epoch on AppsFlyer is stamped as arrival time. Those two MMP clocks are not Date.now() and not Meta seconds.

Convert at the edge, not in a shared column

Do not reuse one warehouse column named timestamp for Meta, Amplitude, and PostHog. Store an instant (UTC) once. Convert at the vendor adapter: floor(ms/1000) for Meta and Pinterest, ms for Amplitude, LinkedIn, and Reddit v3, ms*1000 for GA4, toISOString() for PostHog, Segment, TikTok, and Braze, and the AppsFlyer space-separated UTC form for AppsFlyer.

A shared JSON field event_time copied between Meta and LinkedIn will always be wrong on one of them. Name the outbound fields after the vendor. Run pixellint validate json on each body. Pixellint is not affiliated with Meta, Google, Amplitude, LinkedIn, TikTok, Pinterest, Reddit, Snap, PostHog, Segment, Braze, Adjust, or AppsFlyer.

const ms = Date.now(); // 13 digits, milliseconds

// WRONG: Meta CAPI (wants 10-digit seconds)
event_time: Date.now()

// RIGHT: Meta CAPI / Pinterest CAPI
event_time: Math.floor(Date.now() / 1000)

// WRONG: GA4 MP (wants 16-digit microseconds)
timestamp_micros: Date.now()

// RIGHT: GA4
timestamp_micros: Date.now() * 1000

// RIGHT: Amplitude time / LinkedIn conversionHappenedAt / Reddit v3 event_at
time: Date.now()

// WRONG: PostHog / TikTok / Segment / Braze (want ISO 8601)
timestamp: Date.now()

// RIGHT: ISO 8601
timestamp: new Date(ms).toISOString()

Check the artifact

Paste the pixel URL or JSON body into the playground. Same engine as pixellint validate. Nothing leaves the tab.