Web Analyzer App
/ Docs

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
Base URL: https://webanalyzerapp.com/api/v1
Property Value
Rate limit60 requests / minute per key
Response formatJSON (UTF-8)
Date parametersYYYY-MM-DD (UTC)
Default date rangeLast 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

GET /api/v1/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

GET /api/v1/websites/{id}

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)

GET /api/v1/websites/{id}/summary

Returns key performance indicators for the given date range.

ParameterDescription
fromStart date YYYY-MM-DD (default: 30 days ago)
toEnd 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

GET /api/v1/websites/{id}/timeseries

Returns daily visitor and page view counts for the given date range — ideal for building charts.

ParameterDescription
fromStart date (default: 30 days ago)
toEnd 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

GET /api/v1/websites/{id}/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"
  }
}
Tip: Poll this endpoint every 30–60 seconds to build a live visitor counter. No date parameters are needed.

7. Top pages

GET /api/v1/websites/{id}/pages

Returns the most-visited pages ranked by view count.

ParameterDescription
fromStart date (default: 30 days ago)
toEnd date (default: today)
limitMax 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

GET /api/v1/websites/{id}/sources

Returns session counts broken down by acquisition channel (organic, direct, referral, paid, email, social).

ParameterDescription
fromStart date (default: 30 days ago)
toEnd 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

GET /api/v1/websites/{id}/events

Returns custom event counts. Pass name to filter to a specific event type.

ParameterDescription
fromStart date (default: 30 days ago)
toEnd date (default: today)
nameFilter 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

GET /api/v1/websites/{id}/visitors

Returns a paginated list of visitors with device and location metadata.

ParameterDescription
fromStart date (default: 30 days ago)
toEnd date (default: today)
limitResults per page 1–100 (default: 20)
pagePage 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

GET /api/v1/websites/{id}/sessions

Returns a paginated list of sessions with full metadata including entry/exit pages, UTM parameters, device, and geo info.

ParameterDescription
fromStart date (default: 30 days ago)
toEnd date (default: today)
limitResults per page 1–100 (default: 20)
pagePage 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

GET /api/v1/websites/{id}/countries

Returns session counts broken down by country (ISO 3166 two-letter code).

ParameterDescription
fromStart date (default: 30 days ago)
toEnd date (default: today)
limitMax 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

GET /api/v1/websites/{id}/devices

Returns a combined breakdown of device types, browsers, and operating systems.

ParameterDescription
fromStart date (default: 30 days ago)
toEnd 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

POST /api/v1/websites/{id}/track
FieldTypeDescription
namestringEvent name (required)
payloadobjectKey/value metadata (optional)
visitor_idstringVisitor UUID to link to (optional)
urlstringPage 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

POST /api/v1/websites/{id}/track/batch

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"}}]}'
Deep dive: See the Server-side Tracking guide for language-specific examples (PHP, Node.js, Python, Ruby) and advanced use cases.

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`);
Generating your key: Go to Profile → API Keys, enter a name for the key (e.g. "Zapier integration"), and click Generate key. Copy the token immediately — it is shown only once.

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)),
      ]),
    )),
  );
}
All examples use the same endpoints. Just change the URL path and parameters to call any endpoint documented above — summary, pages, sources, events, sessions, visitors, countries, devices, realtime, or server-side tracking.

Help & FAQ

Find answers instantly