Developer
API Reference
Pull your analytics data into any tool, dashboard, or automation — with a simple REST API.
1. Authentication
All API endpoints require a valid API key passed as a Bearer token in the Authorization header. Generate keys in your profile settings. API access requires a Pro plan.
Request header
Authorization: Bearer waa_YOUR_API_KEY
https://webanalyzerapp.com/api/v1
| Property | Value |
|---|---|
| Rate limit | 60 requests / minute per key |
| Response format | JSON (UTF-8) |
| Date parameters | YYYY-MM-DD (UTC) |
| Default date range | Last 30 days when from / to are omitted |
2. Errors
All error responses return JSON with an error code and a human-readable message.
| HTTP status | Error code | Meaning |
|---|---|---|
| 401 | unauthenticated | No Authorization header provided. |
| 401 | invalid_api_key | The key does not exist or has been revoked. |
| 403 | pro_required | Your account is not on a Pro plan. |
| 404 | — | Website not found or does not belong to your account. |
| 422 | — | Validation error — check the errors field. |
| 429 | — | Rate limit exceeded (60 req/min per key). |
3. Websites
List all websites
Returns all websites in your account.
Response
{
"data": [
{
"id": 1,
"name": "My Store",
"domain": "https://mystore.com",
"tracking_key": "a1b2c3d4e5f6",
"is_verified": true,
"created_at": "2025-01-15T10:00:00Z"
}
]
}
Get a website
Returns details for a single website.
Response
{
"data": {
"id": 1,
"name": "My Store",
"domain": "https://mystore.com",
"tracking_key": "a1b2c3d4e5f6",
"timezone": "Europe/Istanbul",
"is_verified": true,
"created_at": "2025-01-15T10:00:00Z"
}
}
4. Summary (KPIs)
Returns key performance indicators for the given date range.
| Parameter | Description |
|---|---|
| from | Start date YYYY-MM-DD (default: 30 days ago) |
| to | End date YYYY-MM-DD (default: today) |
Response
{
"data": {
"from": "2025-01-01",
"to": "2025-01-31",
"visitors": 1240,
"sessions": 1880,
"page_views": 5430,
"events": 320,
"bounce_rate": 42.5,
"avg_duration": 187
}
}
5. Timeseries
Returns daily visitor and page view counts for the given date range — ideal for building charts.
| Parameter | Description |
|---|---|
| from | Start date (default: 30 days ago) |
| to | End date (default: today) |
Response
{
"data": {
"dates": ["2025-01-01", "2025-01-02", "2025-01-03"],
"visitors": [120, 145, 98],
"pageviews": [340, 410, 280]
},
"meta": { "from": "2025-01-01", "to": "2025-01-03" }
}
6. Realtime
Returns the number of active visitors on the website right now (sessions active in the last 5 minutes).
Response
{
"data": {
"active_visitors": 14,
"timestamp": "2025-01-31T18:45:00+00:00"
}
}
7. Top pages
Returns the most-visited pages ranked by view count.
| Parameter | Description |
|---|---|
| from | Start date (default: 30 days ago) |
| to | End date (default: today) |
| limit | Max rows 1–100 (default: 20) |
Response
{
"data": [
{
"path": "/pricing",
"views": 820,
"visitors": 540,
"sessions": 610,
"bounce_rate": 35.2,
"avg_time": 145
}
],
"meta": { "from": "2025-01-01", "to": "2025-01-31" }
}
8. Traffic sources
Returns session counts broken down by acquisition channel (organic, direct, referral, paid, email, social).
| Parameter | Description |
|---|---|
| from | Start date (default: 30 days ago) |
| to | End date (default: today) |
Response
{
"data": [
{ "channel": "organic", "sessions": 640, "pct": 34.0 },
{ "channel": "direct", "sessions": 510, "pct": 27.1 },
{ "channel": "referral", "sessions": 290, "pct": 15.4 }
],
"meta": { "from": "2025-01-01", "to": "2025-01-31" }
}
9. Events
Returns custom event counts. Pass name to filter to a specific event type.
| Parameter | Description |
|---|---|
| from | Start date (default: 30 days ago) |
| to | End date (default: today) |
| name | Filter to a specific event name (optional) |
Response
{
"data": [
{
"name": "signup",
"count": 142,
"last_triggered": "2025-01-31T18:45:00.000000Z"
},
{
"name": "checkout",
"count": 87,
"last_triggered": "2025-01-31T17:22:00.000000Z"
}
],
"meta": { "from": "2025-01-01", "to": "2025-01-31" }
}
10. Visitors
Returns a paginated list of visitors with device and location metadata.
| Parameter | Description |
|---|---|
| from | Start date (default: 30 days ago) |
| to | End date (default: today) |
| limit | Results per page 1–100 (default: 20) |
| page | Page number (default: 1) |
Response
{
"data": [
{
"id": 42,
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"first_seen_at": "2025-01-10T08:30:00+00:00",
"last_seen_at": "2025-01-31T14:22:00+00:00",
"session_count": 12,
"device_type": "desktop",
"browser": "Chrome",
"os": "Windows",
"country": "US",
"city": "New York"
}
],
"meta": {
"from": "2025-01-01", "to": "2025-01-31",
"total": 1240, "page": 1, "per_page": 20, "total_pages": 62
}
}
11. Sessions
Returns a paginated list of sessions with full metadata including entry/exit pages, UTM parameters, device, and geo info.
| Parameter | Description |
|---|---|
| from | Start date (default: 30 days ago) |
| to | End date (default: today) |
| limit | Results per page 1–100 (default: 20) |
| page | Page number (default: 1) |
Response
{
"data": [
{
"id": 1580,
"visitor_id": 42,
"started_at": "2025-01-31T14:10:00+00:00",
"ended_at": "2025-01-31T14:22:00+00:00",
"duration": 720,
"entry_page": "/",
"exit_page": "/pricing",
"page_view_count": 5,
"device_type": "desktop",
"browser": "Chrome",
"os": "Windows",
"country": "US",
"city": "New York",
"referrer_domain": "google.com",
"utm_source": "newsletter",
"utm_medium": "email",
"utm_campaign": "jan-promo"
}
],
"meta": {
"from": "2025-01-01", "to": "2025-01-31",
"total": 1880, "page": 1, "per_page": 20, "total_pages": 94
}
}
12. Countries
Returns session counts broken down by country (ISO 3166 two-letter code).
| Parameter | Description |
|---|---|
| from | Start date (default: 30 days ago) |
| to | End date (default: today) |
| limit | Max rows 1–100 (default: 20) |
Response
{
"data": [
{ "country": "US", "sessions": 640, "pct": 34.0 },
{ "country": "DE", "sessions": 280, "pct": 14.9 },
{ "country": "GB", "sessions": 210, "pct": 11.2 }
],
"meta": { "from": "2025-01-01", "to": "2025-01-31" }
}
13. Devices
Returns a combined breakdown of device types, browsers, and operating systems.
| Parameter | Description |
|---|---|
| from | Start date (default: 30 days ago) |
| to | End date (default: today) |
Response
{
"data": {
"devices": {
"desktop": 1120,
"mobile": 580,
"tablet": 180
},
"browsers": [
{ "browser": "Chrome", "sessions": 980, "pct": 52.1 },
{ "browser": "Safari", "sessions": 420, "pct": 22.3 },
{ "browser": "Firefox", "sessions": 280, "pct": 14.9 }
],
"operating_systems": [
{ "os": "Windows", "sessions": 820, "pct": 43.6 },
{ "os": "macOS", "sessions": 480, "pct": 25.5 },
{ "os": "iOS", "sessions": 310, "pct": 16.5 }
]
},
"meta": { "from": "2025-01-01", "to": "2025-01-31" }
}
14. Server-side tracking
Send events from your backend (webhooks, payment confirmations, CRM actions) using the server-side tracking endpoints.
Single event
| Field | Type | Description |
|---|---|---|
| name | string | Event name (required) |
| payload | object | Key/value metadata (optional) |
| visitor_id | string | Visitor UUID to link to (optional) |
| url | string | Page URL context (optional) |
curl
curl -X POST https://webanalyzerapp.com/api/v1/websites/1/track \
-H "Authorization: Bearer waa_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"purchase","payload":{"order_id":"1234","value":"49.99"}}'
Batch events
Send up to 25 events in a single request. Pass an events array with the same fields as the single event endpoint.
curl
curl -X POST https://webanalyzerapp.com/api/v1/websites/1/track/batch \
-H "Authorization: Bearer waa_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"events":[{"name":"signup"},{"name":"purchase","payload":{"value":"29"}}]}'
15. Quick start
Fetch your 30-day KPIs with a single curl command:
curl
curl https://webanalyzerapp.com/api/v1/websites/1/summary \
-H "Authorization: Bearer waa_YOUR_API_KEY" \
-H "Accept: application/json"
Or with JavaScript (fetch):
JavaScript
const res = await fetch(
'https://webanalyzerapp.com/api/v1/websites/1/summary?from=2025-01-01&to=2025-01-31',
{ headers: { 'Authorization': 'Bearer waa_YOUR_API_KEY' } }
);
const { data } = await res.json();
console.log(`${data.visitors} visitors · ${data.sessions} sessions`);
16. Code examples
Copy-paste ready examples for every popular language and framework. All examples fetch your 30-day summary KPIs — adapt the endpoint and parameters for any API call.
Fetch summary
curl "https://webanalyzerapp.com/api/v1/websites/1/summary?from=2025-01-01&to=2025-01-31" \
-H "Authorization: Bearer waa_YOUR_API_KEY" \
-H "Accept: application/json"
Track a server-side event
curl -X POST "https://webanalyzerapp.com/api/v1/websites/1/track" \
-H "Authorization: Bearer waa_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"purchase","payload":{"order_id":"ORD-123","value":"49.99"}}'
List top pages
curl "https://webanalyzerapp.com/api/v1/websites/1/pages?from=2025-01-01&to=2025-01-31&limit=10" \
-H "Authorization: Bearer waa_YOUR_API_KEY"
Fetch summary (native PHP with cURL)
<?php
$apiKey = 'waa_YOUR_API_KEY';
$websiteId = 1;
$baseUrl = 'https://webanalyzerapp.com/api/v1';
// ── Fetch 30-day summary ────────────────────────────────────
$ch = curl_init("{$baseUrl}/websites/{$websiteId}/summary?from=2025-01-01&to=2025-01-31");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer {$apiKey}",
'Accept: application/json',
],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status !== 200) {
die("API error: HTTP {$status}");
}
$data = json_decode($response, true)['data'];
echo "Visitors: {$data['visitors']}, Sessions: {$data['sessions']}\n";
Track a server-side event
<?php
$ch = curl_init("{$baseUrl}/websites/{$websiteId}/track");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer {$apiKey}",
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'name' => 'purchase',
'payload' => ['order_id' => 'ORD-123', 'value' => '49.99'],
]),
]);
$response = curl_exec($ch);
curl_close($ch);
Using Guzzle HTTP
<?php
// composer require guzzlehttp/guzzle
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'https://webanalyzerapp.com/api/v1/',
'headers' => [
'Authorization' => 'Bearer waa_YOUR_API_KEY',
'Accept' => 'application/json',
],
]);
// Summary
$res = $client->get('websites/1/summary', ['query' => ['from' => '2025-01-01', 'to' => '2025-01-31']]);
$data = json_decode($res->getBody(), true)['data'];
// Top pages
$res = $client->get('websites/1/pages', ['query' => ['limit' => 10]]);
$pages = json_decode($res->getBody(), true)['data'];
// Track event
$client->post('websites/1/track', [
'json' => ['name' => 'signup', 'payload' => ['plan' => 'pro']],
]);
Using Laravel HTTP Client
<?php
use Illuminate\Support\Facades\Http;
// ── Configuration (add to config/services.php) ──────────────
// 'webanalyzer' => [
// 'key' => env('WEBANALYZER_API_KEY'),
// 'base' => 'https://webanalyzerapp.com/api/v1',
// 'site_id' => env('WEBANALYZER_SITE_ID', 1),
// ],
$api = config('services.webanalyzer.base');
$siteId = config('services.webanalyzer.site_id');
$http = Http::withToken(config('services.webanalyzer.key'))
->acceptJson()
->baseUrl($api);
// ── Fetch 30-day summary ────────────────────────────────────
$summary = $http->get("websites/{$siteId}/summary", [
'from' => now()->subDays(30)->toDateString(),
'to' => now()->toDateString(),
])->json('data');
// $summary['visitors'], $summary['sessions'], etc.
// ── Get top pages ───────────────────────────────────────────
$pages = $http->get("websites/{$siteId}/pages", [
'from' => '2025-01-01',
'to' => '2025-01-31',
'limit' => 10,
])->json('data');
// ── Track server-side event ─────────────────────────────────
$http->post("websites/{$siteId}/track", [
'name' => 'purchase',
'payload' => [
'order_id' => $order->id,
'value' => $order->total,
'currency' => 'USD',
],
]);
Service class pattern
<?php
namespace App\Services;
use Illuminate\Support\Facades\Http;
use Illuminate\Http\Client\PendingRequest;
class WebAnalyzer
{
protected PendingRequest $http;
protected int $siteId;
public function __construct()
{
$this->siteId = (int) config('services.webanalyzer.site_id');
$this->http = Http::withToken(config('services.webanalyzer.key'))
->acceptJson()
->baseUrl(config('services.webanalyzer.base'));
}
public function summary(string $from, string $to): array
{
return $this->http
->get("websites/{$this->siteId}/summary", compact('from', 'to'))
->throw()
->json('data');
}
public function trackEvent(string $name, array $payload = []): void
{
$this->http->post("websites/{$this->siteId}/track", [
'name' => $name,
'payload' => $payload,
])->throw();
}
public function realtime(): int
{
return $this->http
->get("websites/{$this->siteId}/realtime")
->throw()
->json('data.active_visitors');
}
}
// Usage in a controller:
// app(WebAnalyzer::class)->trackEvent('purchase', ['value' => 49.99]);
Scheduled command — daily Slack report
<?php
// app/Console/Commands/DailyAnalyticsReport.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Services\WebAnalyzer;
use Illuminate\Support\Facades\Http;
class DailyAnalyticsReport extends Command
{
protected $signature = 'analytics:daily-report';
public function handle(WebAnalyzer $wa)
{
$data = $wa->summary(
now()->subDay()->toDateString(),
now()->subDay()->toDateString()
);
Http::post(config('services.slack.webhook'), [
'text' => "📊 Yesterday: {$data['visitors']} visitors, "
. "{$data['sessions']} sessions, "
. "{$data['bounce_rate']}% bounce rate",
]);
}
}
// Schedule in routes/console.php:
// Schedule::command('analytics:daily-report')->dailyAt('09:00');
Fetch API (browser & Node 18+)
const API_KEY = 'waa_YOUR_API_KEY';
const BASE = 'https://webanalyzerapp.com/api/v1';
const SITE_ID = 1;
const headers = {
'Authorization': `Bearer ${API_KEY}`,
'Accept': 'application/json',
};
// ── Fetch summary ───────────────────────────────────────────
const res = await fetch(
`${BASE}/websites/${SITE_ID}/summary?from=2025-01-01&to=2025-01-31`,
{ headers }
);
const { data } = await res.json();
console.log(`${data.visitors} visitors, ${data.sessions} sessions`);
// ── Top pages ───────────────────────────────────────────────
const pagesRes = await fetch(
`${BASE}/websites/${SITE_ID}/pages?limit=10`,
{ headers }
);
const pages = (await pagesRes.json()).data;
pages.forEach(p => console.log(`${p.path} → ${p.views} views`));
// ── Track event ─────────────────────────────────────────────
await fetch(`${BASE}/websites/${SITE_ID}/track`, {
method: 'POST',
headers: { ...headers, 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'purchase',
payload: { order_id: 'ORD-123', value: '49.99' },
}),
});
Node.js with axios
// npm install axios
import axios from 'axios';
const api = axios.create({
baseURL: 'https://webanalyzerapp.com/api/v1',
headers: { Authorization: 'Bearer waa_YOUR_API_KEY' },
});
// Summary
const { data: { data: summary } } = await api.get('/websites/1/summary', {
params: { from: '2025-01-01', to: '2025-01-31' },
});
// Realtime visitors
const { data: { data: rt } } = await api.get('/websites/1/realtime');
console.log(`${rt.active_visitors} visitors online`);
// Track event
await api.post('/websites/1/track', {
name: 'signup',
payload: { plan: 'pro', source: 'landing_page' },
});
React hook — useAnalytics
// hooks/useAnalytics.js
import { useState, useEffect } from 'react';
const API_KEY = process.env.REACT_APP_WA_API_KEY;
const BASE = 'https://webanalyzerapp.com/api/v1';
async function fetchAPI(path, params = {}) {
const url = new URL(`${BASE}${path}`);
Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));
const res = await fetch(url, {
headers: { Authorization: `Bearer ${API_KEY}` },
});
if (!res.ok) throw new Error(`API ${res.status}`);
return res.json();
}
export function useAnalytics(siteId, from, to) {
const [summary, setSummary] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(true);
fetchAPI(`/websites/${siteId}/summary`, { from, to })
.then(r => setSummary(r.data))
.finally(() => setLoading(false));
}, [siteId, from, to]);
return { summary, loading };
}
export function useRealtime(siteId, intervalMs = 30000) {
const [count, setCount] = useState(0);
useEffect(() => {
const poll = () =>
fetchAPI(`/websites/${siteId}/realtime`).then(r =>
setCount(r.data.active_visitors)
);
poll();
const timer = setInterval(poll, intervalMs);
return () => clearInterval(timer);
}, [siteId, intervalMs]);
return count;
}
Dashboard component
// components/AnalyticsDashboard.jsx
import { useAnalytics, useRealtime } from '../hooks/useAnalytics';
export default function AnalyticsDashboard({ siteId }) {
const { summary, loading } = useAnalytics(siteId, '2025-01-01', '2025-01-31');
const liveVisitors = useRealtime(siteId);
if (loading) return <div>Loading...</div>;
return (
<div className="grid grid-cols-2 gap-4 md:grid-cols-4">
<Card title="Live Now" value={liveVisitors} />
<Card title="Visitors" value={summary.visitors} />
<Card title="Sessions" value={summary.sessions} />
<Card title="Bounce Rate" value={`${summary.bounce_rate}%`} />
</div>
);
}
function Card({ title, value }) {
return (
<div className="rounded-xl border p-4 shadow-sm">
<p className="text-sm text-gray-500">{title}</p>
<p className="text-2xl font-bold">{value}</p>
</div>
);
}
Server component (App Router)
// app/dashboard/page.tsx
const API_KEY = process.env.WA_API_KEY!;
const BASE = 'https://webanalyzerapp.com/api/v1';
const SITE_ID = process.env.WA_SITE_ID!;
async function getAnalytics(from: string, to: string) {
const res = await fetch(
`${BASE}/websites/${SITE_ID}/summary?from=${from}&to=${to}`,
{
headers: { Authorization: `Bearer ${API_KEY}` },
next: { revalidate: 300 }, // cache 5 min
}
);
if (!res.ok) throw new Error('Failed to fetch analytics');
return res.json();
}
export default async function DashboardPage() {
const today = new Date().toISOString().slice(0, 10);
const from = new Date(Date.now() - 30 * 86400000).toISOString().slice(0, 10);
const { data } = await getAnalytics(from, today);
return (
<div className="grid grid-cols-2 gap-4 md:grid-cols-4">
<Stat label="Visitors" value={data.visitors} />
<Stat label="Sessions" value={data.sessions} />
<Stat label="Page Views" value={data.page_views} />
<Stat label="Bounce Rate" value={`${data.bounce_rate}%`} />
</div>
);
}
function Stat({ label, value }: { label: string; value: string | number }) {
return (
<div className="rounded-xl border p-4">
<p className="text-sm text-gray-500">{label}</p>
<p className="text-2xl font-bold">{value.toLocaleString()}</p>
</div>
);
}
API route handler (track events from your backend)
// app/api/track-event/route.ts
import { NextResponse } from 'next/server';
export async function POST(req: Request) {
const { name, payload } = await req.json();
const res = await fetch(
`https://webanalyzerapp.com/api/v1/websites/${process.env.WA_SITE_ID}/track`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.WA_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ name, payload }),
}
);
return NextResponse.json({ ok: res.ok });
}
Composable — useAnalytics
// composables/useAnalytics.js
import { ref, watchEffect } from 'vue';
const API_KEY = import.meta.env.VITE_WA_API_KEY;
const BASE = 'https://webanalyzerapp.com/api/v1';
async function api(path, params = {}) {
const url = new URL(`${BASE}${path}`);
Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));
const res = await fetch(url, {
headers: { Authorization: `Bearer ${API_KEY}` },
});
return (await res.json()).data;
}
export function useAnalytics(siteId, from, to) {
const summary = ref(null);
const loading = ref(true);
watchEffect(async () => {
loading.value = true;
summary.value = await api(`/websites/${siteId}/summary`, {
from: from.value, to: to.value,
});
loading.value = false;
});
return { summary, loading };
}
export function useRealtime(siteId) {
const count = ref(0);
const poll = async () => {
const data = await api(`/websites/${siteId}/realtime`);
count.value = data.active_visitors;
};
poll();
setInterval(poll, 30000);
return count;
}
Component usage
<script setup>
import { ref } from 'vue';
import { useAnalytics, useRealtime } from '@/composables/useAnalytics';
const from = ref('2025-01-01');
const to = ref('2025-01-31');
const { summary, loading } = useAnalytics(1, from, to);
const liveCount = useRealtime(1);
</script>
<template>
<div v-if="loading">Loading...</div>
<div v-else class="grid grid-cols-4 gap-4">
<div class="rounded-xl border p-4">
<p class="text-sm text-gray-500">Live Now</p>
<p class="text-2xl font-bold">{{ liveCount }}</p>
</div>
<div class="rounded-xl border p-4">
<p class="text-sm text-gray-500">Visitors</p>
<p class="text-2xl font-bold">{{ summary.visitors }}</p>
</div>
<div class="rounded-xl border p-4">
<p class="text-sm text-gray-500">Bounce Rate</p>
<p class="text-2xl font-bold">{{ summary.bounce_rate }}%</p>
</div>
</div>
</template>
Using requests
# pip install requests
import requests
API_KEY = "waa_YOUR_API_KEY"
BASE = "https://webanalyzerapp.com/api/v1"
SITE_ID = 1
headers = {"Authorization": f"Bearer {API_KEY}"}
# ── Fetch summary ────────────────────────────────────────────
resp = requests.get(
f"{BASE}/websites/{SITE_ID}/summary",
headers=headers,
params={"from": "2025-01-01", "to": "2025-01-31"},
)
resp.raise_for_status()
data = resp.json()["data"]
print(f"Visitors: {data['visitors']}, Sessions: {data['sessions']}")
# ── Top pages ────────────────────────────────────────────────
pages = requests.get(
f"{BASE}/websites/{SITE_ID}/pages",
headers=headers,
params={"limit": 10},
).json()["data"]
for page in pages:
print(f" {page['path']} → {page['views']} views")
# ── Track event ──────────────────────────────────────────────
requests.post(
f"{BASE}/websites/{SITE_ID}/track",
headers=headers,
json={
"name": "purchase",
"payload": {"order_id": "ORD-123", "value": "49.99"},
},
)
# ── Batch events ─────────────────────────────────────────────
requests.post(
f"{BASE}/websites/{SITE_ID}/track/batch",
headers=headers,
json={
"events": [
{"name": "signup", "payload": {"plan": "pro"}},
{"name": "email_verified"},
]
},
)
Async with httpx
# pip install httpx
import httpx
import asyncio
API_KEY = "waa_YOUR_API_KEY"
async def main():
async with httpx.AsyncClient(
base_url="https://webanalyzerapp.com/api/v1",
headers={"Authorization": f"Bearer {API_KEY}"},
) as client:
# Parallel requests
summary_req = client.get("/websites/1/summary", params={"from": "2025-01-01", "to": "2025-01-31"})
pages_req = client.get("/websites/1/pages", params={"limit": 5})
realtime_req = client.get("/websites/1/realtime")
summary, pages, realtime = await asyncio.gather(
summary_req, pages_req, realtime_req
)
print(f"Visitors: {summary.json()['data']['visitors']}")
print(f"Live now: {realtime.json()['data']['active_visitors']}")
asyncio.run(main())
Service class
# analytics/services.py
import requests
from django.conf import settings
class WebAnalyzer:
def __init__(self):
self.base = "https://webanalyzerapp.com/api/v1"
self.headers = {"Authorization": f"Bearer {settings.WA_API_KEY}"}
self.site_id = settings.WA_SITE_ID
def summary(self, from_date, to_date):
resp = requests.get(
f"{self.base}/websites/{self.site_id}/summary",
headers=self.headers,
params={"from": from_date, "to": to_date},
)
resp.raise_for_status()
return resp.json()["data"]
def track(self, name, payload=None):
requests.post(
f"{self.base}/websites/{self.site_id}/track",
headers=self.headers,
json={"name": name, "payload": payload or {}},
)
def realtime(self):
resp = requests.get(
f"{self.base}/websites/{self.site_id}/realtime",
headers=self.headers,
)
return resp.json()["data"]["active_visitors"]
View usage
# analytics/views.py
from django.shortcuts import render
from datetime import date, timedelta
from .services import WebAnalyzer
def dashboard(request):
wa = WebAnalyzer()
to = date.today().isoformat()
from_ = (date.today() - timedelta(days=30)).isoformat()
return render(request, "dashboard.html", {
"summary": wa.summary(from_, to),
"live": wa.realtime(),
})
Management command — daily report
# analytics/management/commands/daily_report.py
from django.core.management.base import BaseCommand
from analytics.services import WebAnalyzer
from datetime import date, timedelta
class Command(BaseCommand):
help = "Print yesterday's analytics summary"
def handle(self, *args, **options):
wa = WebAnalyzer()
yesterday = (date.today() - timedelta(days=1)).isoformat()
data = wa.summary(yesterday, yesterday)
self.stdout.write(
f"Yesterday: {data['visitors']} visitors, "
f"{data['sessions']} sessions, "
f"{data['bounce_rate']}% bounce"
)
Using net/http (stdlib)
require 'net/http'
require 'json'
require 'uri'
API_KEY = 'waa_YOUR_API_KEY'
BASE = 'https://webanalyzerapp.com/api/v1'
SITE_ID = 1
def api_get(path, params = {})
uri = URI("#{BASE}#{path}")
uri.query = URI.encode_www_form(params) unless params.empty?
req = Net::HTTP::Get.new(uri)
req['Authorization'] = "Bearer #{API_KEY}"
req['Accept'] = 'application/json'
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
JSON.parse(res.body)['data']
end
# Fetch summary
summary = api_get("/websites/#{SITE_ID}/summary", from: '2025-01-01', to: '2025-01-31')
puts "Visitors: #{summary['visitors']}, Sessions: #{summary['sessions']}"
# Top pages
pages = api_get("/websites/#{SITE_ID}/pages", limit: 10)
pages.each { |p| puts " #{p['path']} → #{p['views']} views" }
Track events
def track_event(name, payload = {})
uri = URI("#{BASE}/websites/#{SITE_ID}/track")
req = Net::HTTP::Post.new(uri, {
'Authorization' => "Bearer #{API_KEY}",
'Content-Type' => 'application/json',
})
req.body = { name: name, payload: payload }.to_json
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
end
track_event('purchase', order_id: 'ORD-123', value: '49.99')
Using Faraday gem
# gem install faraday
require 'faraday'
conn = Faraday.new('https://webanalyzerapp.com/api/v1') do |f|
f.request :json
f.response :json
f.headers['Authorization'] = 'Bearer waa_YOUR_API_KEY'
end
summary = conn.get("websites/1/summary", from: '2025-01-01', to: '2025-01-31').body['data']
conn.post("websites/1/track", { name: 'signup', payload: { plan: 'pro' } })
Full example with struct decoding
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
)
const (
apiKey = "waa_YOUR_API_KEY"
base = "https://webanalyzerapp.com/api/v1"
siteID = "1"
)
type Summary struct {
Visitors int `json:"visitors"`
Sessions int `json:"sessions"`
PageViews int `json:"page_views"`
Events int `json:"events"`
BounceRate float64 `json:"bounce_rate"`
AvgDuration int `json:"avg_duration"`
}
func apiGet(path string, params url.Values) ([]byte, error) {
u := base + path
if len(params) > 0 {
u += "?" + params.Encode()
}
req, _ := http.NewRequest("GET", u, nil)
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
func trackEvent(name string, payload map[string]string) error {
body, _ := json.Marshal(map[string]interface{}{
"name": name, "payload": payload,
})
req, _ := http.NewRequest("POST",
base+"/websites/"+siteID+"/track",
strings.NewReader(string(body)),
)
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
resp.Body.Close()
return nil
}
func main() {
// Fetch summary
params := url.Values{"from": {"2025-01-01"}, "to": {"2025-01-31"}}
body, _ := apiGet("/websites/"+siteID+"/summary", params)
var result struct{ Data Summary `json:"data"` }
json.Unmarshal(body, &result)
s := result.Data
fmt.Printf("Visitors: %d, Sessions: %d, Bounce: %.1f%%\n",
s.Visitors, s.Sessions, s.BounceRate)
// Track event
trackEvent("purchase", map[string]string{
"order_id": "ORD-123", "value": "49.99",
})
}
HttpClient with System.Text.Json
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
var apiKey = "waa_YOUR_API_KEY";
var siteId = 1;
var client = new HttpClient
{
BaseAddress = new Uri("https://webanalyzerapp.com/api/v1/")
};
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", apiKey);
// ── Fetch summary ───────────────────────────────────────────
var res = await client.GetAsync(
$"websites/{siteId}/summary?from=2025-01-01&to=2025-01-31");
res.EnsureSuccessStatusCode();
var json = await res.Content.ReadAsStringAsync();
var summary = JsonSerializer.Deserialize<ApiResponse<Summary>>(json)!.Data;
Console.WriteLine($"Visitors: {summary.Visitors}, Sessions: {summary.Sessions}");
// ── Track event ─────────────────────────────────────────────
var eventPayload = new
{
name = "purchase",
payload = new { order_id = "ORD-123", value = "49.99" }
};
var content = new StringContent(
JsonSerializer.Serialize(eventPayload), Encoding.UTF8, "application/json");
await client.PostAsync($"websites/{siteId}/track", content);
// ── Models ──────────────────────────────────────────────────
record ApiResponse<T>([property: JsonPropertyName("data")] T Data);
record Summary(
[property: JsonPropertyName("visitors")] int Visitors,
[property: JsonPropertyName("sessions")] int Sessions,
[property: JsonPropertyName("page_views")] int PageViews,
[property: JsonPropertyName("bounce_rate")] double BounceRate,
[property: JsonPropertyName("avg_duration")] int AvgDuration
);
ASP.NET minimal API — proxy endpoint
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient("webanalyzer", c =>
{
c.BaseAddress = new Uri("https://webanalyzerapp.com/api/v1/");
c.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer",
builder.Configuration["WebAnalyzer:ApiKey"]);
});
var app = builder.Build();
app.MapGet("/api/analytics", async (IHttpClientFactory factory) =>
{
var client = factory.CreateClient("webanalyzer");
var res = await client.GetStringAsync("websites/1/summary?from=2025-01-01&to=2025-01-31");
return Results.Content(res, "application/json");
});
app.Run();
Java 11+ HttpClient
import java.net.URI;
import java.net.http.*;
import com.google.gson.*;
public class WebAnalyzerExample {
static final String API_KEY = "waa_YOUR_API_KEY";
static final String BASE = "https://webanalyzerapp.com/api/v1";
static final int SITE_ID = 1;
static final HttpClient client = HttpClient.newHttpClient();
static final Gson gson = new Gson();
static String apiGet(String path) throws Exception {
var req = HttpRequest.newBuilder()
.uri(URI.create(BASE + path))
.header("Authorization", "Bearer " + API_KEY)
.header("Accept", "application/json")
.GET().build();
var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
return resp.body();
}
static void trackEvent(String name, JsonObject payload) throws Exception {
var body = new JsonObject();
body.addProperty("name", name);
body.add("payload", payload);
var req = HttpRequest.newBuilder()
.uri(URI.create(BASE + "/websites/" + SITE_ID + "/track"))
.header("Authorization", "Bearer " + API_KEY)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(gson.toJson(body)))
.build();
client.send(req, HttpResponse.BodyHandlers.ofString());
}
public static void main(String[] args) throws Exception {
// Fetch summary
String json = apiGet(
"/websites/" + SITE_ID + "/summary?from=2025-01-01&to=2025-01-31"
);
var data = gson.fromJson(json, JsonObject.class)
.getAsJsonObject("data");
System.out.printf("Visitors: %d, Sessions: %d%n",
data.get("visitors").getAsInt(),
data.get("sessions").getAsInt());
// Track event
var payload = new JsonObject();
payload.addProperty("order_id", "ORD-123");
payload.addProperty("value", "49.99");
trackEvent("purchase", payload);
}
}
Kotlin with ktor-client
// build.gradle.kts: implementation("io.ktor:ktor-client-cio:2.3.+")
// implementation("io.ktor:ktor-client-content-negotiation:2.3.+")
// implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.+")
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.engine.cio.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.*
@Serializable
data class Summary(
val visitors: Int, val sessions: Int,
@SerialName("page_views") val pageViews: Int,
@SerialName("bounce_rate") val bounceRate: Double,
@SerialName("avg_duration") val avgDuration: Int,
)
@Serializable
data class ApiResponse<T>(val data: T)
suspend fun main() {
val apiKey = "waa_YOUR_API_KEY"
val base = "https://webanalyzerapp.com/api/v1"
val siteId = 1
val client = HttpClient(CIO) {
install(ContentNegotiation) { json() }
}
// Fetch summary
val summary = client.get("$base/websites/$siteId/summary") {
bearerAuth(apiKey)
url { parameters.append("from", "2025-01-01"); parameters.append("to", "2025-01-31") }
}.body<ApiResponse<Summary>>().data
println("Visitors: ${summary.visitors}, Sessions: ${summary.sessions}")
// Track event
client.post("$base/websites/$siteId/track") {
bearerAuth(apiKey)
contentType(io.ktor.http.ContentType.Application.Json)
setBody(mapOf("name" to "purchase", "payload" to mapOf("order_id" to "ORD-123")))
}
client.close()
}
Android (OkHttp)
// implementation("com.squareup.okhttp3:okhttp:4.12.+")
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONObject
val client = OkHttpClient()
val apiKey = "waa_YOUR_API_KEY"
val base = "https://webanalyzerapp.com/api/v1"
fun fetchSummary(siteId: Int): JSONObject {
val req = Request.Builder()
.url("$base/websites/$siteId/summary?from=2025-01-01&to=2025-01-31")
.header("Authorization", "Bearer $apiKey")
.build()
val body = client.newCall(req).execute().body!!.string()
return JSONObject(body).getJSONObject("data")
}
fun trackEvent(siteId: Int, name: String, payload: Map<String, String>) {
val json = JSONObject(mapOf("name" to name, "payload" to payload)).toString()
val req = Request.Builder()
.url("$base/websites/$siteId/track")
.header("Authorization", "Bearer $apiKey")
.post(json.toRequestBody("application/json".toMediaType()))
.build()
client.newCall(req).execute()
}
URLSession with async/await
import Foundation
let apiKey = "waa_YOUR_API_KEY"
let base = "https://webanalyzerapp.com/api/v1"
let siteId = 1
struct Summary: Codable {
let visitors: Int
let sessions: Int
let pageViews: Int
let bounceRate: Double
let avgDuration: Int
enum CodingKeys: String, CodingKey {
case visitors, sessions
case pageViews = "page_views"
case bounceRate = "bounce_rate"
case avgDuration = "avg_duration"
}
}
struct ApiResponse<T: Codable>: Codable { let data: T }
func apiRequest(_ path: String) async throws -> Data {
var url = URLComponents(string: "\(base)\(path)")!
var request = URLRequest(url: url.url!)
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Accept")
let (data, _) = try await URLSession.shared.data(for: request)
return data
}
func trackEvent(name: String, payload: [String: String]) async throws {
var request = URLRequest(url: URL(string: "\(base)/websites/\(siteId)/track")!)
request.httpMethod = "POST"
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try JSONEncoder().encode(["name": name, "payload": payload])
_ = try await URLSession.shared.data(for: request)
}
// Usage
Task {
let data = try await apiRequest("/websites/\(siteId)/summary?from=2025-01-01&to=2025-01-31")
let summary = try JSONDecoder().decode(ApiResponse<Summary>.self, from: data).data
print("Visitors: \(summary.visitors), Sessions: \(summary.sessions)")
try await trackEvent(name: "purchase", payload: ["order_id": "ORD-123", "value": "49.99"])
}
Using reqwest + serde
// Cargo.toml:
// [dependencies]
// reqwest = { version = "0.12", features = ["json"] }
// serde = { version = "1", features = ["derive"] }
// serde_json = "1"
// tokio = { version = "1", features = ["full"] }
use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
const API_KEY: &str = "waa_YOUR_API_KEY";
const BASE: &str = "https://webanalyzerapp.com/api/v1";
const SITE_ID: u32 = 1;
#[derive(Debug, Deserialize)]
struct Summary {
visitors: u32,
sessions: u32,
page_views: u32,
bounce_rate: f64,
avg_duration: u32,
}
#[derive(Deserialize)]
struct ApiResponse<T> { data: T }
#[derive(Serialize)]
struct TrackEvent {
name: String,
payload: HashMap<String, String>,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new();
// Fetch summary
let resp: ApiResponse<Summary> = client
.get(format!("{BASE}/websites/{SITE_ID}/summary"))
.bearer_auth(API_KEY)
.query(&[("from", "2025-01-01"), ("to", "2025-01-31")])
.send().await?
.json().await?;
println!("Visitors: {}, Sessions: {}", resp.data.visitors, resp.data.sessions);
// Track event
let mut payload = HashMap::new();
payload.insert("order_id".into(), "ORD-123".into());
payload.insert("value".into(), "49.99".into());
client.post(format!("{BASE}/websites/{SITE_ID}/track"))
.bearer_auth(API_KEY)
.json(&TrackEvent { name: "purchase".into(), payload })
.send().await?;
Ok(())
}
Dart / Flutter with http package
// pubspec.yaml: dependencies: http: ^1.2.0
import 'dart:convert';
import 'package:http/http.dart' as http;
const apiKey = 'waa_YOUR_API_KEY';
const base = 'https://webanalyzerapp.com/api/v1';
const siteId = 1;
final headers = {
'Authorization': 'Bearer $apiKey',
'Accept': 'application/json',
};
// ── Fetch summary ───────────────────────────────────────────
Future<Map<String, dynamic>> fetchSummary(String from, String to) async {
final uri = Uri.parse('$base/websites/$siteId/summary')
.replace(queryParameters: {'from': from, 'to': to});
final res = await http.get(uri, headers: headers);
return jsonDecode(res.body)['data'];
}
// ── Track event ─────────────────────────────────────────────
Future<void> trackEvent(String name, Map<String, String> payload) async {
await http.post(
Uri.parse('$base/websites/$siteId/track'),
headers: {...headers, 'Content-Type': 'application/json'},
body: jsonEncode({'name': name, 'payload': payload}),
);
}
// ── Realtime count ──────────────────────────────────────────
Future<int> realtimeVisitors() async {
final res = await http.get(
Uri.parse('$base/websites/$siteId/realtime'),
headers: headers,
);
return jsonDecode(res.body)['data']['active_visitors'];
}
void main() async {
final summary = await fetchSummary('2025-01-01', '2025-01-31');
print('Visitors: ${summary["visitors"]}, Sessions: ${summary["sessions"]}');
await trackEvent('purchase', {'order_id': 'ORD-123', 'value': '49.99'});
final live = await realtimeVisitors();
print('Live now: $live');
}
Flutter widget example
import 'package:flutter/material.dart';
class AnalyticsCard extends StatefulWidget {
@override
State<AnalyticsCard> createState() => _AnalyticsCardState();
}
class _AnalyticsCardState extends State<AnalyticsCard> {
Map<String, dynamic>? summary;
@override
void initState() {
super.initState();
fetchSummary('2025-01-01', '2025-01-31').then((data) {
setState(() => summary = data);
});
}
@override
Widget build(BuildContext context) {
if (summary == null) return const CircularProgressIndicator();
return Row(children: [
_stat('Visitors', summary!['visitors']),
_stat('Sessions', summary!['sessions']),
_stat('Bounce', '${summary!["bounce_rate"]}%'),
]);
}
Widget _stat(String label, dynamic value) => Expanded(
child: Card(child: Padding(
padding: const EdgeInsets.all(16),
child: Column(children: [
Text(label, style: const TextStyle(color: Colors.grey)),
Text('$value', style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
]),
)),
);
}