A Rakuten (LinkShare) conversion hit is one long line of percent signs, pipes and digits. The hit holds exactly what the network received about the order: which products, how many, and for how much.
This guide shows how to read one in about 30 seconds, using a real order hit with the brand, product names and order number replaced by placeholders. It is written for tracking and analytics consultants and for e-commerce teams who have to answer the question “why does the affiliate report not match our basket?”.
Why the URL looks unreadable: percent-encoding, not HTML entities
The hit is a URL, so its special characters are percent-encoded. Each byte that cannot appear as is becomes a percent sign followed by two hexadecimal digits. MDN describes encodeURIComponent() as replacing characters with one to four escape sequences that represent their UTF-8 encoding, and decodeURIComponent() reverses it.
HTML entities are a different mechanism with a different job. They exist inside HTML documents and start with an ampersand: a named reference such as & or a numeric one such as '. The same character has a different spelling in each system.
| What you see | Where it lives | What it means |
|---|---|---|
%20 | URL (percent-encoding) | a space (byte 0x20) |
%3E | URL | the character > |
%27 | URL | an apostrophe |
%C2%A0 | URL | a non-breaking space: two UTF-8 bytes, C2 and A0 |
& | HTML (named reference) | an ampersand |
' | HTML (numeric reference) | an apostrophe, written as its code point |
| HTML (named reference) | a non-breaking space |
One more detail matters for pixel URLs. URLSearchParams reads a plus sign in a query string as a space, so a value containing a real plus sign has to be sent as %2B. And decodeURIComponent() throws a URIError when a percent sign is not followed by two hexadecimal digits, so a truncated hit can break a decoder.
Step 1: split the query parameters
Cut the URL at the question mark, then at each ampersand. The hit in this example goes to track.linksynergy.com/ep and carries these parameters:
mid=37335,ord=ORDER_12345(the order ID, replaced here),landandtr: identification and context fields. Rakuten’s expected order starts with mid, ord, tr and land, according to MetaRouter.cur=GBP: the currency.discount=18.72anddisamt=18.72: the discount.ordstatus=new,source=scat,img=1,spi=3.4.1: other flags that do not change how the lists are read.- Six pipe-separated lists:
skulist,qlist,amtlist,namelist,catlist,brandlist.
Tealium’s guide says the Rakuten library builds skulist, namelist, qlist and amtlist from the order’s line items. Each product therefore appears at the same position in every list, which is what makes the alignment in step 2 possible.
Step 2: align the lists into one row per product
The first value of every list belongs to the first product, the second value to the second product, and so on. The script below does the alignment. It splits each list on the pipe character before decoding, so that an encoded pipe inside a product name does not create a false column. It then reports any list with a different length and any empty amount.
function decodeHit(hitUrl) {
const dec = (s) => decodeURIComponent(s.replace(/\+/g, " "));
const raw = Object.fromEntries(
new URL(hitUrl).search.slice(1).split("&").map((pair) => {
const i = pair.indexOf("=");
return [pair.slice(0, i), pair.slice(i + 1)];
})
);
const listKeys = ["skulist", "qlist", "amtlist", "namelist", "catlist", "brandlist"];
const columns = {};
for (const k of listKeys) {
columns[k] = (raw[k] ?? "").split("|").map(dec);
}
const n = Math.max(...Object.values(columns).map((c) => c.length));
const rows = Array.from({ length: n }, (_, i) =>
Object.fromEntries(listKeys.map((k) => [k, columns[k][i] ?? ""]))
);
const issues = [];
for (const k of listKeys) {
if (columns[k].length !== n) issues.push(`${k}: ${columns[k].length} values, expected ${n}`);
}
rows.forEach((r, i) => {
if (r.amtlist === "") issues.push(`row ${i + 1} (${r.skulist}): empty amtlist`);
});
return { rows, issues };
}
const { rows, issues } = decodeHit(location.href); // or paste the hit URL as a string
console.table(rows);
console.log(issues);
Run on the example hit, it gives this table:
| # | skulist | qlist | amtlist | namelist | catlist | brandlist |
|---|---|---|---|---|---|---|
| 1 | 80098955 | 2 | 1544 | Product A | Category A | Brand |
| 2 | 80081183 | 1 | 550 | Product B | Category A | Brand |
| 3 | 80082077 | 1 | (empty) | Product C | Samples | Brand |
| 4 | 80083079 | 1 | (empty) | Product D | Samples | Brand |
| 5 | 80124257 | 1 | (empty) | Product E | Samples | Brand |
| 6 | 80104514 | 1 | 1540 | Product F | Category A | Brand |
What the table shows
The order has six lines, but amtlist is empty for three of them (rows 3 to 5), and those three lines sit in the category “Samples”. They are most likely free samples added to the basket at a price of zero. Two things follow from that.
First, the network never receives an amount for those lines. That may be exactly what you want, but it should be a decision, not an accident of how the tag reads the basket.
Second, the amounts are not in the units you see in the shop. MetaRouter’s Rakuten documentation states that amtlist is expressed in cents, multiplied by 100 for most currencies and not for Japanese yen. On that basis the three priced lines add up to 1544 + 550 + 1540 = 3634, or 36.34 GBP, while the discount fields read 18.72. I could not find a Rakuten page that states the unit or explains how empty entries are treated, so confirm both with your Rakuten account manager before you rely on them. Also check whether 1544 on row 1, which has a quantity of 2, is a line total or a unit price.
MetaRouter also documents a convention where a discount travels as an extra line with the SKU “Discount”. This hit sends the discount in separate parameters instead, with no such line. Whether that is correct for your program is a question for the network.
A checklist for any affiliate conversion hit
| Check | What to look for |
|---|---|
| List lengths | All six lists have the same number of values as skulist. |
| Empty amounts | Every empty amtlist slot has a known reason (free sample, gift, bundle component). |
| Quantity and amount | The amount is the line total or the unit price, consistently, on every row. |
| Totals | The sum of amtlist, minus the discount, matches the order total in your back office. |
| Units | amtlist and the discount parameters use the units the documentation expects for the currency. |
| Encoding | No value shows %25 followed by two hex digits, which would mean it was encoded twice. |
Audit it on your own site
Open your browser’s network panel, filter on the network’s domain, place a test order that includes a free item and copy the request URL. Run the script on it and compare the output with the order confirmation. Repeat with a discount code, with a multi-quantity line and with a product whose name contains an accent or an apostrophe, since those are the cases where encoding goes wrong.
If you would rather not paste code, the free URL decoder on this site splits and decodes a hit in the browser.