curl --request POST \
--url https://api.openpulse.cloud/v1/listeners/analyse-website \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"companyName": "<string>",
"website": "<string>",
"sources": [],
"customObjective": "<string>",
"description": "<string>",
"acceptLimited": false
}
'import requests
url = "https://api.openpulse.cloud/v1/listeners/analyse-website"
payload = {
"companyName": "<string>",
"website": "<string>",
"sources": [],
"customObjective": "<string>",
"description": "<string>",
"acceptLimited": False
}
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({
companyName: '<string>',
website: '<string>',
sources: [],
customObjective: '<string>',
description: '<string>',
acceptLimited: false
})
};
fetch('https://api.openpulse.cloud/v1/listeners/analyse-website', 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://api.openpulse.cloud/v1/listeners/analyse-website",
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([
'companyName' => '<string>',
'website' => '<string>',
'sources' => [
],
'customObjective' => '<string>',
'description' => '<string>',
'acceptLimited' => false
]),
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://api.openpulse.cloud/v1/listeners/analyse-website"
payload := strings.NewReader("{\n \"companyName\": \"<string>\",\n \"website\": \"<string>\",\n \"sources\": [],\n \"customObjective\": \"<string>\",\n \"description\": \"<string>\",\n \"acceptLimited\": false\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://api.openpulse.cloud/v1/listeners/analyse-website")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"companyName\": \"<string>\",\n \"website\": \"<string>\",\n \"sources\": [],\n \"customObjective\": \"<string>\",\n \"description\": \"<string>\",\n \"acceptLimited\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.openpulse.cloud/v1/listeners/analyse-website")
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 \"companyName\": \"<string>\",\n \"website\": \"<string>\",\n \"sources\": [],\n \"customObjective\": \"<string>\",\n \"description\": \"<string>\",\n \"acceptLimited\": false\n}"
response = http.request(request)
puts response.read_body{}{
"message": "<string>",
"code": "<string>"
}{
"message": "<string>",
"code": "<string>"
}{
"message": "<string>",
"code": "<string>"
}Derive a listener plan from a website
Crawls a website and returns a complete listener plan: keywords, exclusions, per-source queries, and a mode-specific profile — target roles, rival names and pain vocabulary, or place categories. Everything is editable before it runs.
This is the step every competing product replaces with an empty keyword box.
Rate limited to 5 calls an hour per key, and counted against the workspace’s monthly allowance. An unreadable website answers 422 with website_unreadable rather than an error: the caller is asked to describe the business instead. Nothing is counted against the allowance unless the analysis succeeded.
To recover from a website_unreadable 422, resend the same request with description filled in and acceptLimited: true: the plan is then generated from the description instead of a crawl.
curl --request POST \
--url https://api.openpulse.cloud/v1/listeners/analyse-website \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"companyName": "<string>",
"website": "<string>",
"sources": [],
"customObjective": "<string>",
"description": "<string>",
"acceptLimited": false
}
'import requests
url = "https://api.openpulse.cloud/v1/listeners/analyse-website"
payload = {
"companyName": "<string>",
"website": "<string>",
"sources": [],
"customObjective": "<string>",
"description": "<string>",
"acceptLimited": False
}
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({
companyName: '<string>',
website: '<string>',
sources: [],
customObjective: '<string>',
description: '<string>',
acceptLimited: false
})
};
fetch('https://api.openpulse.cloud/v1/listeners/analyse-website', 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://api.openpulse.cloud/v1/listeners/analyse-website",
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([
'companyName' => '<string>',
'website' => '<string>',
'sources' => [
],
'customObjective' => '<string>',
'description' => '<string>',
'acceptLimited' => false
]),
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://api.openpulse.cloud/v1/listeners/analyse-website"
payload := strings.NewReader("{\n \"companyName\": \"<string>\",\n \"website\": \"<string>\",\n \"sources\": [],\n \"customObjective\": \"<string>\",\n \"description\": \"<string>\",\n \"acceptLimited\": false\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://api.openpulse.cloud/v1/listeners/analyse-website")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"companyName\": \"<string>\",\n \"website\": \"<string>\",\n \"sources\": [],\n \"customObjective\": \"<string>\",\n \"description\": \"<string>\",\n \"acceptLimited\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.openpulse.cloud/v1/listeners/analyse-website")
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 \"companyName\": \"<string>\",\n \"website\": \"<string>\",\n \"sources\": [],\n \"customObjective\": \"<string>\",\n \"description\": \"<string>\",\n \"acceptLimited\": false\n}"
response = http.request(request)
puts response.read_body{}{
"message": "<string>",
"code": "<string>"
}{
"message": "<string>",
"code": "<string>"
}{
"message": "<string>",
"code": "<string>"
}Authorizations
A workspace API key, created in workspace settings and sent as Authorization: Bearer op_live_…. Session cookies are not accepted. Scopes: signals:read, accounts:read, listeners:read, listeners:analyse, pipeline:read, pipeline:write, collections:write, export:read.
Body
The wizard's step-1 answers. sources and objective decide the mode: find_local_prospects plans a places listener, find_dissatisfaction plans a rivals listener, and every other objective plans market.
The company's name.
2 - 120The company's homepage. This is what gets crawled.
What the listener is for. find_dissatisfaction returns a rival profile (competitor names, pain vocabulary); find_local_prospects returns a places profile (categories, locations); every other value, including custom, returns a market keyword plan.
find_customers, find_competitors, find_partnerships, find_dissatisfaction, find_local_prospects, custom Which platforms the resulting plan should carry queries for.
1x, linkedin, facebook, reddit, hackernews, linkedin_jobs, indeed, ashby, greenhouse, lever, google_reviews, g2, trustpilot, youtube Free text describing the objective when objective is custom.
500A short description of the business, used to plan when the site cannot be crawled. Required on the retry that follows a website_unreadable 422.
2000Set on a retry to proceed without a successful crawl, planning from description instead. Sending it true on the first call skips the crawl for no reason, since there is no description yet to plan from.
Response
Success.
The response is of type object.
Was this page helpful?