Developers

Read API

Read stats, goals, attribution, ad spend and short links with a read key. JSON over HTTPS, OpenAPI 3.1, CSV feeds and copyable curl examples.

The read API gives your own code, a client's dashboard or a spreadsheet the same numbers you see in Sporetag. It only reads. Nothing it does can change a site, a goal or a link.

The read API is on paid plans: Solo, Studio, Agency and Custom. It also powers the MCP server and Google Sheets feeds.

Make a key

  1. Open Settings, then API.
  2. Name the key, pick what it reads (All sites, or one site) and press Make a key.
  3. Copy the key. It is shown once. Sporetag keeps only a hash of it.

A key starts with st_rk_. All sites covers sites you add later too. For a client, a script that runs somewhere else, or a shared sheet, make a key for one site. Revoke stops a key at once.

Read keys are not Events API keys. Events API keys (st_sk_) send events and cannot read. Read keys (st_rk_) read and cannot send.

First request

Shell
export SPORETAG_KEY="st_rk_..."
curl https://sporetag.com/api/v1/sites \
  -H "Authorization: Bearer $SPORETAG_KEY"
JSON
{
  "data": [
    {
      "id": "8fK2pQ",
      "name": "Demo shop",
      "domain": "shop.example.com",
      "client_label": null,
      "timezone": "America/New_York",
      "tracking_mode": "attribution",
      "created_at": "2026-09-14T16:02:11.000Z"
    }
  ],
  "pagination": { "page": 1, "limit": 100, "has_more": false, "next_page": null }
}

Every other call takes a site id from this list.

Endpoints

All are GET under https://sporetag.com. The full schema is the OpenAPI 3.1 document.

EndpointWhat it returns
/api/v1/sitesSites the key reads
/api/v1/sites/{site_id}One site
/api/v1/sites/{site_id}/statsTotals for a range, the span before it, and the change
/api/v1/sites/{site_id}/timeseriesThe same totals per hour, day or month
/api/v1/sites/{site_id}/breakdown/{dimension}Top sources, pages, campaigns, countries, devices or goals
/api/v1/sites/{site_id}/goalsGoals with conversions
/api/v1/sites/{site_id}/attributionConversions and revenue credited by source, campaign, ad, platform or short link
/api/v1/sites/{site_id}/adsAd spend, credited revenue and ROAS
/api/v1/linksShort links with clicks
/api/v1/links/{link_id}One link: clicks per day and where they came from

Totals

Shell
curl "https://sporetag.com/api/v1/sites/8fK2pQ/stats?period=7d" \
  -H "Authorization: Bearer $SPORETAG_KEY"
JSON
{
  "data": {
    "site_id": "8fK2pQ",
    "range": { "period": "7d", "date_from": "2026-10-17", "date_to": "2026-10-23", "timezone": "America/New_York", "from": "2026-10-17T00:00:00-04:00", "to": "2026-10-23T11:00:00-04:00" },
    "previous_range": { "from": "2026-10-10T13:00:00-04:00", "to": "2026-10-17T00:00:00-04:00" },
    "filters": {},
    "metrics": { "visitors": 1840, "visits": 2210, "pageviews": 5120, "views_per_visit": 2.32, "bounce_rate": 41, "visit_duration": 96, "conversions": 63, "conversion_rate": 2.9 },
    "previous": { "visitors": 1702, "visits": 2050, "pageviews": 4790, "views_per_visit": 2.34, "bounce_rate": 43, "visit_duration": 91, "conversions": 55, "conversion_rate": 2.7 },
    "change_pct": { "visitors": 8.1, "visits": 7.8, "pageviews": 6.9, "views_per_visit": -0.9, "bounce_rate": -4.7, "visit_duration": 5.5, "conversions": 14.5, "conversion_rate": 7.4 }
  }
}

The numbers above are an example. bounce_rate and conversion_rate are percents. visit_duration is seconds per visit. conversions counts visits that reached any goal. Add compare=false to skip the span before.

Per day

Shell
curl "https://sporetag.com/api/v1/sites/8fK2pQ/timeseries?period=30d&interval=day" \
  -H "Authorization: Bearer $SPORETAG_KEY"

interval is day (the default), hour (up to 7 days) or month. Every bucket is in the list, empty ones too. date is the local day (2026-10-01), hour (2026-10-01 14:00) or month (2026-10).

Breakdowns

Shell
curl "https://sporetag.com/api/v1/sites/8fK2pQ/breakdown/source?period=30d&limit=10" \
  -H "Authorization: Bearer $SPORETAG_KEY"

Dimensions: source, referrer, utm_source, utm_medium, utm_campaign (or campaign), utm_content, utm_term, page, entry_page, country, device, browser, os and goal. Rows come most visitors first, each with visitors, visits, pageviews, bounce rate, visit duration, conversions and conversion rate. A blank source is Direct / None.

Goals

Shell
curl "https://sporetag.com/api/v1/sites/8fK2pQ/goals?period=mtd" \
  -H "Authorization: Bearer $SPORETAG_KEY"

Each goal has conversions (visits that reached it), events (every match) and conversion_rate (percent of visits).

Attribution

Shell
curl "https://sporetag.com/api/v1/sites/8fK2pQ/attribution?period=30d&by=campaign&model=last" \
  -H "Authorization: Bearer $SPORETAG_KEY"

This is the attribution report. by is source, campaign, content (the ad), term (the ad set or keyword), platform or link. model is last (the default) or first. goal_id counts one goal. Revenue is per currency, such as {"USD": 1240.5}, and never converted.

Ad spend and ROAS

Shell
curl "https://sporetag.com/api/v1/sites/8fK2pQ/ads?period=30d&level=campaign" \
  -H "Authorization: Bearer $SPORETAG_KEY"

From connected ad platforms and spend files. level is platform, campaign, adset or ad. revenue is what Sporetag credits to the ads, last touch. platform_reported is what the platform itself counts. roas is null when there is no spend or when spend and revenue are in different currencies. connected is false when the site has no ad spend.

Shell
curl "https://sporetag.com/api/v1/links?period=30d" \
  -H "Authorization: Bearer $SPORETAG_KEY"
curl "https://sporetag.com/api/v1/links/LINK_ID?period=30d" \
  -H "Authorization: Bearer $SPORETAG_KEY"

A key for one site lists the links tied to that site. Add site_id to an all-sites key to do the same. status is active (the default), archived or all.

Dates and time zones

Send a period, or date_from and date_to as YYYY-MM-DD. Both days are included.

periodCovers
todayToday, up to now
yesterdayYesterday
7d, 30d, 90dThe last 7, 30 or 90 days, today included (default 30d)
12mThe last 365 days
mtdThis month, up to now
lastmonthLast calendar month

Days start at local midnight in one time zone: the timezone you send (an IANA name such as Europe/Berlin), else the site's, else UTC. Every answer repeats the range with ISO 8601 times and their offset, so you never have to guess. Days and months in a time series use the zone's offset at the start of the range.

Short link clicks are kept in whole UTC days, so link calls are always UTC.

A range can be up to 731 days, and only as far back as your plan keeps stats.

Filters

Add filter=dimension:value. Repeat it to combine, up to 8.

Shell
curl -G "https://sporetag.com/api/v1/sites/8fK2pQ/stats" \
  --data-urlencode "period=30d" \
  --data-urlencode "filter=source:Instagram" \
  --data-urlencode "filter=device:Mobile" \
  -H "Authorization: Bearer $SPORETAG_KEY"

Filters work on stats, time series, breakdowns and goals. A goal filter takes a goal id from the goals call.

Pages of results

Lists take limit (1 to 1,000, default 100) and page (from 1). The answer says if there is more:

JSON
"pagination": { "page": 1, "limit": 100, "has_more": true, "next_page": 2 }

Lists go 1,000 rows deep.

CSV feeds

Add format=csv to any list for a CSV file with every row. For tools that cannot send headers, such as Google Sheets, the key can ride in the address, for CSV feeds only:

Text
https://sporetag.com/api/v1/sites/8fK2pQ/timeseries?period=90d&format=csv&key=st_rk_...

Anyone with that address can read what the key reads. Use a key for one site, and revoke it if the address leaks. See Google Sheets and Looker Studio.

Errors

Errors have a status code and the same shape:

JSON
{ "error": { "code": "invalid_parameter", "message": "date_from must be a date as YYYY-MM-DD.", "field": "date_from" } }
StatusCodeMeans
400invalid_parameter, range_too_long, key_in_urlA parameter is wrong. field names it
401unauthorizedNo key, or the key was revoked
403planThe account's plan does not include the read API
403history_limitThe range starts before the plan keeps stats
404not_foundNo such site, link or endpoint for this key
429rate_limitedOver the rate limit. Wait Retry-After seconds
502, 503upstream_error, stats_unavailableStats could not be read. Try again in a minute

A site or link another key or account owns reads as not_found, never as forbidden.

Rate limit

Each key can make 120 requests a minute, across the read API, CSV feeds and the MCP server. Every answer has X-RateLimit-Limit and X-RateLimit-Remaining.

Safety

  • Keep keys on a server or in a password manager. The API sends no CORS headers, so a web page cannot call it.
  • Each key reads only its sites. Revoke it in Settings, API and it stops at once.
  • Answers never include visitor emails, names or phone numbers.

Something missing or wrong? Email hello@sporetag.com.