Measurement and Conversions API
See what your billboard did: views, people reached, saves, “Route there” taps and visits. Tell us about sales with offer codes or the Conversions API.
On this page
Your dashboard
Open Your ads in the app and choose a campaign. Results open after your first payment, by day or by hour:
| Measure | What it counts |
|---|---|
| Views | Times your ad was on screen, still and video separately |
| People reached | Different phones that saw it, and how many times each |
| Video watched | How many plays reached 25%, 50%, 75% and the end |
| Saves, Route there, Learn more | Taps on each button |
| Visits | People who arrived at your building after seeing the ad (see below) |
| Conversions | Sales, leads and other results you report, with their value |
| Cost per visit and per sale, return on spend | Worked out from what you paid |
| Where it works best | The situations (time, how people travel) where your goal happens most |
Download any report as CSV. Reports show totals only, never who saw your ad, and hide any group smaller than five people.
Visits
A visit is counted when a phone that saw your ad, or tapped Route there, arrives at your building within your campaign’s window: 7 days unless you change it, from 1 to 28. One visit per phone per day.
Offer codes
Add an offer to your campaign and people who see your ad can tap Get offer for a code like NW-4F2K. When a customer shows it at your counter, type it into Confirm a customer in your dashboard, as a visit or a sale with its amount, and it counts.
Learn more links
A Learn more tap goes through N-Way, which counts it, then opens your website with these added to the address:
| Parameter | Value |
|---|---|
utm_source | nways |
utm_medium | billboard |
utm_campaign | Your campaign’s number |
nwclid | The click ID, like nw.K7Q2…. Keep it with the order, so you can report the sale. |
https://shop.example.ng/menu?nwclid=nw.K7Q2M4XJ3F5RB2LTQWE6HNPAZD3YU&utm_campaign=1042&utm_medium=billboard&utm_source=nwaysConversions API
Your server tells N-Way when a click led to a sale, a lead or another result. It links to a click ID or an offer code only, never to a person.
| Endpoint | POST https://nways.ng/v1/ads/conversions |
| Auth | Authorization: Bearer nwk_… (an API key) |
| Body | JSON, up to 8 KB |
| Rate limit | 600 conversions a minute per key |
API keys
In the app, open Your ads, then Developers, give the key a name and create it. The whole key, starting nwk_, is shown once: copy it to your server then. The list shows each key’s first characters and when it was last used. Revoke a key there at any time; requests with it stop working at once.
Keep keys on your server. Never put one in a web page or an app, where anyone can read it.
Send a conversion
curl https://nways.ng/v1/ads/conversions \
-H "Authorization: Bearer $NWAYS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"nwclid":"nw.K7Q2M4XJ3F5RB2LTQWE6HNPAZD3YU","event":"purchase","value_naira":2500,
"occurred_at":"2026-10-12T14:05:00+01:00","dedupe_id":"order-1042"}'// Node 18 or later
const res = await fetch('https://nways.ng/v1/ads/conversions', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NWAYS_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
nwclid: order.nwclid, // saved from the landing page address
event: 'purchase',
value_naira: 2500,
occurred_at: new Date().toISOString(),
dedupe_id: `order-${order.id}`,
}),
})
if (!res.ok) throw new Error((await res.json()).error)import os
import requests
res = requests.post(
"https://nways.ng/v1/ads/conversions",
headers={"Authorization": f"Bearer {os.environ['NWAYS_API_KEY']}"},
json={
"offer_code": "NW-4F2K", # or "nwclid"
"event": "visit",
"dedupe_id": "till-3-2026-10-12-0931",
},
timeout=10,
)
res.raise_for_status()Fields
| Field | Type | Description |
|---|---|---|
nwclid | string | The click ID from a Learn more link, Route there or Save. Send this or offer_code. |
offer_code | string | A code a customer showed, like NW-4F2K. Used when there’s no nwclid. |
event | string, required | visit, purchase, lead, signup, call or custom |
dedupe_id | string, required | Your own ID for this conversion, such as an order number: letters, digits and . _ : -, up to 100. The same ID is counted once, so resending is safe. |
value_naira | number | Optional. The value in naira, from 0 to 1,000,000,000. |
occurred_at | string or number | Optional. When it happened: RFC 3339 (2026-10-12T14:05:00+01:00) or Unix seconds. Defaults to now. |
A conversion counts if it happened within your campaign’s window after the click (7 days unless you changed it, up to 28), and not in the future.
No personal data. Requests with any of these fields are refused: email, phone, phone_number, name, first_name, last_name, full_name, address, user_id, customer_id, dob, birthday, ip, ip_address, device_id, bvn, nin.
Responses
{"status": "recorded"}| Status | Body | Meaning |
|---|---|---|
| 200 | {"status":"recorded"} | Counted. |
| 200 | {"status":"duplicate"} | This dedupe_id was already counted. Nothing changed. |
| 400 | {"error":"…"} | A field is missing or wrong, or personal data was sent. The message says which. |
| 401 | {"error":"…"} | No API key, or it’s unknown or revoked. |
| 404 | {"error":"Unknown click id or offer code."} | Not one of your campaign’s clicks or codes. |
| 422 | {"error":"…"} | Outside the campaign’s attribution window. |
| 429 | {"error":"…"} | Over 600 a minute for this key. Wait and retry. |
Retry 429 and network errors with the same dedupe_id: a conversion is never counted twice.