Conversion tracking

Rankings and clicks say an article was found. Conversions say it earned something. There are two ways to report them, and most sites end up using both: one line in the browser for what a visitor did, and a call from your own server for what your business actually accepted.

1 · The browser line

Add this once, before </head>, on every page — the shared layout template, the WordPress theme header, or a header-scripts plugin. Your project id is shown in the dashboard under Settings → Conversion tracking, which also has a button that checks the install.

<script async src="https://app.serp-agent.com/api/tracker/script" data-project="YOUR_PROJECT_ID"></script>

It watches for form submissions, tel: and mailto: clicks, and buttons you mark with data-sa-cta:

<a href="/get-a-quote" data-sa-cta="hero-quote">Get a quote</a>

The page a visit started on is remembered for the session, so a lead sent from your contact page is still credited to the article that brought the visitor. Bots and repeated clicks are filtered out.

2 · Accepted leads from your server

Optional. The browser line above is enough on its own: a submitted form, a booked call, a tapped phone number and a clicked email address all count as results. Reporting from your server adds one thing — certainty that a lead was accepted rather than spam, a duplicate or a test. If your form backend, CRM or booking system knows that, report it with an API key, which you create in Settings → SERP Agent API keys. The key is shown once, so copy it straight into your server's environment; it belongs to the account that owns the project.

curl -X POST https://serp-agent.com/v1/projects/{projectId}/conversions \
  -H "Authorization: Bearer $SERP_AGENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "eventType": "FORM_SUBMIT",
    "externalId": "lead_88421",
    "pageUrl": "https://example.com/contact",
    "landingUrl": "https://example.com/blog/how-to-choose-movers",
    "occurredAt": "2026-09-18T09:12:04Z"
  }'
{ "recorded": true, "duplicate": false, "article_attributed": true, "attribution": "landing" }

Send externalId — your own lead id. A repeat with the same id is ignored and answered 200 with duplicate: true, so a retry or a re-delivered webhook can never inflate your numbers. Full field list, errors and rate limits: POST /v1/projects/{projectId}/conversions.

Event types

FORM_SUBMIT
A contact or quote form was submitted. From the server: the lead was accepted.
CALENDAR_BOOKED
A call or visit was booked.
PHONE_CLICK
A tel: link was tapped — on a phone, that is the call being placed.
EMAIL_CLICK
A mailto: link was clicked.
CTA_CLICK
A tracked call-to-action button was clicked. This one is navigation inside your site, so it counts as interest rather than a result.

Which article gets the credit

One article is credited per conversion, by the first signal available: an explicit articleUrl, then ctaTrackingId, then pageUrl, then landingUrl. The response tells you which one was used (attribution). A conversion that matches no article is still stored and counted for the project — it just does not name an article.

What is never sent

No cookies, no cross-site identifiers, no fingerprinting. The event type, the pages involved and your own lead id are the whole payload. Do not put names, emails, phone numbers or message bodies in any field, including externalId.

Checking that it works

  • Dashboard → Settings → Conversion tracking → Check installation. It fetches your pages the way a visitor would and names the pages it read.
  • If the line is added through a tag manager, or your pages are served from cache, the check may not see it in the HTML. Arriving events are the stronger proof, and the panel says so when it has them.
  • The same panel shows events from the last 7 days and how many were credited to an article. Server reports answer immediately: recorded and attribution tell you what was stored.