curl --request POST \
--url https://app.goosybear.ai/api/v1/tools/ads.suggest_competitors \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"candidates": [
{
"name": "<string>",
"reason": "<string>"
}
],
"workspace": "<string>"
}
'import requests
url = "https://app.goosybear.ai/api/v1/tools/ads.suggest_competitors"
payload = {
"candidates": [
{
"name": "<string>",
"reason": "<string>"
}
],
"workspace": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({candidates: [{name: '<string>', reason: '<string>'}], workspace: '<string>'})
};
fetch('https://app.goosybear.ai/api/v1/tools/ads.suggest_competitors', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.goosybear.ai/api/v1/tools/ads.suggest_competitors",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'candidates' => [
[
'name' => '<string>',
'reason' => '<string>'
]
],
'workspace' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.goosybear.ai/api/v1/tools/ads.suggest_competitors"
payload := strings.NewReader("{\n \"candidates\": [\n {\n \"name\": \"<string>\",\n \"reason\": \"<string>\"\n }\n ],\n \"workspace\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.goosybear.ai/api/v1/tools/ads.suggest_competitors")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"candidates\": [\n {\n \"name\": \"<string>\",\n \"reason\": \"<string>\"\n }\n ],\n \"workspace\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.goosybear.ai/api/v1/tools/ads.suggest_competitors")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"candidates\": [\n {\n \"name\": \"<string>\",\n \"reason\": \"<string>\"\n }\n ],\n \"workspace\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"ok": true,
"candidates": [
{
"id": "<string>",
"name": "<string>",
"platform": "meta",
"reason": "<string>",
"confirmed": false
}
],
"already_tracked": [
{
"name": "<string>",
"platform": "<string>"
}
],
"not_running": [
{
"name": "<string>",
"platform": "<string>",
"why": "<string>"
}
],
"workspace": "<string>",
"working_in": {
"label": "<string>",
"note": "<string>",
"workspace": "<string>",
"source": "call-override"
}
}{
"ok": false,
"code": "<string>",
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
},
"retry_after_seconds": 123
}{
"ok": false,
"code": "<string>",
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
},
"retry_after_seconds": 123
}{
"ok": false,
"code": "<string>",
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
},
"retry_after_seconds": 123
}{
"ok": false,
"code": "<string>",
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
},
"retry_after_seconds": 123
}{
"ok": false,
"code": "<string>",
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
},
"retry_after_seconds": 123
}Call ads.suggest_competitors
Suggest a few advertisers worth tracking on the Ads door — ‘who are our competitors’, ‘suggest some competitors to watch’ while making ads. Offer TWO OR THREE advertisers on Meta or Google — never six — guessed from what you know of their brand and site. Each name is checked in its ad library first (one lookup charged per name not looked up in the last 30 days): only advertisers running ads there are recorded, as Suggested rows in the watched list for the operator to approve or dismiss; this does not start tracking them. Names in not_running were not recorded: tell the operator plainly that the library showed no running ads for them. Give a short honest reason per name; they read it before approving. Once they approve, use ads.research_pull to see what that competitor is running.
Every result names the workspace it ran in. Say which workspace the answer is about. When the account has more than one workspace and none is selected, this tool refuses with workspace_ambiguous and lists the choices — offer them, never pick one.
curl --request POST \
--url https://app.goosybear.ai/api/v1/tools/ads.suggest_competitors \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"candidates": [
{
"name": "<string>",
"reason": "<string>"
}
],
"workspace": "<string>"
}
'import requests
url = "https://app.goosybear.ai/api/v1/tools/ads.suggest_competitors"
payload = {
"candidates": [
{
"name": "<string>",
"reason": "<string>"
}
],
"workspace": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({candidates: [{name: '<string>', reason: '<string>'}], workspace: '<string>'})
};
fetch('https://app.goosybear.ai/api/v1/tools/ads.suggest_competitors', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.goosybear.ai/api/v1/tools/ads.suggest_competitors",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'candidates' => [
[
'name' => '<string>',
'reason' => '<string>'
]
],
'workspace' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.goosybear.ai/api/v1/tools/ads.suggest_competitors"
payload := strings.NewReader("{\n \"candidates\": [\n {\n \"name\": \"<string>\",\n \"reason\": \"<string>\"\n }\n ],\n \"workspace\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.goosybear.ai/api/v1/tools/ads.suggest_competitors")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"candidates\": [\n {\n \"name\": \"<string>\",\n \"reason\": \"<string>\"\n }\n ],\n \"workspace\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.goosybear.ai/api/v1/tools/ads.suggest_competitors")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"candidates\": [\n {\n \"name\": \"<string>\",\n \"reason\": \"<string>\"\n }\n ],\n \"workspace\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"ok": true,
"candidates": [
{
"id": "<string>",
"name": "<string>",
"platform": "meta",
"reason": "<string>",
"confirmed": false
}
],
"already_tracked": [
{
"name": "<string>",
"platform": "<string>"
}
],
"not_running": [
{
"name": "<string>",
"platform": "<string>",
"why": "<string>"
}
],
"workspace": "<string>",
"working_in": {
"label": "<string>",
"note": "<string>",
"workspace": "<string>",
"source": "call-override"
}
}{
"ok": false,
"code": "<string>",
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
},
"retry_after_seconds": 123
}{
"ok": false,
"code": "<string>",
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
},
"retry_after_seconds": 123
}{
"ok": false,
"code": "<string>",
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
},
"retry_after_seconds": 123
}{
"ok": false,
"code": "<string>",
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
},
"retry_after_seconds": 123
}{
"ok": false,
"code": "<string>",
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
},
"retry_after_seconds": 123
}Authorizations
An API key minted at Settings › API & MCP. Send it as Authorization: Bearer <key>. A key carries its holder's own permissions, resolved on every call — revoking a membership closes the key's reach immediately. Keep it in an environment variable (GOOSY_API_KEY), never in a committed file.
Headers
Body
Response
The call was admitted and dispatched. ok says whether the tool succeeded — a refusal the tool itself produced is still a 200, exactly as it is a successful JSON-RPC result over MCP.
- Option 1
- Option 2
The tool ran and answered.