Field test · Google Data Manager API · 3 October 2026
Google said 200 OK. An hour later it rejected all of them.
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
- A 200 from
events:ingestmeans "received", not "counted". Every upload with a bad click ID got 200 and a request ID. - The verdict came 67–73 minutes later, and only because I asked for it with
requestStatus:retrieve. Google sent no notification. - Only a request-level error fails fast. A conversion action ID that doesn't exist was rejected immediately with a 404.
- Counts arrive as strings (
"recordCount": "2"), and the status enum isFAILED, not theFAILUREthe 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.
| Upload | Case | Events | Immediate answer | Final verdict | After |
|---|---|---|---|---|---|
| 13:49:45 | Well-formed fake GCLIDs | 2 | 200 OK | FAILED · 0 kept | 70 min |
| 13:49:49 | Junk string as GCLID | 1 | 200 OK | FAILED · 0 kept | 73 min |
| 13:49:52 | With value + currency | 1 | 200 OK | FAILED · 0 kept | 67 min |
| 13:49:56 | Event 120 days old | 1 | 200 OK | FAILED · 0 kept | 68 min |
| 13:50:17 | Unknown conversion action | 1 | 404 INVALID_CONVERSION_ACTION_ID | no status (request failed) | 0 min |
Timeline of one request
POST /v1/events:ingest→ 200{"requestId": "8edf4943-…"}. Nothing in the body hints at a problem.- First
requestStatus:retrieve, 29 minutes later, as Google's guide suggests → PROCESSING. - 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 bugCLICK_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