Analytics JavaScript API

On this page

This page lists every command the Quillly analytics tag accepts through qa() and every data-qa-* attribute it reads, with the rules the collector applies to each. It is the lookup table for the analytics JavaScript API, and it ends with the one server call, recording a conversion with an API key. For the steps, see Install analytics on your website and Track goals and conversions.

A goal sent with qa() in JavaScript and with data-qa-goal in HTML, both recorded by Quillly as the same signup goal with its metadata

How the analytics JavaScript API loads#

The analytics JavaScript API is the qa function and the attributes on this page. Both come with the two lines of the tag, and the Cookie tag (/qa.js) and the Cookieless tag (/qa.cookieless.js) accept exactly the same commands.

html
<script>window.qa=window.qa||function(){(window.qa.q=window.qa.q||[]).push(arguments)}</script>
<script async src="https://quillly.com/qa.js" data-website="YOUR_WEBSITE_ID"></script>
  • The first line creates qa at once and queues every call made before the script loads. The script sends the queue when it starts.

  • Without the first line, a call made before the script loads fails with qa is not defined.

  • If the script never loads, for example because an ad blocker stops it, queued calls are never sent. Your page keeps working.

  • qa() returns nothing and never throws. A command it doesn't know, or a call that breaks a rule below, is ignored without an error.

What happens to a qa() goal call: it waits in the queue until the tag loads, then Quillly checks it and stores it as one event

qa() commands#

Table

Command

Arguments

What it records

qa('goal', name, metadata)

A goal name, then an optional object

One completion of that goal

qa('event', name, metadata)

The same as goal

The same as goal

qa('identify', person)

An object with a userId or an email

Who the visitor is on your side

There is no command to send a page view, change the page path or switch the website ID. The tag records page views by itself; see Events the tag sends by itself below.

qa('goal')#

Send a goal when the action succeeds, such as after a signup form returns.

js
qa('goal', 'signup', { plan: 'pro', seats: 3 });
Table 2

Part

Rule

When a call breaks the rule

Name

1 to 64 lowercase letters, digits, - or _. Capital letters are lowercased first.

The call is dropped

Name starting with qy_

Reserved for the goals of pages Quillly serves

The call is dropped

Metadata

A plain object with up to 10 keys

Keys after the tenth are dropped

Metadata keys

1 to 32 letters, digits or _. A - becomes _.

That key is dropped

Metadata values

Text up to 255 characters, a number, true or false

Longer text is cut; other values are dropped

Different names per site

Up to 500

A new name past 500 is dropped; names already sent keep counting

A goal appears in the Goal tab the first time it arrives, so there is nothing to create first. Each call counts as one event toward your monthly allowance.

Open Analytics

qa('event')#

event is another word for goal: the call, the rules and the result are the same.

js
qa('event', 'signup', { plan: 'pro' });

qa('identify')#

Call it when a person signs up or logs in, so the Users list shows who they are.

js
qa('identify', {
  userId: 'u_4821',
  email: '[email protected]',
  name: 'Maya',
  plan: 'pro',
});
Table 3

Field

Type

Limit

What it does

userId

Text or a number

128 characters

Your own ID for the person

email

Text

254 characters

The person's email address

name

Text

120 characters

The name the Users list shows

revenue

A number above 0

1,000,000,000 per call

Adds to the person's revenue total and marks them as a Customer

isCustomer

true

None

Marks the person as a Customer

Any other key

Text, a number, true or false

10 keys of 1 to 32 letters, digits or _; text up to 200 characters

Stored with the person

  • userId or email is required. A call with neither sends nothing, and Quillly drops a call whose userId and email hold only spaces.

  • Later calls add to earlier ones. A new field is added, a field you send again replaces its old value, and revenue adds up.

  • Keys with a - are dropped. Unlike goal metadata, identify doesn't turn - into _.

  • Each call is one event toward your allowance. It doesn't count as the visitor doing something, so it never changes the bounce rate.

Warning

Every revenue value adds to the person's total. Send it once for each payment, not on every page load.

data-qa-* attributes#

On the tag's script element#

Table 4

Attribute

What it does

data-website

Required. Your website's ID. Without it, the tag does nothing.

data-qa-exclude

A comma-separated list of path prefixes where the tag sends no page views, time on page or Web Vitals. Goals and identify still send there.

data-api

Where the tag sends events. Without it, events go to https://quillly.com/api/qa/collect, and you don't need to set it.

data-qa-content

Set by Quillly on the pages it serves, with the page's details. Don't add it to your own tag.

data-qa-exclude matches a prefix and everything under it: /app covers /app and /app/billing, but not /apps. Matching is case-sensitive, spaces around an entry are ignored, and so is a / at its end. An entry of / on its own matches only the home page. In a single-page app, the tag checks the path again on every navigation.

html
<script async src="https://quillly.com/qa.js" data-website="YOUR_WEBSITE_ID" data-qa-exclude="/app,/dashboard"></script>

On any element#

These are standard HTML data attributes, so they don't change how the element looks or works.

Table 5

Attribute

What it does

data-qa-goal="name"

A click on the element, or on anything inside it, sends the goal name

data-qa-event="name"

The same as data-qa-goal

data-qa-{key}="value"

On the same element, adds key to the goal's metadata with value as text

html
<a href="/pricing" data-qa-goal="pricing_click" data-qa-billing-cycle="yearly">See pricing</a>
  • The name follows the goal rules above. An element whose name breaks them sends nothing.

  • Metadata comes from the same element only. Its other data-qa-* attributes become metadata, so data-qa-billing-cycle arrives as billing_cycle: "yearly".

  • The nearest element wins. When elements with data-qa-goal sit inside each other, a click sends the goal of the one closest to it.

  • The goal is sent at once, so a click on a link that leaves the page still records it.

Events the tag sends by itself#

Table 6

Event

When the tag sends it

Page view

When the page loads, and when the path changes in a single-page app. A change to only the query string or the #hash is not a new page.

Time on page

When the visitor hides the page, leaves it or moves to another path, after at least 2 seconds on it

Web Vitals (LCP, CLS and INP)

Once per page, when the visitor hides or leaves it, or moves to another path. Pages Quillly serves don't send them.

The tag also reads utm_source, utm_medium and utm_campaign from the address the visitor landed on. It sends events in batches of up to 20 within a second of them happening; goals and identify calls go at once.

What the collector accepts#

Quillly's collector checks every request the tag sends. These are its rules, and the answer the tag gets back when a request breaks one.

Table 7

Rule

Limit

Past the limit

Where events come from

Your website's domain and its subdomains, quillly.com addresses, localhost and 127.0.0.1

403, nothing stored

The website

Its owner's plan must include analytics

404, nothing stored

Events in one request

20

400, nothing stored

Size of one request

16 KB

413, nothing stored

Requests from one IP address

240 a minute

429 for up to a minute

Event time

From 24 hours ago to 5 minutes ahead

Set to the time Quillly receives it

Bots and headless browsers

Never stored

204

Monthly allowance

Used up

204, nothing stored until the next period or an upgrade

A 204 answer means the request arrived, whether or not its events were stored. The tag never retries, and your page's own code never sees these answers.

Record a conversion from your server#

A payment webhook on your server can mark a person as a Customer without the browser. Send a POST with an account API key, the kind you create on Connections, in the Authorization header:

bash
curl -X POST https://quillly.com/api/websites/YOUR_WEBSITE_ID/analytics/conversions \
  -H "Authorization: Bearer qly_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"userId": "u_4821", "revenue": 49, "currency": "EUR"}'
Table 8

Field

What it is

userId

The userId you sent with qa('identify') for that person.

visitorId

Instead of userId, Quillly's own visitor ID. Your pages can't read it, so use userId.

revenue

Optional. A number from 0 to 1,000,000,000, added to the person's revenue.

currency

Optional. A three-letter code, such as EUR.

at

Optional. When the payment happened, as an ISO timestamp, for replaying an old webhook. It can't be in the future.

  • The key decides who is calling. Its owner needs Analytics on their plan and access to the website, with Manage on Analytics when the website is shared with them, as for setting up goals.

  • A bad key is refused. An unknown, revoked or expired key gets 401 "Invalid or revoked API key", even from a browser that is signed in.

  • The person must be known. A userId that qa('identify') never sent gets 404 "No visitor identified with that userId". A visitorId isn't checked, so a wrong one still succeeds without matching any of your visitors. A body with neither gets 400.

  • It is not an event. A server conversion doesn't count toward your monthly allowance and doesn't appear in a visitor's journey. It marks the person as a Customer and adds the revenue.

API keys, OAuth and tool access shows how to create and revoke a key.

Common questions#

Can my server send events?#

Not events: goals, identify calls and page views all come from the tag running in your visitors' browsers. Your server can record a conversion with an API key, as Record a conversion from your server shows above. To count the purchase as a goal too, call qa('goal', 'purchase') on the page the buyer sees after paying, such as your thank-you page.

Why doesn't my goal show up?#

Check the name against the rules above, and test from your own domain or localhost. New completions can take a few minutes to show in the Goal tab, and nothing is stored while your monthly allowance is used up.

Do goals on excluded paths count?#

Yes. data-qa-exclude stops page views, time on page and Web Vitals only, so a signup inside your app still sends its goal and its identify call.

  • Install analytics on your website: where the tag goes, and the Cookie and Cookieless tags.

  • Track goals and conversions: the Goal tab, conversion goals and Customers.

  • Analytics limits, retention and excluding traffic: the monthly allowance and what counts toward it.

  • Cookies or cookieless analytics: what each tag stores in the browser.

  • Visitor journeys and the Users list: the people identify names.