Acquisition
Return acquisition metrics grouped by channel, source, medium, or campaign.
curl -X GET "https://api.hardal.ai/v1/reports/acquisition?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&reportingCurrency=example_string&attributionModel=first_touch&attributionWindowDays=example_string&conversionEventNames=%5B%22John%20Doe%22%5D&eventMetricNames=%5B%22John%20Doe%22%5D&trafficScope=consented_traffic&breakdownMode=channels&campaignsBreakdownMode=all&filterField=source&filterValue=example_string&groupBy=none&columns=example_string&aiFocus=true&limit=example_string&offset=example_string&sortBy=source&sortEventMetricName=John%20Doe&sortDirection=asc" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN"
import requests
import json
url = "https://api.hardal.ai/v1/reports/acquisition?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&reportingCurrency=example_string&attributionModel=first_touch&attributionWindowDays=example_string&conversionEventNames=%5B%22John%20Doe%22%5D&eventMetricNames=%5B%22John%20Doe%22%5D&trafficScope=consented_traffic&breakdownMode=channels&campaignsBreakdownMode=all&filterField=source&filterValue=example_string&groupBy=none&columns=example_string&aiFocus=true&limit=example_string&offset=example_string&sortBy=source&sortEventMetricName=John%20Doe&sortDirection=asc"
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/acquisition?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&reportingCurrency=example_string&attributionModel=first_touch&attributionWindowDays=example_string&conversionEventNames=%5B%22John%20Doe%22%5D&eventMetricNames=%5B%22John%20Doe%22%5D&trafficScope=consented_traffic&breakdownMode=channels&campaignsBreakdownMode=all&filterField=source&filterValue=example_string&groupBy=none&columns=example_string&aiFocus=true&limit=example_string&offset=example_string&sortBy=source&sortEventMetricName=John%20Doe&sortDirection=asc", {
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/acquisition?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&reportingCurrency=example_string&attributionModel=first_touch&attributionWindowDays=example_string&conversionEventNames=%5B%22John%20Doe%22%5D&eventMetricNames=%5B%22John%20Doe%22%5D&trafficScope=consented_traffic&breakdownMode=channels&campaignsBreakdownMode=all&filterField=source&filterValue=example_string&groupBy=none&columns=example_string&aiFocus=true&limit=example_string&offset=example_string&sortBy=source&sortEventMetricName=John%20Doe&sortDirection=asc", 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/acquisition?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&reportingCurrency=example_string&attributionModel=first_touch&attributionWindowDays=example_string&conversionEventNames=%5B%22John%20Doe%22%5D&eventMetricNames=%5B%22John%20Doe%22%5D&trafficScope=consented_traffic&breakdownMode=channels&campaignsBreakdownMode=all&filterField=source&filterValue=example_string&groupBy=none&columns=example_string&aiFocus=true&limit=example_string&offset=example_string&sortBy=source&sortEventMetricName=John%20Doe&sortDirection=asc')
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"
},
"previousRange": {
"startDate": "2026-09-24",
"endDate": "2026-09-30",
"timezone": "Europe/Istanbul"
},
"currency": {
"reportingCurrency": "USD",
"excludedCount": 0
},
"controls": {
"attributionModel": "last_non_direct_touch",
"attributionWindowDays": 30,
"conversionEventNames": [
"purchase"
],
"eventMetricNames": [],
"trafficScope": "all_traffic",
"breakdownMode": "channels",
"campaignsBreakdownMode": "all",
"filter": null,
"groupBy": "none",
"columns": [
"source",
"medium",
"campaign",
"channel",
"sessions",
"conversions",
"conversion_rate",
"bounce_rate",
"revenue",
"revenue_per_conversion"
],
"aiFocus": false,
"sortBy": "sessions",
"sortDirection": "desc"
},
"scoreCards": {
"consentedVisitors": {
"value": 1840,
"previousValue": 1600,
"change": 240,
"changeRate": 0.15
},
"sessions": {
"value": 4200,
"previousValue": 3750,
"change": 450,
"changeRate": 0.12
},
"conversions": {
"value": 113,
"previousValue": 105,
"change": 8,
"changeRate": 0.0762
},
"sessionDuration": {
"value": 96,
"previousValue": 88,
"change": 8,
"changeRate": 0.0909,
"formattedValue": "1m 36s",
"previousFormattedValue": "1m 28s"
},
"revenue": {
"value": 8475,
"previousValue": 7875,
"change": 600,
"changeRate": 0.0762
}
},
"trafficBreakdown": [
{
"date": "2026-10-01",
"sessions": 208,
"conversions": 5,
"breakdownKey": "Direct"
},
{
"date": "2026-10-01",
"sessions": 156,
"conversions": 5,
"breakdownKey": "OrganicSearch"
},
{
"date": "2026-10-01",
"sessions": 78,
"conversions": 3,
"breakdownKey": "PaidSearch"
},
{
"date": "2026-10-01",
"sessions": 52,
"conversions": 1,
"breakdownKey": "Referral"
},
{
"date": "2026-10-01",
"sessions": 26,
"conversions": 0,
"breakdownKey": "OrganicSocial"
},
{
"date": "2026-10-02",
"sessions": 240,
"conversions": 6,
"breakdownKey": "Direct"
},
{
"date": "2026-10-02",
"sessions": 180,
"conversions": 6,
"breakdownKey": "OrganicSearch"
},
{
"date": "2026-10-02",
"sessions": 90,
"conversions": 4,
"breakdownKey": "PaidSearch"
},
{
"date": "2026-10-02",
"sessions": 60,
"conversions": 1,
"breakdownKey": "Referral"
},
{
"date": "2026-10-02",
"sessions": 30,
"conversions": 0,
"breakdownKey": "OrganicSocial"
},
{
"date": "2026-10-03",
"sessions": 256,
"conversions": 6,
"breakdownKey": "Direct"
},
{
"date": "2026-10-03",
"sessions": 192,
"conversions": 6,
"breakdownKey": "OrganicSearch"
},
{
"date": "2026-10-03",
"sessions": 96,
"conversions": 4,
"breakdownKey": "PaidSearch"
},
{
"date": "2026-10-03",
"sessions": 64,
"conversions": 1,
"breakdownKey": "Referral"
},
{
"date": "2026-10-03",
"sessions": 32,
"conversions": 0,
"breakdownKey": "OrganicSocial"
},
{
"date": "2026-10-04",
"sessions": 240,
"conversions": 6,
"breakdownKey": "Direct"
},
{
"date": "2026-10-04",
"sessions": 180,
"conversions": 6,
"breakdownKey": "OrganicSearch"
},
{
"date": "2026-10-04",
"sessions": 90,
"conversions": 4,
"breakdownKey": "PaidSearch"
},
{
"date": "2026-10-04",
"sessions": 60,
"conversions": 1,
"breakdownKey": "Referral"
},
{
"date": "2026-10-04",
"sessions": 30,
"conversions": 0,
"breakdownKey": "OrganicSocial"
},
{
"date": "2026-10-05",
"sessions": 264,
"conversions": 6,
"breakdownKey": "Direct"
},
{
"date": "2026-10-05",
"sessions": 198,
"conversions": 6,
"breakdownKey": "OrganicSearch"
},
{
"date": "2026-10-05",
"sessions": 99,
"conversions": 4,
"breakdownKey": "PaidSearch"
},
{
"date": "2026-10-05",
"sessions": 66,
"conversions": 1,
"breakdownKey": "Referral"
},
{
"date": "2026-10-05",
"sessions": 33,
"conversions": 0,
"breakdownKey": "OrganicSocial"
},
{
"date": "2026-10-06",
"sessions": 264,
"conversions": 6,
"breakdownKey": "Direct"
},
{
"date": "2026-10-06",
"sessions": 198,
"conversions": 6,
"breakdownKey": "OrganicSearch"
},
{
"date": "2026-10-06",
"sessions": 99,
"conversions": 4,
"breakdownKey": "PaidSearch"
},
{
"date": "2026-10-06",
"sessions": 66,
"conversions": 1,
"breakdownKey": "Referral"
},
{
"date": "2026-10-06",
"sessions": 33,
"conversions": 0,
"breakdownKey": "OrganicSocial"
},
{
"date": "2026-10-07",
"sessions": 208,
"conversions": 5,
"breakdownKey": "Direct"
},
{
"date": "2026-10-07",
"sessions": 156,
"conversions": 5,
"breakdownKey": "OrganicSearch"
},
{
"date": "2026-10-07",
"sessions": 78,
"conversions": 3,
"breakdownKey": "PaidSearch"
},
{
"date": "2026-10-07",
"sessions": 52,
"conversions": 1,
"breakdownKey": "Referral"
},
{
"date": "2026-10-07",
"sessions": 26,
"conversions": 0,
"breakdownKey": "OrganicSocial"
}
],
"trafficDistribution": {
"rows": [
{
"source": "direct",
"medium": "",
"campaign": "",
"channel": "Direct",
"sessions": 1680,
"conversions": 40,
"conversion_rate": 0.0238,
"bounce_rate": 0.44,
"revenue": 3000,
"revenue_per_conversion": 75,
"eventCounts": {}
},
{
"source": "google",
"medium": "organic",
"campaign": "",
"channel": "OrganicSearch",
"sessions": 1260,
"conversions": 40,
"conversion_rate": 0.0317,
"bounce_rate": 0.38,
"revenue": 3000,
"revenue_per_conversion": 75,
"eventCounts": {}
},
{
"source": "google",
"medium": "cpc",
"campaign": "brand_search",
"channel": "PaidSearch",
"sessions": 630,
"conversions": 26,
"conversion_rate": 0.0413,
"bounce_rate": 0.41,
"revenue": 1950,
"revenue_per_conversion": 75,
"eventCounts": {}
},
{
"source": "partner.example",
"medium": "referral",
"campaign": "",
"channel": "Referral",
"sessions": 420,
"conversions": 7,
"conversion_rate": 0.0167,
"bounce_rate": 0.47,
"revenue": 525,
"revenue_per_conversion": 75,
"eventCounts": {}
},
{
"source": "linkedin",
"medium": "social",
"campaign": "",
"channel": "OrganicSocial",
"sessions": 210,
"conversions": 0,
"conversion_rate": 0,
"bounce_rate": 0.52,
"revenue": 0,
"revenue_per_conversion": 0,
"eventCounts": {}
}
],
"pagination": {
"limit": 50,
"offset": 0,
"total": 5
},
"columns": [
"source",
"medium",
"campaign",
"channel",
"sessions",
"conversions",
"conversion_rate",
"bounce_rate",
"revenue",
"revenue_per_conversion"
],
"eventMetrics": [],
"groupBy": "none",
"sort": {
"sortBy": "sessions",
"sortDirection": "desc"
}
}
},
"timestamp": "2026-11-01T09:30:00.000Z",
"path": "/v1/reports/acquisition",
"requestId": "req_01JD2V8Q9Z6R4XK7T3M5N8P2WA"
}
{
"success": false,
"error": {
"code": "tenant-required",
"message": "tenant-required"
},
"timestamp": "2026-11-01T09:30:00.000Z",
"path": "/v1/reports/acquisition",
"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/acquisition",
"requestId": "req_01JD2V8Q9Z6R4XK7T3M5N8P2WA"
}
{
"success": false,
"error": {
"code": "bearer-required",
"message": "bearer-required"
},
"timestamp": "2026-11-01T09:30:00.000Z",
"path": "/v1/reports/acquisition",
"requestId": "req_01JD2V8Q9Z6R4XK7T3M5N8P2WA"
}
{
"success": false,
"error": {
"code": "invalid-credential",
"message": "invalid-credential"
},
"timestamp": "2026-11-01T09:30:00.000Z",
"path": "/v1/reports/acquisition",
"requestId": "req_01JD2V8Q9Z6R4XK7T3M5N8P2WA"
}
{
"success": false,
"error": {
"code": "credential-revoked",
"message": "credential-revoked"
},
"timestamp": "2026-11-01T09:30:00.000Z",
"path": "/v1/reports/acquisition",
"requestId": "req_01JD2V8Q9Z6R4XK7T3M5N8P2WA"
}
{
"success": false,
"error": {
"code": "credential-expired",
"message": "credential-expired"
},
"timestamp": "2026-11-01T09:30:00.000Z",
"path": "/v1/reports/acquisition",
"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/acquisition",
"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/acquisition",
"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/acquisition",
"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/acquisition",
"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/acquisition",
"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/acquisition",
"requestId": "req_01JD2V8Q9Z6R4XK7T3M5N8P2WA"
}
/v1/reports/acquisition
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 trafficBreakdown. Defaults to 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.
ISO 4217 currency code, for example USD. Amounts are returned in the tenant's reporting currency.
How a conversion is credited to a traffic source: first_touch credits the first visit, last_touch the last visit and last_non_direct_touch the last visit that was not direct. Defaults to the tenant's attribution setting.
How many days before a conversion a visit can receive credit: 7, 14 or 30. Defaults to the tenant's attribution setting.
Event names counted as conversions. Repeat the parameter or separate names with commas. Defaults to the tenant's conversion events; when none are configured, conversions and revenue are 0.
Up to 10 event names to count on each trafficDistribution row. The counts are returned in eventCounts.
Which sessions to include by consent: consented_traffic, unconsented_traffic or all_traffic. Defaults to the tenant's setting.
Dimension of the trafficBreakdown series: channels or campaigns. source and medium are shorthand for campaigns broken down by that field. Defaults to channels.
With breakdownMode=campaigns, break the series down by source, by medium or by both with all. Defaults to all.
Column to filter trafficDistribution by: source, medium, campaign or channel. Requires filterValue. Score cards and trafficBreakdown are not filtered.
Text to look for in filterField. Case-insensitive and matches part of the value. Requires filterField.
Group trafficDistribution rows by one dimension. none returns one row per combination of source, medium, campaign and channel. Defaults to none.
Comma-separated columns to return on each trafficDistribution row: source, medium, campaign, channel, sessions, conversions, conversion_rate, bounce_rate, revenue, revenue_per_conversion. Defaults to all.
true limits the report to traffic from AI assistants such as ChatGPT, Perplexity, Gemini, Claude and Copilot. Defaults to false.
Maximum number of trafficDistribution rows to return, 1 to 500. Defaults to 50.
Number of trafficDistribution rows to skip, 0 to 10000. Defaults to 0.
Column to sort trafficDistribution by. event_metric sorts by the event named in sortEventMetricName. Defaults to sessions.
Event to sort by when sortBy=event_metric. It must be one of eventMetricNames.
asc or desc. Defaults to desc.
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 trafficBreakdown. Defaults to 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.
ISO 4217 currency code, for example USD. Amounts are returned in the tenant's reporting currency.
How a conversion is credited to a traffic source: first_touch credits the first visit, last_touch the last visit and last_non_direct_touch the last visit that was not direct. Defaults to the tenant's attribution setting.
How many days before a conversion a visit can receive credit: 7, 14 or 30. Defaults to the tenant's attribution setting.
Event names counted as conversions. Repeat the parameter or separate names with commas. Defaults to the tenant's conversion events; when none are configured, conversions and revenue are 0.
Up to 10 event names to count on each trafficDistribution row. The counts are returned in eventCounts.
Which sessions to include by consent: consented_traffic, unconsented_traffic or all_traffic. Defaults to the tenant's setting.
Dimension of the trafficBreakdown series: channels or campaigns. source and medium are shorthand for campaigns broken down by that field. Defaults to channels.
With breakdownMode=campaigns, break the series down by source, by medium or by both with all. Defaults to all.
Column to filter trafficDistribution by: source, medium, campaign or channel. Requires filterValue. Score cards and trafficBreakdown are not filtered.
Text to look for in filterField. Case-insensitive and matches part of the value. Requires filterField.
Group trafficDistribution rows by one dimension. none returns one row per combination of source, medium, campaign and channel. Defaults to none.
Comma-separated columns to return on each trafficDistribution row: source, medium, campaign, channel, sessions, conversions, conversion_rate, bounce_rate, revenue, revenue_per_conversion. Defaults to all.
true limits the report to traffic from AI assistants such as ChatGPT, Perplexity, Gemini, Claude and Copilot. Defaults to false.
Maximum number of trafficDistribution rows to return, 1 to 500. Defaults to 50.
Number of trafficDistribution rows to skip, 0 to 10000. Defaults to 0.
Column to sort trafficDistribution by. event_metric sorts by the event named in sortEventMetricName. Defaults to sessions.
Event to sort by when sortBy=event_metric. It must be one of eventMetricNames.
asc or desc. Defaults to desc.
Responses
true