X (Twitter) Ads CAPI (Server-Side)
X’s Conversions API (still commonly called “Twitter CAPI” in older docs) is the most painful of the major-platform CAPIs to implement. OAuth 1.0a request signing, confusing field naming (where event_id means something different than on every other platform), and lower rate limits than competitors. That said, if you are spending meaningfully on X ads — particularly B2B, developer tools, or crypto — CAPI recovers conversions the client-side pixel loses on Safari and to blockers.
Valid as of April 2026, X Ads API v12.
Why server-side for X
Section titled “Why server-side for X”X’s audience skews heavily toward desktop Safari and privacy-conscious mobile users — both cohorts where the client-side pixel underreports. Add in the general click-ID volatility that affects every platform (URL parameter stripping by corporate proxies, in-app browsers, etc.) and you have meaningful lift potential.
When it is not worth it: if X is less than 5% of your paid spend, the OAuth 1.0a signing effort is disproportionate. Start with pixel only, add CAPI later if spend grows. For everyone else, CAPI is worth it but expect a full day of implementation effort even for engineers who have shipped Meta CAPI before.
The event model
Section titled “The event model”X’s CAPI is structured around conversion events you configure in Ads Manager. Each conversion event (e.g., “Website Purchase”) has its own UUID. You POST to the pixel endpoint with that conversion event UUID in the event_id field — which is the single most confusing part of X’s API.
Required per event:
| Field | Notes |
|---|---|
conversion_time | ISO 8601 timestamp |
event_id | UUID of the Ads Manager conversion event (not a dedup key) |
conversion_id | Your per-event dedup key |
identifiers[] | Array with at least one of twclid, hashed_email, hashed_phone_number |
Supported conversion event categories in Ads Manager: Site Visit, Page View, Purchase, Lead, Sign Up, Download, Custom. Each category you use requires a corresponding conversion event record in Ads Manager.
Match keys:
| Field | Hashed? |
|---|---|
hashed_email | SHA-256 (lowercase, trimmed) |
hashed_phone_number | SHA-256 (E.164 first) |
twclid | No (click identifier from URL) |
Setup in sGTM
Section titled “Setup in sGTM”-
Create an X developer app at developer.x.com. Apply for the Ads API product tier — this requires manual review and can take several days. You cannot call the X CAPI endpoints without Ads API approval.
-
Generate OAuth 1.0a credentials:
- Consumer key + consumer secret (app-level)
- Access token + access token secret (user-level, with Ads API write scope)
- Store all four as sGTM Constant variables
-
In Ads Manager → Conversions → your pixel, create a conversion event (e.g., “Website Purchase”). Note the UUID — this is what goes in the
event_idfield of your CAPI requests. -
In sGTM, create a Custom Template. OAuth 1.0a signing is the hard part — the sGTM sandbox supports
hmacSha256,sha256Sync, andgenerateRandombut you will need to implement the OAuth 1.0a signing base string and signature manually, or use a community template that wraps this. -
Configure the tag with:
- Pixel ID and Conversion Event UUID (one per event type you are firing)
- OAuth credentials (all four)
- Conversion ID:
{{Event Data - event_id}} - twclid: from a Cookie Variable (
_twclid, set on landing from the URL param)
-
Firing trigger on the relevant GA4 events.
Raw HTTP shape:
POST https://ads-api.twitter.com/12/measurement/conversions/<pixel_id>Authorization: OAuth oauth_consumer_key="...", oauth_nonce="...", oauth_signature="...", oauth_signature_method="HMAC-SHA1", oauth_timestamp="...", oauth_token="...", oauth_version="1.0"Content-Type: application/json{ "conversions": [{ "conversion_time": "2026-04-20T12:34:56.000Z", "event_id": "<conversion_event_uuid_from_ads_manager>", "identifiers": [ {"twclid": "<twclid value>"}, {"hashed_email": "<sha256 of email>"}, {"hashed_phone_number": "<sha256 of phone>"} ], "conversion_id": "evt-ORDER-123-1713571200", "conversion_value": "79.99", "conversion_currency": "USD", "number_items": 2, "content_ids": ["SKU-A", "SKU-B"], "content_type": "product" }]}Event mapping from the X Pixel
Section titled “Event mapping from the X Pixel”| GA4 event | X conversion category | Notes |
|---|---|---|
page_view | Site Visit / Page View | Optional server-side |
view_item | Page View (custom) | |
add_to_cart | Custom | X has no native AddToCart — create a Custom event |
begin_checkout | Custom (Checkout) | |
purchase | Purchase | Carry order_id, value, currency |
sign_up | Sign Up | |
generate_lead | Lead | |
file_download | Download |
Each row maps to a separate conversion event record in Ads Manager, each with its own UUID that goes in the event_id field.
Deduplication with the X Pixel
Section titled “Deduplication with the X Pixel”X dedups by conversion_id plus event_id (conversion event UUID) within a 24-hour window. Both the client-side pixel and the server-side CAPI must send the same conversion_id against the same conversion event UUID for dedup to work.
Client-side (X Pixel): pass conversion_id in the twq() options:
twq('event', 'tw-<pixel_id>-<conversion_event_uuid>', { value: 79.99, currency: 'USD', conversion_id: 'evt-ORDER-123-1713571200', email_address: '<sha256 of email>'});sGTM CAPI: map event.event_id from the sGTM Event Model to the CAPI tag’s conversion_id field (remembering that event_id on the wire is the Ads Manager conversion UUID, not your event’s dedup key).
Testing and validation
Section titled “Testing and validation”X Ads Manager → Conversions → your pixel → Events shows inbound events with a source breakdown. Latency: 5–15 minutes.
X does not have a test-events mode equivalent to Meta’s Test Event Code. Your options:
- Use a dedicated test pixel/conversion event in a test Ads account.
- Monitor the Events panel with timestamp filtering during test windows.
- Watch sGTM Preview mode for the HTTP 200 response from the CAPI endpoint.
Common response codes: 401 (OAuth signing wrong — almost always the problem), 403 (Ads API not approved for your app), 429 (rate limit hit — X’s limits are a few hundred requests per second per app).
Common mistakes
Section titled “Common mistakes”OAuth 1.0a signing errors
Section titled “OAuth 1.0a signing errors”By far the most common failure mode. OAuth 1.0a requires a carefully constructed base string (HTTP method + URL + sorted params) and HMAC-SHA1 signature. A single character off and every request is 401. Use a vetted community template or a reference implementation — do not write the signing logic from scratch unless you enjoy pain.
Confusing event_id and conversion_id
Section titled “Confusing event_id and conversion_id”On X, event_id is the Ads Manager conversion event UUID (static, one per conversion type). conversion_id is the per-event dedup key (dynamic, unique per firing). Every other major platform uses event_id as the dedup key. Label your sGTM variables clearly — e.g., X - Purchase Event UUID vs. DL - event_id (dedup) — to avoid mixing them up.
Missing Ads API approval
Section titled “Missing Ads API approval”You cannot call X CAPI endpoints without the Ads API product tier on your developer account. If your test requests return 403 before you even get to OAuth signing debates, that is the cause. Apply early.
Sending value_micros inconsistently
Section titled “Sending value_micros inconsistently”If half your events send conversion_value: "79.99" and the other half send value_micros: 79990000, your reports show mismatched values and campaign optimization degrades. Pick conversion_value (the current recommendation) and stick with it across all events.
Rate limit blindness
Section titled “Rate limit blindness”X’s CAPI rate limits are much lower than Meta’s. A Black Friday sale with 500 purchases/minute will hit the limit. Batch events on the server side if volume exceeds a few hundred per minute, or contact X for a quota increase before a major campaign.