Base URLs & Authentication
Base URLs
https://gtrack-api.wiremo.cohttps://gtrack-mcp.wiremo.co/?key=gtrk_live_XXXXXXXXXXXXXXXXXAuthentication
All endpoints require an API key header:
x-api-key: gtrk_live_XXXXXXXXXXXXXXXXX
Rate Limits
| Plan | Reads/Hour | Writes/Hour |
|---|---|---|
| Business | 2000 | 1000 |
| Pro | 5000 | 2000 |
X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
HTTP 429 is returned when limits are exceeded.
POST Initiate Scan – No Polling (with Center)
Creates a new scan using a center coordinate. Grid coordinates are auto-generated.
| Parameter | Type | Required | Description |
|---|---|---|---|
| center | Array [lat, lng] | Optional | Center coordinate for grid generation |
| coordinates | Array of [lat, lng] | Optional | Manual coordinates (use either center OR coordinates) |
| gridSize | Integer | Required | Grid dimension (3-13, e.g., 3 = 3x3 = 9 points) |
| distance | Number | Required | Distance from center (0.1+ km/miles) |
| unit | String | Required | "kilometer" or "mile" |
| scanType | String | Optional | "maps" (1 credit) or "local" (2 credits), defaults to "maps" |
| keyword | String | Required | Search keyword |
| placeId | String | Required | Google Place ID |
| customZoom | Integer | Optional | Zoom level 1-20 (default: 13, maps only) |
| language | String | Optional | ISO language code (default: 'en') |
center or coordinates, not both. If neither provided, uses placeId coordinates as center.Example with Coordinates Array
curl -X POST https://gtrack-api.wiremo.co/api/initiate-scan \
-H "Content-Type: application/json" \
-H "x-api-key: gtrk_demo_sample_key_here" \
-d '{
"coordinates": [
[40.7128, -74.0060],
[40.7138, -74.0070],
[40.7148, -74.0080],
[40.7158, -74.0090],
[40.7168, -74.0100],
[40.7178, -74.0110],
[40.7188, -74.0120],
[40.7198, -74.0130],
[40.7208, -74.0140]
],
"gridSize": 3,
"scanType": "maps",
"keyword": "pizza restaurant",
"placeId": "ChIJN1t_tDeuEmsRUsoyG83frY4",
"customZoom": 18,
"language": "es"
}'
curl -X POST https://gtrack-api.wiremo.co/api/initiate-scan \
-H "Content-Type: application/json" \
-H "x-api-key: gtrk_demo_sample_key_here" \
-d '{
"center": [40.7128, -74.0060],
"gridSize": 5,
"distance": 0.5,
"unit": "kilometer",
"scanType": "maps",
"keyword": "coffee shop",
"placeId": "ChIJOwg_06VPwokRYv534QaPC8g",
"customZoom": 18,
"language": "es"
}'
import fetch from 'node-fetch';
const res = await fetch('https://gtrack-api.wiremo.co/api/initiate-scan', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'gtrk_demo_sample_key_here'
},
body: JSON.stringify({
center: [40.7128, -74.0060],
gridSize: 5,
distance: 0.5,
unit: 'kilometer',
scanType: 'maps',
keyword: 'coffee shop',
placeId: 'ChIJOwg_06VPwokRYv534QaPC8g',
customZoom: 18,
language: 'es'
})
});
console.log(await res.json());
import requests
url = 'https://gtrack-api.wiremo.co/api/initiate-scan'
headers = {'x-api-key': 'gtrk_demo_sample_key_here', 'Content-Type': 'application/json'}
payload = {
'center': [40.7128, -74.0060],
'gridSize': 5,
'distance': 0.5,
'unit': 'kilometer',
'scanType': 'maps',
'keyword': 'coffee shop',
'placeId': 'ChIJOwg_06VPwokRYv534QaPC8g',
'customZoom': 18,
'language': 'es'
}
resp = requests.post(url, json=payload, headers=headers)
print(resp.json())
[40.7128, -74.0060],
'gridSize' => 5,
'distance' => 0.5,
'unit' => 'kilometer',
'scanType' => 'maps',
'keyword' => 'coffee shop',
'placeId' => 'ChIJOwg_06VPwokRYv534QaPC8g',
'customZoom' => 18,
'language' => 'es'
];
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'x-api-key: gtrk_demo_sample_key_here'
],
CURLOPT_POSTFIELDS => json_encode($payload)
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
import { useState } from 'react';
export default function StartScan(){
const [result,setResult] = useState(null);
const start = async () => {
const res = await fetch('https://gtrack-api.wiremo.co/api/initiate-scan', {
method: 'POST',
headers: { 'Content-Type':'application/json', 'x-api-key':'gtrk_demo_sample_key_here' },
body: JSON.stringify({
center: [40.7128, -74.0060],
gridSize: 5,
distance: 0.5,
unit: 'kilometer',
scanType: 'maps',
keyword: 'coffee shop',
placeId: 'ChIJOwg_06VPwokRYv534QaPC8g',
customZoom: 18,
language: 'es'
})
});
setResult(await res.json());
};
return (
{JSON.stringify(result,null,2)}
);
}
scans:write, 400 insufficient credits / invalid params, 404 user not found, 500 scraper unavailable.POST Initiate Scan – With Polling
status = COMPLETED.curl -X POST "https://gtrack-api.wiremo.co/api/initiate-scan?wait=true&timeout=300" \
-H "Content-Type: application/json" \
-H "x-api-key: gtrk_demo_sample_key_here" \
-d '{
"center": [51.5074, -0.1278],
"gridSize": 4,
"distance": 0.3,
"unit": "kilometer",
"scanType": "maps",
"keyword": "hotel",
"placeId": "ChIJ3S-JXmauEmsRUcIaWtf4MzE",
"customZoom": 18,
"language": "es"
}'
import fetch from 'node-fetch';
const url = 'https://gtrack-api.wiremo.co/api/initiate-scan?wait=true&timeout=300';
const payload = {
center: [51.5074, -0.1278],
gridSize: 3,
distance: 300,
unit: 'kilometer',
scanType: 'both',
keyword: 'hotel',
placeId: 'ChIJ3S-JXmauEmsRUcIaWtf4MzE',
businessName: 'The London Hotel'
};
const res = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'gtrk_live_0202d7fcf814dd6f09d0feee'
},
body: JSON.stringify(payload)
});
console.log(await res.json());
[51.5074, -0.1278],
'gridSize' => 4,
'distance' => 0.3,
'unit' => 'kilometer',
'scanType' => 'maps',
'keyword' => 'hotel',
'placeId' => 'ChIJ3S-JXmauEmsRUcIaWtf4MzE',
'customZoom' => 18,
'language' => 'es'
];
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'x-api-key: gtrk_demo_sample_key_here'
],
CURLOPT_POSTFIELDS => json_encode($payload)
]);
$response = curl_exec($ch);
if ($response === false) {
$error = curl_error($ch);
curl_close($ch);
http_response_code(500);
echo json_encode(['error' => $error], JSON_PRETTY_PRINT);
exit;
}
curl_close($ch);
header('Content-Type: application/json');
echo $response;
?>
import { useState } from 'react';
export default function StartScan() {
const [result, setResult] = useState(null);
const [error, setError] = useState(null);
const start = async () => {
setError(null);
setResult(null);
try {
const res = await fetch('https://gtrack-api.wiremo.co/api/initiate-scan?wait=true&timeout=300', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'gtrk_demo_sample_key_here'
},
body: JSON.stringify({
center: [51.5074, -0.1278],
gridSize: 4,
distance: 0.3,
unit: 'kilometer',
scanType: 'maps',
keyword: 'hotel',
placeId: 'ChIJ3S-JXmauEmsRUcIaWtf4MzE',
customZoom: 18,
language: 'es'
})
});
const data = await res.json().catch(() => null);
if (!res.ok) {
throw new Error(data?.message || 'Request failed');
}
setResult(data);
} catch (e) {
setError(e.message);
}
};
return (
{error && {error}
}
{result && {JSON.stringify(result, null, 2)}
}
);
}
import requests
url = 'https://gtrack-api.wiremo.co/api/initiate-scan'
params = {'wait': 'true', 'timeout': 300}
headers = {'x-api-key': 'gtrk_live_0202d7fcf814dd6f09d0feee', 'Content-Type': 'application/json'}
payload = {
'center': [51.5074, -0.1278],
'gridSize': 3,
'distance': 300,
'unit': 'kilometer',
'scanType': 'both',
'keyword': 'hotel',
'placeId': 'ChIJ3S-JXmauEmsRUcIaWtf4MzE',
'businessName': 'The London Hotel'
}
resp = requests.post(url, params=params, json=payload, headers=headers)
print(resp.json())
[51.5074, -0.1278],
'gridSize' => 4,
'distance' => 0.3,
'unit' => 'kilometer',
'scanType' => 'maps',
'keyword' => 'hotel',
'placeId' => 'ChIJ3S-JXmauEmsRUcIaWtf4MzE',
'customZoom' => 18,
'language' => 'es'
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'x-api-key: gtrk_demo_sample_key_here'
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
import { useState } from 'react';
function ScanWithPolling() {
const [results, setResults] = useState(null);
const [loading, setLoading] = useState(false);
const startScan = async () => {
setLoading(true);
const response = await fetch(
'https://gtrack-api.wiremo.co/api/initiate-scan?wait=true&timeout=300',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'gtrk_demo_sample_key_here'
},
body: JSON.stringify({
center: [51.5074, -0.1278],
gridSize: 4,
distance: 0.3,
unit: 'kilometer',
scanType: 'maps',
keyword: 'hotel',
placeId: 'ChIJ3S-JXmauEmsRUcIaWtf4MzE',
customZoom: 18,
language: 'es'
})
}
);
const data = await response.json();
setResults(data);
setLoading(false);
};
return (
{results && {JSON.stringify(results, null, 2)}}
);
}
COMPLETED payload or a timeout envelope advising to poll by taskId.POST Generate Grid Coordinates
Generates grid coordinates without initiating a scan.
curl -X POST https://gtrack-api.wiremo.co/api/generate-grid \
-H "x-api-key: gtrk_demo_sample_key_here" \
-H "Content-Type: application/json" \
-d '{
"center": [28.0833107, -16.7223283],
"gridSize": 3,
"distance": 8,
"unit": "kilometer"
}'
import fetch from 'node-fetch';
const res = await fetch('https://gtrack-api.wiremo.co/api/generate-grid', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'gtrk_demo_sample_key_here'
},
body: JSON.stringify({
center: [28.0833107, -16.7223283],
gridSize: 3,
distance: 8,
unit: 'kilometer'
})
});
console.log(await res.json());
import requests
url = 'https://gtrack-api.wiremo.co/api/generate-grid'
headers = {'x-api-key': 'gtrk_demo_sample_key_here', 'Content-Type': 'application/json'}
payload = {
'center': [28.0833107, -16.7223283],
'gridSize': 3,
'distance': 8,
'unit': 'kilometer'
}
resp = requests.post(url, json=payload, headers=headers)
print(resp.json())
[40.7128, -74.0060],
'gridSize' => 5,
'distance' => 1.0,
'unit' => 'kilometer'
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'x-api-key: gtrk_demo_sample_key_here'
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
import { useState } from 'react';
function GenerateGrid() {
const [grid, setGrid] = useState([]);
const generateGrid = async () => {
const response = await fetch('https://gtrack-api.wiremo.co/api/generate-grid', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'gtrk_demo_sample_key_here'
},
body: JSON.stringify({
center: [40.7128, -74.0060],
gridSize: 5,
distance: 1.0,
unit: 'kilometer'
})
});
const data = await response.json();
setGrid(data.coordinates);
};
return (
{grid.length > 0 && Generated {grid.length} coordinates
}
);
}
GET Get Scan Status / Results
Get the current status and results of a scan by task ID.
curl -X GET https://gtrack-api.wiremo.co/api/fullstatus/scan/TASK_ID_HERE/status \
-H "x-api-key: gtrk_demo_sample_key_here" \
-H "Content-Type: application/json"
import fetch from 'node-fetch';
const taskId = 'TASK_ID_HERE';
const res = await fetch(`https://gtrack-api.wiremo.co/api/fullstatus/scan/${taskId}/status`, {
method: 'GET',
headers: {
'x-api-key': 'gtrk_demo_sample_key_here',
'Content-Type': 'application/json'
}
});
console.log(await res.json());
import requests
task_id = 'TASK_ID_HERE'
url = f'https://gtrack-api.wiremo.co/api/fullstatus/scan/{task_id}/status'
headers = {'x-api-key': 'gtrk_demo_sample_key_here', 'Content-Type': 'application/json'}
resp = requests.get(url, headers=headers)
print(resp.json())
<?php
$taskId = 'TASK_ID_HERE';
$url = "https://gtrack-api.wiremo.co/api/fullstatus/scan/{$taskId}/status";
$headers = [
'x-api-key: gtrk_demo_sample_key_here',
'Content-Type: application/json'
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
import React, { useState, useEffect } from 'react';
function ScanStatus({ taskId }) {
const [status, setStatus] = useState(null);
useEffect(() => {
const fetchStatus = async () => {
const response = await fetch(`https://gtrack-api.wiremo.co/api/fullstatus/scan/${taskId}/status`, {
headers: {
'x-api-key': 'gtrk_demo_sample_key_here',
'Content-Type': 'application/json'
}
});
const data = await response.json();
setStatus(data);
};
if (taskId) fetchStatus();
}, [taskId]);
return (
{status && {JSON.stringify(status, null, 2)}}
);
}
GET List All Scans
Retrieve all scans for the authenticated user.
curl -X GET https://gtrack-api.wiremo.co/api/scans \
-H "x-api-key: gtrk_demo_sample_key_here" \
-H "Content-Type: application/json"
import fetch from 'node-fetch';
const res = await fetch('https://gtrack-api.wiremo.co/api/scans', {
method: 'GET',
headers: {
'x-api-key': 'gtrk_demo_sample_key_here',
'Content-Type': 'application/json'
}
});
console.log(await res.json());
import requests
url = 'https://gtrack-api.wiremo.co/api/scans'
headers = {'x-api-key': 'gtrk_demo_sample_key_here', 'Content-Type': 'application/json'}
resp = requests.get(url, headers=headers)
print(resp.json())
<?php
$url = 'https://gtrack-api.wiremo.co/api/scans';
$headers = [
'x-api-key: gtrk_demo_sample_key_here',
'Content-Type: application/json'
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
import React, { useState, useEffect } from 'react';
function ScanList() {
const [scans, setScans] = useState([]);
useEffect(() => {
const fetchScans = async () => {
const response = await fetch('https://gtrack-api.wiremo.co/api/scans', {
headers: {
'x-api-key': 'gtrk_demo_sample_key_here',
'Content-Type': 'application/json'
}
});
const data = await response.json();
setScans(data);
};
fetchScans();
}, []);
return (
{scans.map(scan => (
{scan.taskId}
))}
);
}
GET List Scheduled Scans
Retrieve your scheduled scan configurations. Returns a JSON array of schedule objects — each includes _id (use it with Run History below), name, frequency, timeOfDay, timezone, isActive, lastRunTime, nextRunTime and scanParameters (business name, place ID, keywords, grid size, distance).
Note: completed runs from scheduled scans are intentionally not included in GET /api/scans (that endpoint lists one-off scans). Use Run History below to retrieve scheduled results.
curl -X GET https://gtrack-api.wiremo.co/api/scheduled-scans \
-H "x-api-key: gtrk_demo_sample_key_here" \
-H "Content-Type: application/json"
import fetch from 'node-fetch';
const res = await fetch('https://gtrack-api.wiremo.co/api/scheduled-scans', {
headers: { 'x-api-key': 'gtrk_demo_sample_key_here' }
});
const schedules = await res.json();
console.log(schedules.map(s => ({ id: s._id, name: s.name, next: s.nextRunTime })));
import requests
url = 'https://gtrack-api.wiremo.co/api/scheduled-scans'
headers = {'x-api-key': 'gtrk_demo_sample_key_here'}
schedules = requests.get(url, headers=headers).json()
for s in schedules:
print(s['_id'], s['name'], s.get('nextRunTime'))
GET Scheduled Scan Run History
Retrieve run results for one schedule (:id from List Scheduled Scans), most recent first. Each entry includes keyword, businessName, scanDate, status (pending | completed | failed), averageRanking and — for completed runs — the taskId.
Pagination (recommended): pass ?page= and ?limit= (default 50, max 200) to receive { success, pagination: { page, limit, total, pages }, results: [...] }. Without these parameters the endpoint returns the complete history as a plain JSON array — fine for small schedules, heavy for long-running daily ones.
Feed each taskId into GET /api/fullstatus/scan/:taskId/status to obtain the full grid data for that run.
Share links for a schedule can also be managed with your API key: POST /api/scheduled-scans/:id/share creates one, DELETE removes it.
curl -X GET "https://gtrack-api.wiremo.co/api/scheduled-scans/SCHEDULE_ID_HERE/history?page=1&limit=50" \
-H "x-api-key: gtrk_demo_sample_key_here" \
-H "Content-Type: application/json"
import fetch from 'node-fetch';
const scheduleId = 'SCHEDULE_ID_HERE';
const res = await fetch(`https://gtrack-api.wiremo.co/api/scheduled-scans/${scheduleId}/history?page=1&limit=50`, {
headers: { 'x-api-key': 'gtrk_demo_sample_key_here' }
});
const { pagination, results } = await res.json();
const completed = results.filter(r => r.status === 'completed');
console.log(completed.map(r => ({ taskId: r.taskId, keyword: r.keyword, date: r.scanDate })));
import requests
schedule_id = 'SCHEDULE_ID_HERE'
url = f'https://gtrack-api.wiremo.co/api/scheduled-scans/{schedule_id}/history'
headers = {'x-api-key': 'gtrk_demo_sample_key_here'}
data = requests.get(url, headers=headers, params={'page': 1, 'limit': 50}).json()
for r in data['results']:
if r['status'] == 'completed':
print(r['taskId'], r['keyword'], r['scanDate'])
GET Get All Tracked Businesses
Retrieve all tracked businesses for the authenticated user.
curl -X GET https://gtrack-api.wiremo.co/api/tracked-businesses \
-H "x-api-key: gtrk_demo_sample_key_here" \
-H "Content-Type: application/json"
import fetch from 'node-fetch';
const res = await fetch('https://gtrack-api.wiremo.co/api/tracked-businesses', {
method: 'GET',
headers: {
'x-api-key': 'gtrk_demo_sample_key_here',
'Content-Type': 'application/json'
}
});
console.log(await res.json());
import requests
url = 'https://gtrack-api.wiremo.co/api/tracked-businesses'
headers = {'x-api-key': 'gtrk_demo_sample_key_here', 'Content-Type': 'application/json'}
resp = requests.get(url, headers=headers)
print(resp.json())
<?php
$url = 'https://gtrack-api.wiremo.co/api/tracked-businesses';
$headers = [
'x-api-key: gtrk_demo_sample_key_here',
'Content-Type: application/json'
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
import React, { useState, useEffect } from 'react';
function TrackedBusinessesList() {
const [businesses, setBusinesses] = useState([]);
useEffect(() => {
const fetchBusinesses = async () => {
const response = await fetch('https://gtrack-api.wiremo.co/api/tracked-businesses', {
headers: {
'x-api-key': 'gtrk_demo_sample_key_here',
'Content-Type': 'application/json'
}
});
const data = await response.json();
setBusinesses(data);
};
fetchBusinesses();
}, []);
return (
{businesses.map(business => (
{business.businessName}
))}
);
}
POST Add Tracked Business
businesses:write
curl -X POST https://gtrack-api.wiremo.co/api/tracked-businesses \
-H "x-api-key: gtrk_demo_sample_key_here" \
-H "Content-Type: application/json" \
-d '{
"placeId": "ChIJ9X1uQniZagwR8nA2NqxFEb8",
"businessName": "Dulce Vegan",
"trackingParams": {"rating": true, "reviewCount": true, "position": false, "hours": false},
"alertThresholds": {"ratingChange": 0.5, "reviewCountChange": 10},
"alertFrequency": "daily",
"alertEmail": "user@example.com"
}'
import fetch from 'node-fetch';
const res = await fetch('https://gtrack-api.wiremo.co/api/tracked-businesses', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'gtrk_live_0202d7fcf814dd6f09d0feee'
},
body: JSON.stringify({
placeId: 'ChIJ9X1uQniZagwR8nA2NqxFEb8',
businessName: 'Dulce Vegan',
trackingParams: { rating: true, reviewCount: true, position: false, hours: false },
alertThresholds: { ratingChange: 0.5, reviewCountChange: 10 },
alertFrequency: 'daily',
alertEmail: 'user@example.com'
})
});
console.log(await res.json());
<?php
$ch = curl_init('https://gtrack-api.wiremo.co/api/tracked-businesses');
$payload = [
'placeId' => 'ChIJ9X1uQniZagwR8nA2NqxFEb8',
'businessName' => 'Dulce Vegan',
'trackingParams' => [
'rating' => true,
'reviewCount' => true,
'position' => false,
'hours' => false
],
'alertThresholds' => [
'ratingChange' => 0.5,
'reviewCountChange' => 10
],
'alertFrequency' => 'daily',
'alertEmail' => 'user@example.com'
];
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'x-api-key: gtrk_live_0202d7fcf814dd6f09d0feee'
],
CURLOPT_POSTFIELDS => json_encode($payload)
]);
$response = curl_exec($ch);
if ($response === false) {
http_response_code(500);
echo 'cURL error: ' . curl_error($ch);
} else {
header('Content-Type: application/json');
echo $response;
}
curl_close($ch);
?>
import requests
url = 'https://gtrack-api.wiremo.co/api/tracked-businesses'
headers = {'x-api-key': 'gtrk_demo_sample_key_here', 'Content-Type': 'application/json'}
payload = {
'placeId': 'ChIJ9X1uQniZagwR8nA2NqxFEb8',
'businessName': 'Dulce Vegan',
'trackingParams': {'rating': True, 'reviewCount': True, 'position': False, 'hours': False},
'alertThresholds': {'ratingChange': 0.5, 'reviewCountChange': 10},
'alertFrequency': 'daily',
'alertEmail': 'user@example.com'
}
resp = requests.post(url, json=payload, headers=headers)
print(resp.json())
import React, { useState } from 'react';
function AddTrackedBusiness() {
const [result, setResult] = useState(null);
const [loading, setLoading] = useState(false);
const addBusiness = async () => {
setLoading(true);
const response = await fetch('https://gtrack-api.wiremo.co/api/tracked-businesses', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'gtrk_demo_sample_key_here'
},
body: JSON.stringify({
placeId: 'ChIJ9X1uQniZagwR8nA2NqxFEb8',
businessName: 'Dulce Vegan',
trackingParams: { rating: true, reviewCount: true, position: false, hours: false },
alertThresholds: { ratingChange: 0.5, reviewCountChange: 10 },
alertFrequency: 'daily',
alertEmail: 'user@example.com'
})
});
const data = await response.json();
setResult(data);
setLoading(false);
};
return (
{result && {JSON.stringify(result, null, 2)}}
);
}
GET List AI Reports
Retrieve all saved AI scan reports for the authenticated user.
curl -X GET https://gtrack-api.wiremo.co/reports-ai/reports/list \
-H "x-api-key: gtrk_demo_sample_key_here" \
-H "Content-Type: application/json"
import fetch from 'node-fetch';
const res = await fetch('https://gtrack-api.wiremo.co/reports-ai/reports/list', {
method: 'GET',
headers: {
'x-api-key': 'gtrk_demo_sample_key_here',
'Content-Type': 'application/json'
}
});
console.log(await res.json());
import requests
url = 'https://gtrack-api.wiremo.co/reports-ai/reports/list'
headers = {'x-api-key': 'gtrk_demo_sample_key_here', 'Content-Type': 'application/json'}
resp = requests.get(url, headers=headers)
print(resp.json())
<?php
$url = 'https://gtrack-api.wiremo.co/reports-ai/reports/list';
$headers = [
'x-api-key: gtrk_demo_sample_key_here',
'Content-Type: application/json'
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
import React, { useState, useEffect } from 'react';
function AIReportsList() {
const [reports, setReports] = useState([]);
useEffect(() => {
const fetchReports = async () => {
const response = await fetch('https://gtrack-api.wiremo.co/reports-ai/reports/list', {
headers: {
'x-api-key': 'gtrk_demo_sample_key_here',
'Content-Type': 'application/json'
}
});
const data = await response.json();
setReports(data);
};
fetchReports();
}, []);
return (
{reports.map(report => (
{report.title}
))}
);
}
GET Get AI Report Details
Get detailed data for a specific AI scan report.
curl -X GET https://gtrack-api.wiremo.co/reports-ai/reports/REPORT_ID_HERE \
-H "x-api-key: gtrk_demo_sample_key_here" \
-H "Content-Type: application/json"
import fetch from 'node-fetch';
const reportId = 'REPORT_ID_HERE';
const res = await fetch(`https://gtrack-api.wiremo.co/reports-ai/reports/${reportId}`, {
method: 'GET',
headers: {
'x-api-key': 'gtrk_demo_sample_key_here',
'Content-Type': 'application/json'
}
});
console.log(await res.json());
import requests
report_id = 'REPORT_ID_HERE'
url = f'https://gtrack-api.wiremo.co/reports-ai/reports/{report_id}'
headers = {'x-api-key': 'gtrk_demo_sample_key_here', 'Content-Type': 'application/json'}
resp = requests.get(url, headers=headers)
print(resp.json())
<?php
$reportId = 'REPORT_ID_HERE';
$url = "https://gtrack-api.wiremo.co/reports-ai/reports/{$reportId}";
$headers = [
'x-api-key: gtrk_demo_sample_key_here',
'Content-Type: application/json'
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
import React, { useState, useEffect } from 'react';
function AIReportDetails({ reportId }) {
const [report, setReport] = useState(null);
useEffect(() => {
const fetchReport = async () => {
const response = await fetch(`https://gtrack-api.wiremo.co/reports-ai/reports/${reportId}`, {
headers: {
'x-api-key': 'gtrk_demo_sample_key_here',
'Content-Type': 'application/json'
}
});
const data = await response.json();
setReport(data);
};
fetchReport();
}, [reportId]);
return (
{report && {report.title}}
);
}
DELETE Delete AI Report
Delete a specific AI scan report permanently.
curl -X DELETE https://gtrack-api.wiremo.co/reports-ai/reports/REPORT_ID_HERE \
-H "x-api-key: gtrk_demo_sample_key_here" \
-H "Content-Type: application/json"
import fetch from 'node-fetch';
const reportId = 'REPORT_ID_HERE';
const res = await fetch(`https://gtrack-api.wiremo.co/reports-ai/reports/${reportId}`, {
method: 'DELETE',
headers: {
'x-api-key': 'gtrk_demo_sample_key_here',
'Content-Type': 'application/json'
}
});
console.log(await res.json());
import requests
report_id = 'REPORT_ID_HERE'
url = f'https://gtrack-api.wiremo.co/reports-ai/reports/{report_id}'
headers = {'x-api-key': 'gtrk_demo_sample_key_here', 'Content-Type': 'application/json'}
resp = requests.delete(url, headers=headers)
print(resp.json())
<?php
$reportId = 'REPORT_ID_HERE';
$url = "https://gtrack-api.wiremo.co/reports-ai/reports/{$reportId}";
$headers = [
'x-api-key: gtrk_demo_sample_key_here',
'Content-Type: application/json'
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
import React from 'react';
function DeleteAIReport({ reportId, onDelete }) {
const handleDelete = async () => {
const response = await fetch(`https://gtrack-api.wiremo.co/reports-ai/reports/${reportId}`, {
method: 'DELETE',
headers: {
'x-api-key': 'gtrk_demo_sample_key_here',
'Content-Type': 'application/json'
}
});
const result = await response.json();
onDelete(result);
};
return (
);
}
AI Tracker — overview
AI Tracker measures whether AI engines (ChatGPT, Perplexity, Gemini, Google AI Mode) mention and recommend a business when customers ask. Each campaign holds prompts (grouped by topic); a scan runs every prompt against every engine, producing a prompt × engine matrix of mention / position / sentiment / cited sources. All endpoints below are under the standard base URL and accept your x-api-key.
Read endpoints are free. Trigger Scan consumes the campaign's scan allowance (paid extras are purchased in the dashboard). Generate endpoints consume your account AI-token allowance (the same pool as AI Reports) when called via the API/MCP — see below. Campaigns can be created via the API when a slot is available (the free taste slot, or an already-paid subscription slot); if a new paid slot would be required, create returns 402 with { reason, priceUsd } — buy the subscription in the dashboard, then retry. Editing config, archiving/deleting a campaign, and managing the subscription remain dashboard-only (owner) and are not part of this API.
GET List Campaigns
Returns { success, campaigns: [...] }. Each campaign includes _id, businessName, domain, engines, schedule, promptCount, lastAiScore (0–100, share of checks that mentioned the business), lastRunAt, and activeScan (true while a scan is running).
curl -X GET https://gtrack-api.wiremo.co/api/ai-tracker/campaigns \
-H "x-api-key: gtrk_demo_sample_key_here"
GET Get Campaign
Returns { success, campaign, manualRunUsage }. manualRunUsage reports the free-scan allowance for the current billing period: { used, included, remaining, resetsAt, isLifetime }.
curl https://gtrack-api.wiremo.co/api/ai-tracker/campaigns/CAMPAIGN_ID \
-H "x-api-key: gtrk_demo_sample_key_here"
Prompts for a campaign: GET /api/ai-tracker/campaigns/:id/prompts.
POST Create Campaign
Body: { businessName, domain, businessDescription?, brandAliases?, location?: { country }, language?, engines?: ["chatgpt","perplexity","gemini","google_ai_mode"], schedule?: { frequency, dayOfWeek?, dayOfMonth?, hour?, minute?, timezone?, enabled? }, notifications?: { emailOnComplete, email } }. Returns { success, campaign }.
Slots: creation succeeds when a slot is free — the included taste campaign (monthly schedule only) or an already-paid subscription slot. If a new paid slot would be needed, returns 402 { reason:"subscription_required", priceUsd }; purchase the subscription in the dashboard, then retry.
Typical automation flow: generate/topics → generate/prompts (both metered) → POST /campaigns → POST /campaigns/:id/prompts → POST /campaigns/:id/run.
curl -X POST https://gtrack-api.wiremo.co/api/ai-tracker/campaigns \
-H "x-api-key: gtrk_demo_sample_key_here" -H "Content-Type: application/json" \
-d '{"businessName":"Acme Bakery","domain":"acme.example","engines":["chatgpt","perplexity","gemini","google_ai_mode"],"schedule":{"frequency":"weekly","dayOfWeek":1,"hour":9,"timezone":"UTC","enabled":true}}'
Add prompts after creating: POST /api/ai-tracker/campaigns/:id/prompts with { prompts: [{ text, topic, intent }] }.
GET List Runs & Get Results
Scan history for a campaign, newest first. Each run: runId, status (pending|running|completed|partial|failed|cancelled), engines, expectedCells, completedCells, mentionedCells, startedAt, completedAt.
The result matrix for one run: { success, run, results } where each result is one prompt × engine cell — engine, mentioned, mentionType, position, sentiment, brands (ordered, with domain and isTarget), sources. Heavy answer text is omitted here; fetch a single cell with GET /api/ai-tracker/results/:id (add ?raw=1 for the raw engine payload).
Full-detail CSV of a run (prompt, topic, intent, per-engine mention/position/sentiment, ranked brands with domains, source URLs, answer excerpt).
RUN=$(curl -s https://gtrack-api.wiremo.co/api/ai-tracker/campaigns/CAMPAIGN_ID/runs \
-H "x-api-key: gtrk_demo_sample_key_here" | jq -r '.runs[0].runId')
curl "https://gtrack-api.wiremo.co/api/ai-tracker/runs/$RUN/results" \
-H "x-api-key: gtrk_demo_sample_key_here"
GET Progression
Run-over-run trend (last 12 finished runs): per run the aiScore, per-engine hit rates, and per-prompt mention counts + average position. Ideal for charting AI-visibility change over time.
POST Trigger Scan
Queues a scan (executed by the AI Tracker worker; poll /runs for status). Subject to the campaign's scan allowance — if the included scans are used, returns 402 with { reason, priceUsd }; extra scans are purchased in the dashboard.
curl -X POST https://gtrack-api.wiremo.co/api/ai-tracker/campaigns/CAMPAIGN_ID/run \
-H "x-api-key: gtrk_demo_sample_key_here"
POST Generate Topics / Prompts metered
These consume your account AI-token allowance (the same pool as AI Reports: plan token limit + ai_tokens extra credits) when called with an API key or via MCP. If the balance is too low the call returns 403 with { reason: "insufficient_ai_tokens", tokensAvailable }. Topic/prompt generation in the dashboard is free; metering applies only to programmatic use.
generate/topics body: { businessName, domain, businessDescription?, country?, language? } → { topics: [{ topic, intent }] }. generate/prompts body: { businessName, domain?, businessDescription?, country?, language?, topics: [{ topic, intent }] } → { prompts: [{ topic, intent, text }] }.
curl -X POST https://gtrack-api.wiremo.co/api/ai-tracker/generate/topics \
-H "x-api-key: gtrk_demo_sample_key_here" -H "Content-Type: application/json" \
-d '{"businessName":"Acme Bakery","domain":"acme.example","country":"United States","language":"en"}'
Credit Calculation
| Scan Type | Multiplier | Formula |
|---|---|---|
| Maps | 1x | coordinates × keywords × 1 |
| Local | 2x | coordinates × keywords × 2 |
Common Error Codes
| Code | Description | Common Causes |
|---|---|---|
| 400 | Bad Request | Missing/invalid params, insufficient credits |
| 401 | Unauthorized | Missing API key, invalid format |
| 403 | Forbidden | Missing scope, feature not in plan, plan limit reached |
| 404 | Not Found | Resource absent, wrong owner, scan not completed |
| 500 | Internal Server Error | Scraper/DB/AI service error |
| 429 | Too Many Requests | Rate limits exceeded; see Retry-After |
Retry-After Header
When you receive a 429 (Too Many Requests) response, the server includes a Retry-After header indicating when you can retry your request.
| Header | Value | Description |
|---|---|---|
Retry-After | Seconds (integer) | Number of seconds to wait before retrying |
Retry-After | HTTP Date | Specific date/time when you can retry |
Retry-After: 60 - Wait 60 seconds before retryingRetry-After: Wed, 21 Oct 2015 07:28:00 GMT - Retry after this specific time
Best Practices
- Never expose API keys in client-side code
- Rotate keys; use separate keys per env
- Prefer IP restrictions
- Use polling only when immediate results are needed
- Cache results; paginate large lists
- Generate grid once; reuse
- Prefer
maps(1x) unless local pack is required - Start with smaller grids; schedule scans
- Check credits before large runs
- Handle 400/403 for credits/scopes
- Retry 5xx with backoff
- Poll status if not using
wait=true
Changelog
| Date | Version | Changes |
|---|---|---|
| 2025-10-22 | v1.0 | Initial release: scan management, competitor tracking, AI reports |
| 2026-08-31 | v1.1 | Scheduled Scans: list schedules (GET /api/scheduled-scans) and per-schedule run history with task IDs (GET /api/scheduled-scans/:id/history); share-link create/delete accept API keys; history supports page/limit; MCP gains get_scheduled_scans and get_scheduled_scan_history tools |
| 2026-09-08 | v1.2 | AI Tracker: campaigns, runs & result matrix, progression, CSV export, trigger scan, and metered topic/prompt generation via API + MCP (/api/ai-tracker/*); generation consumes the account AI-token pool on programmatic calls. MCP gains ai_tracker_list_campaigns, ai_tracker_get_campaign, ai_tracker_list_runs, ai_tracker_get_results, ai_tracker_get_progression, ai_tracker_run_scan, ai_tracker_suggest_topics, ai_tracker_generate_prompts |