Convwatch

Field test · Google Data Manager API · 3 October 2026

Google said 200 OK. An hour later it rejected all of them.

Viktor Shataev · developer · tested on a live Google Ads account, not a test account

Since 15 June 2026, offline click conversions go to Google Ads through the Data Manager API. I sent five conversions that should fail and watched what Google said, and when.

What I learned

  1. A 200 from events:ingest means "received", not "counted". Every upload with a bad click ID got 200 and a request ID.
  2. The verdict came 67–73 minutes later, and only because I asked for it with requestStatus:retrieve. Google sent no notification.
  3. Only a request-level error fails fast. A conversion action ID that doesn't exist was rejected immediately with a 404.
  4. Counts arrive as strings ("recordCount": "2"), and the status enum is FAILED, not the FAILURE the guide's prose uses.

What I sent

One conversion action of type Import from clicks. Four upload requests, five events in total, each with a click ID that Google could not match to a real ad click: two well-formed fake GCLIDs, one plain junk string, one with a conversion value and currency, one dated 120 days back. A fifth request pointed at a conversion action that doesn't exist.

Sent vs kepttimes UTC
UploadCaseEventsImmediate answerFinal verdictAfter
13:49:45Well-formed fake GCLIDs2200 OKFAILED · 0 kept70 min
13:49:49Junk string as GCLID1200 OKFAILED · 0 kept73 min
13:49:52With value + currency1200 OKFAILED · 0 kept67 min
13:49:56Event 120 days old1200 OKFAILED · 0 kept68 min
13:50:17Unknown conversion action1404 INVALID_CONVERSION_ACTION_IDno status (request failed)0 min
All four accepted requests failed with PROCESSING_ERROR_REASON_INVALID_GCLID, including the 120-day-old event: the click ID is checked before the date.

Timeline of one request

  1. POST /v1/events:ingest → 200 {"requestId": "8edf4943-…"}. Nothing in the body hints at a problem.
  2. First requestStatus:retrieve, 29 minutes later, as Google's guide suggests → PROCESSING.
  3. Second check → FAILED, 2 of 2 events rejected, reason INVALID_GCLID.

The responses, verbatim

The accepted upload:

POST https://datamanager.googleapis.com/v1/events:ingest → 200
{"requestId": "8edf4943-0aa7-458e-aede-bc545357857f"}

The same request, an hour later:

GET /v1/requestStatus:retrieve?requestId=8edf4943-… → 200
{"requestStatusPerDestination": [{
  "destination": {"operatingAccount": {"accountType": "GOOGLE_ADS", "accountId": "…"},
                  "productDestinationId": "…"},
  "requestStatus": "FAILED",
  "errorInfo": {"errorCounts": [{"recordCount": "2",
                                 "reason": "PROCESSING_ERROR_REASON_INVALID_GCLID"}]},
  "eventsIngestionStatus": {"recordCount": "2"}
}]}

The one that failed fast, which carries a request ID you can't look up later:

POST /v1/events:ingest → 404
{"error": {"code": 404, "status": "NOT_FOUND",
  "message": "Conversion action ID is not valid.",
  "details": [
    {"@type": "…ErrorInfo", "reason": "INVALID_CONVERSION_ACTION_ID",
     "metadata": {"requestId": "t-cbb4c79b-…"}},
    {"@type": "…BadRequest", "fieldViolations": [{
       "field": "destinations[0].product_destination_id",
       "reason": "INVALID_CONVERSION_ACTION_ID"}]}]}}

Check your own uploads

If your backend uploads offline conversions, keep the requestId from every successful ingest and ask for its status from 30 minutes on. Google's guide suggests backing off ×1.3 up to an hour and giving up after 24 hours.

# token for the same account that uploaded, with the datamanager scope
curl -s "https://datamanager.googleapis.com/v1/requestStatus:retrieve?requestId=$REQUEST_ID" \
  -H "Authorization: Bearer $TOKEN" \
  -H "x-goog-user-project: $GCP_PROJECT"

Look at requestStatus per destination, then errorInfo.errorCounts for how many events were dropped and why. Status can't be retrieved for a request that failed outright or was sent with validateOnly.

Reasons worth alerting on

From Google's ProcessingErrorReason list, the ones that point at a problem on your side rather than a lead that never clicked an ad:

…_INVALID_GCLIDClick ID can't be decoded: truncated, re-cased or not a GCLID
…_EVENT_TOO_OLDConversion is past the action's window
…_DENIED_CONSENT · …_NO_CONSENTConsent missing or denied for ad user data
…_DUPLICATE_GCLID · …_DUPLICATE_TRANSACTION_IDRe-sending the same conversions
…_INVALID_OPERATING_ACCOUNT_FOR_CLICKUploading to the wrong account
…_CONVERSION_PRECEDES_CLICKTimestamp or time-zone bug

CLICK_NOT_FOUND on its own is often normal for leads that came from organic traffic. Watch its rate, not its presence.

One oddity

With validateOnly: true, a request carrying conversionValue and currency was refused with destination_references: Resource not found. The identical request without validateOnly returned 200. Dropping the value fields made validateOnly pass. I saw this on one account and don't know the cause; if you rely on validateOnly in CI, check it against a real upload.

How do you catch this today?

I'm building Convwatch, a small monitor for exactly these failures, and talking to people who upload conversions from their own backend or CRM. If an upload broke for you recently, I'd like to hear how you found out. 20 minutes, no pitch. Or join the waitlist.

hello@convwatch.com