Visit Trends
Return an AI visit time series, optionally grouped by category.
curl -X GET "https://api.hardal.ai/v1/reports/ai-visibility/graphs?tenantId=123e4567-e89b-12d3-a456-426614174000&sourceId=123e4567-e89b-12d3-a456-426614174000&timeframe=today&startDate=example_string&endDate=example_string&timezone=example_string&interval=minute&path=example_string&channel=example_string&source=example_string&device=example_string&os=example_string&country=USA&city=New%20York&eventName=John%20Doe&isAi=true&agentType=example_string&agentName=John%20Doe&agentPlatform=example_string&agentCategory=example_string&pagePath=example_string&category=agentName" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN"
import requests
import json
url = "https://api.hardal.ai/v1/reports/ai-visibility/graphs?tenantId=123e4567-e89b-12d3-a456-426614174000&sourceId=123e4567-e89b-12d3-a456-426614174000&timeframe=today&startDate=example_string&endDate=example_string&timezone=example_string&interval=minute&path=example_string&channel=example_string&source=example_string&device=example_string&os=example_string&country=USA&city=New%20York&eventName=John%20Doe&isAi=true&agentType=example_string&agentName=John%20Doe&agentPlatform=example_string&agentCategory=example_string&pagePath=example_string&category=agentName"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
}
response = requests.get(url, headers=headers)
print(response.json())
const response = await fetch("https://api.hardal.ai/v1/reports/ai-visibility/graphs?tenantId=123e4567-e89b-12d3-a456-426614174000&sourceId=123e4567-e89b-12d3-a456-426614174000&timeframe=today&startDate=example_string&endDate=example_string&timezone=example_string&interval=minute&path=example_string&channel=example_string&source=example_string&device=example_string&os=example_string&country=USA&city=New%20York&eventName=John%20Doe&isAi=true&agentType=example_string&agentName=John%20Doe&agentPlatform=example_string&agentCategory=example_string&pagePath=example_string&category=agentName", {
method: "GET",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
}
});
const data = await response.json();
console.log(data);
package main
import (
"fmt"
"net/http"
)
func main() {
req, err := http.NewRequest("GET", "https://api.hardal.ai/v1/reports/ai-visibility/graphs?tenantId=123e4567-e89b-12d3-a456-426614174000&sourceId=123e4567-e89b-12d3-a456-426614174000&timeframe=today&startDate=example_string&endDate=example_string&timezone=example_string&interval=minute&path=example_string&channel=example_string&source=example_string&device=example_string&os=example_string&country=USA&city=New%20York&eventName=John%20Doe&isAi=true&agentType=example_string&agentName=John%20Doe&agentPlatform=example_string&agentCategory=example_string&pagePath=example_string&category=agentName", nil)
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
fmt.Println("Response Status:", resp.Status)
}
require 'net/http'
require 'json'
uri = URI('https://api.hardal.ai/v1/reports/ai-visibility/graphs?tenantId=123e4567-e89b-12d3-a456-426614174000&sourceId=123e4567-e89b-12d3-a456-426614174000&timeframe=today&startDate=example_string&endDate=example_string&timezone=example_string&interval=minute&path=example_string&channel=example_string&source=example_string&device=example_string&os=example_string&country=USA&city=New%20York&eventName=John%20Doe&isAi=true&agentType=example_string&agentName=John%20Doe&agentPlatform=example_string&agentCategory=example_string&pagePath=example_string&category=agentName')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Content-Type'] = 'application/json'
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
response = http.request(request)
puts response.body
{
"success": true,
"data": {
"range": {
"startDate": "2026-10-01",
"endDate": "2026-10-07",
"timezone": "Europe/Istanbul"
},
"points": [
{
"date": "2026-10-01",
"visits": 312,
"categoryValue": "Googlebot"
},
{
"date": "2026-10-01",
"visits": 156,
"categoryValue": "GPTBot"
},
{
"date": "2026-10-01",
"visits": 104,
"categoryValue": "ClaudeBot"
},
{
"date": "2026-10-01",
"visits": 52,
"categoryValue": "PerplexityBot"
},
{
"date": "2026-10-02",
"visits": 360,
"categoryValue": "Googlebot"
},
{
"date": "2026-10-02",
"visits": 180,
"categoryValue": "GPTBot"
},
{
"date": "2026-10-02",
"visits": 120,
"categoryValue": "ClaudeBot"
},
{
"date": "2026-10-02",
"visits": 60,
"categoryValue": "PerplexityBot"
},
{
"date": "2026-10-03",
"visits": 384,
"categoryValue": "Googlebot"
},
{
"date": "2026-10-03",
"visits": 192,
"categoryValue": "GPTBot"
},
{
"date": "2026-10-03",
"visits": 128,
"categoryValue": "ClaudeBot"
},
{
"date": "2026-10-03",
"visits": 64,
"categoryValue": "PerplexityBot"
},
{
"date": "2026-10-04",
"visits": 360,
"categoryValue": "Googlebot"
},
{
"date": "2026-10-04",
"visits": 180,
"categoryValue": "GPTBot"
},
{
"date": "2026-10-04",
"visits": 120,
"categoryValue": "ClaudeBot"
},
{
"date": "2026-10-04",
"visits": 60,
"categoryValue": "PerplexityBot"
},
{
"date": "2026-10-05",
"visits": 396,
"categoryValue": "Googlebot"
},
{
"date": "2026-10-05",
"visits": 198,
"categoryValue": "GPTBot"
},
{
"date": "2026-10-05",
"visits": 132,
"categoryValue": "ClaudeBot"
},
{
"date": "2026-10-05",
"visits": 66,
"categoryValue": "PerplexityBot"
},
{
"date": "2026-10-06",
"visits": 396,
"categoryValue": "Googlebot"
},
{
"date": "2026-10-06",
"visits": 198,
"categoryValue": "GPTBot"
},
{
"date": "2026-10-06",
"visits": 132,
"categoryValue": "ClaudeBot"
},
{
"date": "2026-10-06",
"visits": 66,
"categoryValue": "PerplexityBot"
},
{
"date": "2026-10-07",
"visits": 312,
"categoryValue": "Googlebot"
},
{
"date": "2026-10-07",
"visits": 156,
"categoryValue": "GPTBot"
},
{
"date": "2026-10-07",
"visits": 104,
"categoryValue": "ClaudeBot"
},
{
"date": "2026-10-07",
"visits": 52,
"categoryValue": "PerplexityBot"
}
]
},
"timestamp": "2026-11-01T09:30:00.000Z",
"path": "/v1/reports/ai-visibility/graphs",
"requestId": "req_01JD2V8Q9Z6R4XK7T3M5N8P2WA"
}
{
"success": false,
"error": {
"code": "tenant-required",
"message": "tenant-required"
},
"timestamp": "2026-11-01T09:30:00.000Z",
"path": "/v1/reports/ai-visibility/graphs",
"requestId": "req_01JD2V8Q9Z6R4XK7T3M5N8P2WA"
}
{
"success": false,
"error": {
"code": "validation-error",
"message": "Validation failed for 'timeframe': timeframe must be one of: today, yesterday, last7days, last30days, custom",
"details": {
"field": "timeframe",
"message": "timeframe must be one of: today, yesterday, last7days, last30days, custom"
}
},
"timestamp": "2026-11-01T09:30:00.000Z",
"path": "/v1/reports/ai-visibility/graphs",
"requestId": "req_01JD2V8Q9Z6R4XK7T3M5N8P2WA"
}
{
"success": false,
"error": {
"code": "bearer-required",
"message": "bearer-required"
},
"timestamp": "2026-11-01T09:30:00.000Z",
"path": "/v1/reports/ai-visibility/graphs",
"requestId": "req_01JD2V8Q9Z6R4XK7T3M5N8P2WA"
}
{
"success": false,
"error": {
"code": "invalid-credential",
"message": "invalid-credential"
},
"timestamp": "2026-11-01T09:30:00.000Z",
"path": "/v1/reports/ai-visibility/graphs",
"requestId": "req_01JD2V8Q9Z6R4XK7T3M5N8P2WA"
}
{
"success": false,
"error": {
"code": "credential-revoked",
"message": "credential-revoked"
},
"timestamp": "2026-11-01T09:30:00.000Z",
"path": "/v1/reports/ai-visibility/graphs",
"requestId": "req_01JD2V8Q9Z6R4XK7T3M5N8P2WA"
}
{
"success": false,
"error": {
"code": "credential-expired",
"message": "credential-expired"
},
"timestamp": "2026-11-01T09:30:00.000Z",
"path": "/v1/reports/ai-visibility/graphs",
"requestId": "req_01JD2V8Q9Z6R4XK7T3M5N8P2WA"
}
{
"success": false,
"error": {
"code": "tenant-not-granted",
"message": "tenant-not-granted",
"details": {
"tenantId": "2f1c7c3e-6c1a-4f1e-9b7a-2d8e4c1a9f10"
}
},
"timestamp": "2026-11-01T09:30:00.000Z",
"path": "/v1/reports/ai-visibility/graphs",
"requestId": "req_01JD2V8Q9Z6R4XK7T3M5N8P2WA"
}
{
"success": false,
"error": {
"code": "source-does-not-belong-to-tenant",
"message": "source-does-not-belong-to-tenant"
},
"timestamp": "2026-11-01T09:30:00.000Z",
"path": "/v1/reports/ai-visibility/graphs",
"requestId": "req_01JD2V8Q9Z6R4XK7T3M5N8P2WA"
}
{
"success": false,
"error": {
"code": "insufficient-scope",
"message": "insufficient-scope",
"details": {
"required": "reports:read"
}
},
"timestamp": "2026-11-01T09:30:00.000Z",
"path": "/v1/reports/ai-visibility/graphs",
"requestId": "req_01JD2V8Q9Z6R4XK7T3M5N8P2WA"
}
{
"success": false,
"error": {
"code": "tenant-not-found",
"message": "tenant-not-found",
"details": {
"tenantId": "2f1c7c3e-6c1a-4f1e-9b7a-2d8e4c1a9f10"
}
},
"timestamp": "2026-11-01T09:30:00.000Z",
"path": "/v1/reports/ai-visibility/graphs",
"requestId": "req_01JD2V8Q9Z6R4XK7T3M5N8P2WA"
}
{
"success": false,
"error": {
"code": "source-not-found",
"message": "source-not-found",
"details": {
"sourceId": "7b0e5a54-1d43-4c8e-8d2f-5a9c3e6b1f27"
}
},
"timestamp": "2026-11-01T09:30:00.000Z",
"path": "/v1/reports/ai-visibility/graphs",
"requestId": "req_01JD2V8Q9Z6R4XK7T3M5N8P2WA"
}
{
"success": false,
"error": {
"code": "rate-limited",
"message": "rate-limited",
"details": {
"retryAfterSeconds": 30
}
},
"timestamp": "2026-11-01T09:30:00.000Z",
"path": "/v1/reports/ai-visibility/graphs",
"requestId": "req_01JD2V8Q9Z6R4XK7T3M5N8P2WA"
}
/v1/reports/ai-visibility/graphs
Target server for requests. Edit to use your own host.
OAuth access token (token exchange) or organization API key.
UUID of the tenant to report on. Send it exactly once. GET /v1/workspace/access lists the tenants this credential can read.
UUID of one source of the tenant. Omit it to include every source of the tenant.
Reporting period. The presets today, yesterday, last7days and last30days cover whole calendar days ending today in the request timezone; last7days is today and the six days before it. Use custom together with startDate and endDate. Defaults to today.
First day of a custom period, YYYY-MM-DD, inclusive. Send it only with timeframe=custom, together with endDate.
Last day of a custom period, YYYY-MM-DD, inclusive. Send it only with timeframe=custom; it must not be before startDate.
IANA time zone name such as Europe/Istanbul or America/New_York. It sets the calendar-day boundaries of the period and of the time buckets. Defaults to Europe/Istanbul.
Bucket size of points. Defaults to hour; send day for periods longer than a day.
Page path, for example /pricing.
Marketing channel, for example OrganicSearch.
Traffic source, for example google. This is not sourceId.
Device type, for example desktop or mobile.
Operating system, for example iOS.
Two-letter country code, for example TR.
City name, for example Istanbul.
Event name, for example purchase.
true returns AI agents only and false returns other crawlers only. Omit it for both.
Filter by agent type: ai or nonAi.
Filter by agent name, for example GPTBot. Exact match. Separate several values with commas.
Filter by the company that runs the agent, for example OpenAI. Exact match. Separate several values with commas.
Filter by agent category, for example ai-crawler or search-bot. Exact match. Separate several values with commas.
Only count visits to these page paths. Exact match. Separate several values with commas.
Split the series by agentName, agentPlatform, agentCategory or pagePath; each point then carries the value in categoryValue. Omit it for total visits per bucket. Only the 250 values with the most visits are returned.
Request Preview
Response
Response will appear here after sending the request
Authentication
Bearer token. OAuth access token (token exchange) or organization API key.
Query Parameters
UUID of the tenant to report on. Send it exactly once. GET /v1/workspace/access lists the tenants this credential can read.
UUID of one source of the tenant. Omit it to include every source of the tenant.
Reporting period. The presets today, yesterday, last7days and last30days cover whole calendar days ending today in the request timezone; last7days is today and the six days before it. Use custom together with startDate and endDate. Defaults to today.
First day of a custom period, YYYY-MM-DD, inclusive. Send it only with timeframe=custom, together with endDate.
Last day of a custom period, YYYY-MM-DD, inclusive. Send it only with timeframe=custom; it must not be before startDate.
IANA time zone name such as Europe/Istanbul or America/New_York. It sets the calendar-day boundaries of the period and of the time buckets. Defaults to Europe/Istanbul.
Bucket size of points. Defaults to hour; send day for periods longer than a day.
Page path, for example /pricing.
Marketing channel, for example OrganicSearch.
Traffic source, for example google. This is not sourceId.
Device type, for example desktop or mobile.
Operating system, for example iOS.
Two-letter country code, for example TR.
City name, for example Istanbul.
Event name, for example purchase.
true returns AI agents only and false returns other crawlers only. Omit it for both.
Filter by agent type: ai or nonAi.
Filter by agent name, for example GPTBot. Exact match. Separate several values with commas.
Filter by the company that runs the agent, for example OpenAI. Exact match. Separate several values with commas.
Filter by agent category, for example ai-crawler or search-bot. Exact match. Separate several values with commas.
Only count visits to these page paths. Exact match. Separate several values with commas.
Split the series by agentName, agentPlatform, agentCategory or pagePath; each point then carries the value in categoryValue. Omit it for total visits per bucket. Only the 250 values with the most visits are returned.
Responses
true