Public API Reference

Access your LogLens analytics data programmatically. Build custom dashboards, integrate with your tools, or automate reporting.

Base URL

https://api.loglens.ai

1 Authentication

All API requests require authentication using an API key. API keys can be created in your Organization Settings.

Pass your API key in the Authorization header:

Request Header
Authorization: Bearer llapi_your_api_key_here

Note: API keys start with llapi_ to distinguish them from ingest keys.

2 Rate Limits

Rate limits are applied per organization, per hour. The limit depends on your plan:

Plan Requests/Hour
Free No API access
Starter 100
Professional 500
Enterprise 2,000

Rate limit headers are included in every response:

X-RateLimit-Limit: 500
X-RateLimit-Remaining: 499
X-RateLimit-Reset: 2024-01-21T18:00:00Z

3 Error Handling

The API uses standard HTTP status codes and returns JSON error responses:

Status Description
200 Success
401 Invalid or missing API key
403 API access not available on your plan
404 Website not found
429 Rate limit exceeded
500 Internal server error
Error Response Example
{
  "detail": "Rate limit exceeded",
  "limit": 100,
  "reset_at": "2024-01-21T18:00:00Z"
}

4 Date Ranges

All analytics endpoints accept a time range. Use hours for a rolling window, or start and end for a specific date range (ISO 8601 format).

Rolling window (last 7 days)
GET /websites/{id}/bots?hours=168
Specific date range
GET /websites/{id}/bots?start=2026-03-05&end=2026-03-27

If neither is provided, the default is hours=24. When start/end are present, hours is ignored.

Googlebot Split Variants

The bots endpoint accepts split_variants=true to show Googlebot Desktop and Googlebot Smartphone as separate entries instead of merged:

Split Googlebot variants
GET /websites/{id}/bots?hours=168&split_variants=true

Command-Line Interface (CLI) NEW

Access your LogLens analytics from the terminal. Query traffic, bots, SEO data, and more — with table, JSON, and CSV output.

Installation

Terminal
npm install -g @loglens/cli

Setup

Terminal
# Save your API key
loglens config set-key YOUR_API_KEY

# Set a default website (optional)
loglens config set-website YOUR_WEBSITE_ID

Examples

Terminal
# List websites
loglens websites

# Bot breakdown (last 7 days, JSON output)
loglens bots -w <id> -h 168 --json

# SEO crawl budget by directory
loglens seo budget-urls -w <id> --dir /blog/

# Export paths as CSV
loglens paths -w <id> --csv > paths.csv

# Pipe to jq
loglens bots -w <id> --json | jq '.[].name'

# Query a specific date range
loglens bots -w <id> --start 2026-03-05 --end 2026-03-27

# Show Googlebot Desktop and Smartphone separately
loglens bots -w <id> --split-variants

Run loglens --help for the full list of commands. See the help docs for detailed usage.

MCP Server Integration NEW

Connect LogLens to AI assistants like Claude, Cursor, and other tools that support the Model Context Protocol (MCP). The MCP server exposes all API endpoints as tools that AI assistants can call automatically. All tools support start, end, and hours date parameters.

MCP Endpoint

https://mcp-loglens.com/mcp?apiKey=YOUR_API_KEY

Setup

Install the mcp-remote bridge (requires Node.js 18+):

Terminal
npm install -g mcp-remote

Claude Desktop

Add to your claude_desktop_config.json:

claude_desktop_config.json
{
  "mcpServers": {
    "loglens": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://mcp-loglens.com/mcp?apiKey=YOUR_API_KEY"
      ]
    }
  }
}

If npx isn't found, use the full path to node and mcp-remote. Run which node and which mcp-remote to find them.

Cursor

Go to Settings → MCP Servers and add the endpoint URL:

MCP Server URL
https://mcp-loglens.com/mcp?apiKey=YOUR_API_KEY

Other MCP Clients

Any MCP-compatible client can connect using the endpoint URL. Clients that don't support remote servers directly can use the mcp-remote bridge as shown above.

Available Tools

23 tools covering all LogLens analytics — traffic, bots, SEO, crawl budget, index coverage, and more. AI assistants will discover these automatically when connected.

Tool Description
list_websitesList all websites in your account
get_summaryTraffic summary for a website
get_trafficTraffic time-series data
get_botsBot and crawler breakdown (supports split_variants for Googlebot Desktop/Smartphone)
get_seoSEO crawler analytics
get_budget_urlsPer-URL crawl budget breakdown
get_url_patternsAuto-detected URL patterns
get_index_coverageGoogle index coverage summary

Plus 15 more tools for paths, geography, status codes, IPs, referrers, devices, robots.txt, sitemap history, and site events.


GET

List Websites

Returns all websites accessible to your API key.

Endpoint
GET /public/v1/websites

Response

{
  "websites": [
    {
      "id": "ws_abc123",
      "domain": "example.com",
      "name": "Main Website",
      "created_at": "2024-01-01T00:00:00Z"
    }
  ],
  "count": 1
}
GET

Get Summary

Returns summary statistics for a website.

Endpoint
GET /public/v1/websites/{website_id}/summary

Parameters

Name Type Description
hours integer Time period in hours (default: 24, max: 8760)
GET

Get Traffic

Returns traffic data with hourly breakdown.

GET /public/v1/websites/{website_id}/traffic?hours=24
GET

Get Bots

Returns bot analytics including identification and verification status.

GET /public/v1/websites/{website_id}/bots?hours=24
GET

Get Paths

Returns path/URL analytics sorted by request count.

GET /public/v1/websites/{website_id}/paths?hours=24&limit=100

Parameters

Name Type Description
hours integer Time period (default: 24)
limit integer Max results (default: 100, max: 1000)
GET

Get Geography

Returns geographic distribution of traffic by country and city.

GET /public/v1/websites/{website_id}/geography?hours=24
GET

Get Status Codes

Returns HTTP status code distribution with hourly breakdown.

GET /public/v1/websites/{website_id}/status-codes?hours=24
GET

Get IPs

Returns IP address and IP range analytics.

GET /public/v1/websites/{website_id}/ips?hours=24
GET

Get Referrers

Returns referrer domain analytics - see which sites are sending traffic to you.

GET /public/v1/websites/{website_id}/referrers?hours=24&limit=100

Query Parameters

  • hours - Time period (1-8760, default: 24)
  • limit - Maximum referrers to return (1-500, default: 100)
GET

Get Devices

Returns device, browser, and operating system analytics.

GET /public/v1/websites/{website_id}/devices?hours=24

Response includes

  • browsers - Browser breakdown (Chrome, Safari, Firefox, etc.)
  • operating_systems - OS breakdown (Windows, macOS, iOS, Android, etc.)
  • device_types - Device type breakdown (Desktop, Mobile, Tablet)
GET

Get SEO Stats

Returns search engine crawler statistics - Googlebot, Bingbot, etc.

GET /public/v1/websites/{website_id}/seo?hours=24&bot=googlebot

Query Parameters

  • hours - Time period (1-8760, default: 24)
  • bot - Filter by specific bot (optional, e.g. "googlebot", "bingbot")

Response includes

  • crawler_requests - Total crawler requests
  • verified_requests - Verified (legitimate) crawler requests
  • unverified_suspicious - Potentially spoofed crawler requests
  • avg_response_time_ms - Average response time to crawlers
  • top_crawlers - Breakdown by crawler
GET

LLM / AI Crawler Analytics

NEW

Dedicated analytics for LLM and AI crawlers (GPTBot, ClaudeBot, PerplexityBot, Google-Extended, etc.). Returns per-bot time series, crawler verification status, and the pages those bots are reading.

Endpoint
GET /public/v1/websites/{website_id}/llms?hours=168

Query Parameters

Name Type Description
hours integer Rolling window in hours (default: 24, max: 8760)
start string ISO 8601 start timestamp (use with end instead of hours)
end string ISO 8601 end timestamp
countries string Comma-separated ISO country codes to filter by (e.g. US,GB,DE)

Response

{
  "total_requests": 184201,
  "total_ai_requests": 21084,
  "ai_percentage": 11.4,
  "unique_crawlers": 7,
  "unique_pages_crawled": 3419,
  "period_hours": 168,
  "granularity": "hourly",
  "crawlers": [
    {
      "bot_name": "GPTBot",
      "requests": 12847,
      "unique_pages": 2105,
      "verified_count": 12840,
      "unverified_count": 7,
      "verification_status": "verified"
    }
  ],
  "bot_hourly": [
    { "time_bucket": "2026-05-09-14-00", "GPTBot": 432, "ClaudeBot": 118 }
  ],
  "bot_names": ["GPTBot", "ClaudeBot", "PerplexityBot"],
  "pages": [
    {
      "path": "/blog/launch",
      "requests": 412,
      "unique_bots": 5,
      "status_2xx": 410,
      "status_4xx": 2
    }
  ]
}

Available on Starter and above.

GET

Crawl Budget URLs

Per-URL crawl budget breakdown within a directory. Shows which paths search engine crawlers are spending budget on, so you can spot waste.

GET /public/v1/websites/{website_id}/seo/budget-urls?hours=168&directory=/blog/

Query Parameters

Name Type Description
hoursintegerTime window (default: 24, max: 8760)
directorystringDirectory prefix to drill into (default: /)
botstringFilter to a single crawler (e.g. googlebot)
pageintegerPage number (default: 1)
page_sizeintegerResults per page (default: 50, max: 500)
params_filterstringFilter by URL params (e.g. with_params, no_params)
file_typestringFilter by file extension (e.g. html, pdf)

Response

{
  "urls": [
    {
      "path": "/blog/seo-guide",
      "requests": 842,
      "percentage": 12.4,
      "daily_avg": 120.3
    }
  ],
  "total": 317,
  "page": 1,
  "page_size": 50,
  "directory": "/blog/"
}

Available on Starter and above.

GET

URL Patterns

Auto-detected URL pattern templates with crawl frequency. Variable segments (numbers, UUIDs, dates) are replaced with placeholders so you can see how crawlers treat each route family.

GET /public/v1/websites/{website_id}/seo/url-patterns?hours=168&min_urls=3

Query Parameters

Name Type Description
hoursintegerTime window (default: 168, max: 8760)
botstringFilter to a single crawler
min_urlsintegerMinimum unique URLs per pattern to be returned (default: 3)
params_filterstringFilter by URL params
file_typestringFilter by file extension

Response

{
  "patterns": [
    {
      "pattern": "/products/{id}",
      "total_requests": 14820,
      "unique_urls": 1042,
      "daily_avg": 2117.1,
      "pct_of_crawl": 22.5,
      "status_2xx": 14501,
      "status_3xx": 12,
      "status_4xx": 301,
      "status_5xx": 6,
      "pattern_type": "numeric_id",
      "flags": ["high_cardinality"],
      "example_url": "/products/8421"
    }
  ],
  "total_crawl_requests": 65820,
  "days_in_period": 7.0,
  "min_urls": 3
}

Available on Starter and above.

GET

Path Explorer

Hierarchical view of crawled paths with per-bot and per-status breakdowns. Use to build a directory tree of crawler activity.

GET /public/v1/websites/{website_id}/seo/path-explorer?hours=24

Query Parameters

Name Type Description
hoursintegerTime window (default: 24, max: 8760)
botstringFilter to a single crawler
params_filterstringFilter by URL params
file_typestringFilter by file extension

Response

{
  "paths": [
    {
      "path": "/blog/seo-guide",
      "requests": 842,
      "bots": { "Googlebot": 621, "Bingbot": 221 },
      "status_2xx": 840,
      "status_3xx": 0,
      "status_4xx": 2,
      "status_5xx": 0
    }
  ],
  "top_bots": ["Googlebot", "Bingbot", "GPTBot"],
  "total_paths": 3219
}

Available on Starter and above.

GET

Status Consistency

URLs whose HTTP status changed across crawls — e.g. mostly 200 but occasionally 404 or 5xx. Useful for surfacing flapping pages and intermittent errors that hit crawlers.

GET /public/v1/websites/{website_id}/seo/status-consistency?hours=168&min_requests=3

Query Parameters

Name Type Description
hoursintegerTime window (default: 24, max: 8760)
botstringFilter to a single crawler
min_requestsintegerOnly include pages with at least this many crawls (default: 3)
params_filterstringFilter by URL params
file_typestringFilter by file extension

Response

{
  "pages": [
    {
      "path": "/checkout",
      "total_requests": 214,
      "status_2xx": 198,
      "status_3xx": 0,
      "status_4xx": 12,
      "status_5xx": 4,
      "statuses": { "200": 198, "404": 12, "503": 4 },
      "dominant_class": "2xx",
      "consistency_pct": 92.5,
      "non_2xx_pct": 7.5,
      "impact_score": 16,
      "last_non_2xx": "2026-05-09T18:42:00Z",
      "status_classes": 3
    }
  ],
  "summary": {
    "total_pages_checked": 2418,
    "total_inconsistent": 147,
    "critical": 12,
    "warning": 38,
    "healthy": 97
  }
}

Available on Starter and above.

GET

Crawler Request Log

Raw crawler request log with filtering. Each row is one crawler hit — timestamp, path, status, bot identity and verification, and response time.

GET /public/v1/websites/{website_id}/seo/requests?hours=24&filter_type=4xx&page=1

Query Parameters

Name Type Description
hoursintegerTime window (default: 24, max: 8760)
botstringFilter to a single crawler
filter_typestringOne of: all, verified, unverified, 2xx, 3xx, 4xx, 5xx, slowest
pageintegerPage number (default: 1)
page_sizeintegerResults per page (default: 50, max: 500)
params_filterstringFilter by URL params
file_typestringFilter by file extension

Response

{
  "items": [
    {
      "timestamp": "2026-05-09T18:42:11Z",
      "path": "/blog/old-post",
      "status": 404,
      "method": "GET",
      "client_ip": "66.249.66.1",
      "user_agent": "Mozilla/5.0 (compatible; Googlebot/2.1; ...)",
      "bot_name": "Googlebot",
      "bot_verified": true,
      "bot_verification_status": "verified",
      "time_taken_ms": 182.4
    }
  ],
  "total_count": 312,
  "page": 1,
  "page_size": 50,
  "total_pages": 7,
  "has_next": true,
  "has_prev": false,
  "filter": "4xx"
}

Available on Starter and above.

GET

robots.txt Audit

Fetches your live robots.txt, parses the rule groups, and cross-references them against actual crawler activity to surface violations (bots hitting paths they were told to skip).

GET /public/v1/websites/{website_id}/seo/robots?hours=168

Query Parameters

Name Type Description
hoursintegerTime window for violation lookup (default: 24, max: 8760)
botstringLimit violation check to a single crawler

Response

{
  "domain": "example.com",
  "status": 200,
  "fetch_blocked": false,
  "using_saved": false,
  "raw_content": "User-agent: *\nDisallow: /admin/\n...",
  "rule_groups": [
    {
      "user_agent": "*",
      "rules": [
        { "type": "disallow", "path": "/admin/" }
      ]
    }
  ],
  "sitemaps": ["https://example.com/sitemap.xml"],
  "violations": [
    {
      "bot_name": "SemrushBot",
      "path": "/admin/login",
      "requests": 42,
      "rule": "Disallow: /admin/"
    }
  ],
  "violations_total": 42,
  "violation_summary": {
    "SemrushBot": { "total_violations": 1, "total_requests": 42, "unique_paths": 1 }
  },
  "history": [],
  "is_new_version": false,
  "current_hash": "a1b2c3..."
}

Available on Starter and above.

GET

Get Sitemap Coverage

Returns sitemap coverage data - tracks which URLs from your sitemap have been crawled by search engines, their crawl frequency, and current status.

Endpoint
GET /public/v1/websites/{website_id}/seo/sitemap?page=1&page_size=50&status=never_crawled

Query Parameters

  • page - Page number (default: 1)
  • page_size - Results per page (1-500, default: 50)
  • status - Filter: all, never_crawled, recently_crawled, stale, not_in_sitemap
  • sort - Sort by: path, times_crawled, last_crawled, first_seen
  • sort_dir - Sort direction: asc, desc

Response fields (per URL)

  • url - Full URL from sitemap
  • status - Crawl status: crawled or not_crawled
  • last_crawl_date - Timestamp of most recent crawl
  • crawl_count - Total number of times this URL has been crawled
  • content_type - Content type of the URL (e.g. text/html)
  • response_code - HTTP response code from last crawl
GET

URL Crawl History

Per-URL crawl history — every individual crawl event for a single sitemap URL, including which bot, status code, response time, and country.

GET /public/v1/websites/{website_id}/seo/sitemap/url-history?url_path=/blog/post-1&hours=720

Query Parameters

Name Type Description
url_pathstringRequired. Path of the URL to look up (e.g. /blog/post-1)
hoursintegerTime window (default: 168, max: 8760)
botstringFilter to a single crawler
pageintegerPage number (default: 1)
page_sizeintegerResults per page (default: 50, max: 500)

Response

{
  "url_path": "/blog/post-1",
  "total_crawls": 42,
  "hours": 720,
  "events": [
    {
      "timestamp": "2026-05-09 14:22:00",
      "bot_name": "Googlebot",
      "status": 200,
      "method": "GET",
      "user_agent": "Mozilla/5.0 (compatible; Googlebot/2.1; ...)",
      "response_time_ms": 182,
      "country": "US"
    }
  ],
  "page": 1,
  "page_size": 50,
  "total_pages": 1,
  "bot_summary": {
    "Googlebot": { "count": 38, "last_crawled": "2026-05-09 14:22:00" }
  }
}

Available on Starter and above.

GET

Index Coverage

Get Google index coverage summary for your website.

Endpoint
GET /public/v1/websites/{website_id}/seo/index-coverage

Response

{
  "buckets": {
    "crawled_indexed": 1842,
    "crawled_not_indexed": 356,
    "not_crawled_indexed": 23,
    "not_crawled_not_indexed": 491
  }
}
GET

Index Coverage URLs

Get paginated list of URLs in an index coverage bucket.

Endpoint
GET /public/v1/websites/{website_id}/seo/index-coverage/urls?bucket=crawled_indexed&page=1&page_size=50

Parameters

Name Type Description
bucket string Required. One of: crawled_indexed, crawled_not_indexed, not_crawled_indexed, not_crawled_not_indexed
page integer Page number (default: 1)
page_size integer Results per page (default: 50)

Response

{
  "urls": [
    {
      "url": "https://example.com/blog/post-1",
      "last_crawl_date": "2024-01-28T14:22:00Z",
      "last_status_code": 200
    }
  ],
  "page": 1,
  "page_size": 50,
  "total": 1842
}
GET

Site Events

Annotated, site-wide events that overlay onto charts in the dashboard — deploys, algorithm updates, marketing campaigns, anything you want to correlate against traffic shifts.

GET /public/v1/websites/{website_id}/site-events?start_date=2026-04-01&end_date=2026-05-10

Query Parameters

Name Type Description
start_datestringOptional. Earliest event date (YYYY-MM-DD)
end_datestringOptional. Latest event date (YYYY-MM-DD). Use with start_date.

Response

{
  "events": [
    {
      "website_id": "ws_abc123",
      "event_id": "evt_71d2...",
      "date": "2026-05-08",
      "time": "14:30",
      "title": "Deployed v2.4",
      "category": "deploy",
      "description": "New caching layer rolled out to all regions",
      "created_by": "user_abc",
      "created_at": "2026-05-08T14:31:09Z",
      "updated_at": "2026-05-08T14:31:09Z"
    }
  ]
}

Available on Starter and above.


Operations

Background exports, alert history, and alert configuration for orchestrating LogLens from your own systems.

GET

List Exports

NEW

List your background export jobs for a website with status, row count, file size, and a presigned download URL for completed jobs (URL valid for 7 days).

GET /public/v1/websites/{website_id}/exports

Response

{
  "exports": [
    {
      "job_id": "9c1f...e31",
      "export_type": "seo-requests",
      "status": "completed",
      "row_count": 12842,
      "file_size_bytes": 4129083,
      "created_at": "2026-05-09T10:30:00Z",
      "completed_at": "2026-05-09T10:31:48Z",
      "error": null,
      "filters": "{\"hours\":\"168\",\"filter\":\"4xx\"}",
      "description": "4xx filter | last 7d",
      "download_url": "https://...s3.amazonaws.com/exports/9c1f...e31.csv?X-Amz-..."
    }
  ]
}

Available on Starter and above. Only your own exports are returned.

POST

Create Export

NEW

Queue a new background export job. The export runs asynchronously; poll List Exports for status, or wait for the email notification when it's ready. Files are CSV and links expire after 7 days.

POST /public/v1/websites/{website_id}/exports

Request Body

Name Type Description
export_type string Required. One of: traffic, paths, status-codes, bots, bot-history, referrers, geography, devices, ips, sitemap-coverage, crawl-budget-urls, seo-requests, url-pattern-urls, recommendations-paths-404, recommendations-slow-paths, recommendations-unverified-bots
hours integer Rolling window in hours. Use this or start/end.
start / end string ISO 8601 timestamps for an explicit range.
bot / bot_name string Filter to a single crawler.
countries string Comma-separated ISO country codes.
status / status_code / status_category string Filter by HTTP status (e.g. 404) or category (4xx).
filter string Sub-filter passed through to the underlying endpoint (e.g. verified, slowest for seo-requests).
directory string Directory prefix (used by crawl-budget-urls).
pattern string URL pattern template (used by url-pattern-urls).
params_filter / file_type / path_filter string Optional URL/path filters, mirror the underlying SEO endpoints.

Response

HTTP/1.1 201 Created
{
  "job_id": "9c1f...e31",
  "status": "pending",
  "message": "Export started. You'll receive an email when it's ready."
}

Available on Starter and above.

GET

Alert History

NEW

Fired alerts for a website (anomaly detector, error spikes, new-bot detection, etc.). Sorted most-recent first; includes a count of unacknowledged alerts in the last 30 days.

GET /public/v1/websites/{website_id}/alerts?limit=50&type=traffic_spike

Query Parameters

Name Type Description
limitintegerMax alerts to return (default: 50, max: 500)
typestringFilter to a specific alert_type (e.g. traffic_spike, error_rate, new_bot)
acknowledgedbooleanFilter by acknowledgement state (true/false)

Response

{
  "alerts": [
    {
      "website_id": "ws_abc123",
      "alert_id": "2026-05-09T14:22:00Z#a1b2",
      "alert_type": "traffic_spike",
      "severity": "warning",
      "message": "Traffic 340% above baseline for the last hour",
      "details": {
        "current_value": 12842,
        "baseline_value": 2914,
        "deviation_pct": 340.6
      },
      "acknowledged": false,
      "created_at": "2026-05-09T14:22:00Z"
    }
  ],
  "count": 1,
  "unacknowledged_count": 3
}

Available on Starter and above.

GET

Alert Configuration

NEW

Returns the website's current alert configuration — which alert types are enabled, how often each runs, suppression lists, email/webhook settings, and the anomaly-detector baseline status.

GET /public/v1/websites/{website_id}/alerts/config

Response

{
  "config": {
    "alerts_enabled": true,
    "traffic_alerts_enabled": true,
    "traffic_frequency_minutes": 60,
    "error_alerts_enabled": true,
    "error_frequency_minutes": 5,
    "bot_alerts_enabled": true,
    "bot_frequency_minutes": 1440,
    "seo_alerts_enabled": true,
    "seo_frequency_minutes": 60,
    "suppressed_crawlers": [],
    "suppressed_bots": [],
    "email_notifications_enabled": true,
    "email_disabled_types": [],
    "min_email_severity": 5,
    "digest_enabled": true,
    "min_digest_severity": 3,
    "digest_send_hour_utc": 8,
    "webhook_notifications_enabled": false,
    "webhook_urls": []
  },
  "baseline_status": {
    "status": "ready",
    "message": "Baseline established. Anomaly detection is active.",
    "data_points": 1248,
    "ready": true
  }
}

Available on Starter and above.

GET

Search IP Requests

Returns all requests made by a specific IP address. Useful for investigating suspicious activity.

GET /public/v1/websites/{website_id}/ips/{ip_address}/requests?hours=24&page=1

Query Parameters

  • hours - Time period (1-168, default: 24, max 7 days)
  • page - Page number (default: 1)
  • page_size - Results per page (1-500, default: 50)

Response includes (for each request)

  • timestamp - Request timestamp
  • path - URL path requested
  • method - HTTP method
  • status - HTTP status code
  • user_agent - User agent string
  • is_bot - Whether request was from a bot

Example Usage

$ cURL

curl -X GET \
  "https://api.loglens.ai/public/v1/websites" \
  -H "Authorization: Bearer llapi_your_key_here"

JS JavaScript / Node.js

const response = await fetch(
  'https://api.loglens.ai/public/v1/websites',
  {
    headers: {
      'Authorization': `Bearer ${apiKey}`
    }
  }
);
const data = await response.json();
console.log(data.websites);

PY Python

import requests

response = requests.get(
    "https://api.loglens.ai/public/v1/websites",
    headers={"Authorization": f"Bearer {api_key}"}
)
data = response.json()
print(data["websites"])