API reference
Access your analytics data programmatically with the Glyphex REST API.
Authentication
All API requests require a Bearer token. Create an API key in your site settings under API keys.
curl https://glyphex.io/api/v1/stats/overview?range=30d \ -H "Authorization: Bearer glx_your_api_key_here"
Your full API key is shown only once when you create it. Store it securely.
Base URL
https://glyphex.io/api/v1
Rate limits
Rate limits are applied per API key on a sliding 1-hour window.
| Plan | Requests per hour |
|---|---|
| Starter | 1,000 |
| Growth | 2,000 |
| Agency | 5,000 |
Every response includes rate limit headers: X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (Unix timestamp).
Response format
All successful responses return JSON with this structure:
{
"data": { ... },
"meta": {
"site_id": "your-site-id",
"range": "30d",
"generated_at": "2026-02-18T12:00:00.000Z"
}
}Errors return a similar structure with an error object:
{
"error": {
"code": "unauthorized",
"message": "Missing or invalid Authorization header. Use: Bearer glx_..."
}
}Query parameters
All endpoints accept these query parameters:
| Parameter | Default | Description |
|---|---|---|
range | 30d | Time range: 24h, 7d, 30d, 90d, or custom |
start | - | Start date for custom range (ISO 8601) |
end | - | End date for custom range (ISO 8601) |
limit | 10 | Number of results (1-100). Applies to pages, referrers, devices, geo, events, utm |
country | - | Filter by country code (e.g. US, DE) |
device | - | Filter by device type (desktop, mobile, tablet) |
hostname | - | Filter by hostname |
utm_source | - | Filter by UTM source |
Country exclusions
If you have configured excluded countries in your site settings, those exclusions are automatically applied to all API responses. You do not need to pass any additional parameters. The API uses the same exclusion list as the dashboard and email reports.
Endpoints
/api/v1/stats/overviewAggregated stats for the selected period with comparison to the previous period.
Change values are percentages. A null change value means the previous period had zero.
{
"data": {
"current": {
"visitors": 1240,
"pageviews": 3891,
"sessions": 1580,
"bounce_rate": 42,
"avg_duration": 127
},
"previous": {
"visitors": 1100,
"pageviews": 3200,
"sessions": 1400,
"bounce_rate": 45,
"avg_duration": 115
},
"change": {
"visitors": 13,
"pageviews": 22,
"sessions": 13,
"bounce_rate": -7,
"avg_duration": 10
}
},
"meta": { "site_id": "...", "range": "30d", "generated_at": "..." }
}/api/v1/stats/timeseriesDaily visitor and pageview counts for the selected period.
{
"data": [
{ "date": "2026-02-01", "visitors": 42, "pageviews": 128 },
{ "date": "2026-02-02", "visitors": 38, "pageviews": 104 }
],
"meta": { "site_id": "...", "range": "30d", "generated_at": "..." }
}/api/v1/stats/pagesMost visited pages ranked by views.
{
"data": [
{ "pathname": "/", "title": "Home", "views": 520, "visitors": 380 },
{ "pathname": "/pricing", "title": "Pricing", "views": 210, "visitors": 180 }
],
"meta": { "site_id": "...", "range": "30d", "limit": 10, "generated_at": "..." }
}/api/v1/stats/referrersTop traffic sources by referrer domain.
{
"data": [
{ "referrer": "google.com", "views": 320, "visitors": 280 },
{ "referrer": "twitter.com", "views": 85, "visitors": 72 }
],
"meta": { "site_id": "...", "range": "30d", "limit": 10, "generated_at": "..." }
}/api/v1/stats/channelsVisitor breakdown by traffic channel (direct, organic, social, referral, etc.).
{
"data": [
{ "channel": "Organic", "visitors": 580, "percentage": 47 },
{ "channel": "Direct", "visitors": 340, "percentage": 27 },
{ "channel": "Social", "visitors": 200, "percentage": 16 }
],
"meta": { "site_id": "...", "range": "30d", "generated_at": "..." }
}/api/v1/stats/devicesVisitor breakdown by device type.
{
"data": [
{ "name": "Desktop", "visitors": 820 },
{ "name": "Mobile", "visitors": 350 },
{ "name": "Tablet", "visitors": 70 }
],
"meta": { "site_id": "...", "range": "30d", "limit": 10, "generated_at": "..." }
}/api/v1/stats/geoVisitor breakdown by country.
{
"data": [
{ "name": "United States", "visitors": 480 },
{ "name": "Germany", "visitors": 220 },
{ "name": "United Kingdom", "visitors": 180 }
],
"meta": { "site_id": "...", "range": "30d", "limit": 10, "generated_at": "..." }
}/api/v1/stats/eventsCustom event counts and unique visitors.
{
"data": [
{ "name": "signup_click", "count": 142, "visitors": 130 },
{ "name": "download_pdf", "count": 87, "visitors": 65 }
],
"meta": { "site_id": "...", "range": "30d", "limit": 10, "generated_at": "..." }
}/api/v1/stats/utmVisitor breakdown by UTM source.
{
"data": [
{ "name": "google", "visitors": 320 },
{ "name": "newsletter", "visitors": 85 }
],
"meta": { "site_id": "...", "range": "30d", "limit": 10, "generated_at": "..." }
}Error codes
| Status | Code | Description |
|---|---|---|
| 401 | unauthorized | Missing or invalid Authorization header |
| 401 | invalid_key | API key not found or invalid format |
| 401 | key_disabled | API key has been disabled |
| 401 | key_expired | API key has passed its expiry date |
| 429 | rate_limit_exceeded | Too many requests. Check X-RateLimit-Reset header |
| 500 | internal_error | Server error fetching data |
CORS
All endpoints allow cross-origin requests. Responses include Access-Control-Allow-Origin: *. You can call the API from browser-side JavaScript, but be careful not to expose your API key in client-side code.
Example: fetch top pages from JavaScript
const response = await fetch(
"https://glyphex.io/api/v1/stats/pages?range=7d&limit=5",
{
headers: {
"Authorization": "Bearer glx_your_api_key_here"
}
}
);
const { data, meta } = await response.json();
// data = [{ pathname: "/", title: "Home", views: 520, visitors: 380 }, ...]Need help?
If you have questions about the API, reach out via