curl --request POST \
--url https://api.parallellabs.app/api/v0/seo/projects/ensure \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"companyId": "<string>",
"domain": "<string>"
}
'import requests
url = "https://api.parallellabs.app/api/v0/seo/projects/ensure"
payload = {
"companyId": "<string>",
"domain": "<string>"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({companyId: '<string>', domain: '<string>'})
};
fetch('https://api.parallellabs.app/api/v0/seo/projects/ensure', 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.parallellabs.app/api/v0/seo/projects/ensure",
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([
'companyId' => '<string>',
'domain' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$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.parallellabs.app/api/v0/seo/projects/ensure"
payload := strings.NewReader("{\n \"companyId\": \"<string>\",\n \"domain\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
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.parallellabs.app/api/v0/seo/projects/ensure")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"companyId\": \"<string>\",\n \"domain\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.parallellabs.app/api/v0/seo/projects/ensure")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"companyId\": \"<string>\",\n \"domain\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"companyId": "<string>",
"domain": "<string>",
"name": "<string>",
"status": "active",
"frequency": "manual",
"timezone": "<string>",
"nextRunAt": "2023-11-07T05:31:56Z",
"lastRunAt": "2023-11-07T05:31:56Z",
"runCount": 123,
"enabledModules": {},
"trackedKeywords": [
"<string>"
],
"competitorDomains": [
"<string>"
],
"trackedPrompts": [
"<string>"
],
"brandNames": [
"<string>"
],
"aiPlatforms": {},
"trackAiOverview": true,
"backlinksMaxLinks": 123,
"auditMaxPages": 123,
"country": "<string>",
"language": "<string>",
"created_date": "2023-11-07T05:31:56Z",
"updated_date": "2023-11-07T05:31:56Z",
"created": true
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}Get or create the project for a domain.
Module pages call this when a site is typed freeform: the domain gets a blank project (manual schedule, no modules enabled) so the module can run against it, without the user first setting up monitoring.
curl --request POST \
--url https://api.parallellabs.app/api/v0/seo/projects/ensure \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"companyId": "<string>",
"domain": "<string>"
}
'import requests
url = "https://api.parallellabs.app/api/v0/seo/projects/ensure"
payload = {
"companyId": "<string>",
"domain": "<string>"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({companyId: '<string>', domain: '<string>'})
};
fetch('https://api.parallellabs.app/api/v0/seo/projects/ensure', 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.parallellabs.app/api/v0/seo/projects/ensure",
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([
'companyId' => '<string>',
'domain' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$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.parallellabs.app/api/v0/seo/projects/ensure"
payload := strings.NewReader("{\n \"companyId\": \"<string>\",\n \"domain\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
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.parallellabs.app/api/v0/seo/projects/ensure")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"companyId\": \"<string>\",\n \"domain\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.parallellabs.app/api/v0/seo/projects/ensure")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"companyId\": \"<string>\",\n \"domain\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"companyId": "<string>",
"domain": "<string>",
"name": "<string>",
"status": "active",
"frequency": "manual",
"timezone": "<string>",
"nextRunAt": "2023-11-07T05:31:56Z",
"lastRunAt": "2023-11-07T05:31:56Z",
"runCount": 123,
"enabledModules": {},
"trackedKeywords": [
"<string>"
],
"competitorDomains": [
"<string>"
],
"trackedPrompts": [
"<string>"
],
"brandNames": [
"<string>"
],
"aiPlatforms": {},
"trackAiOverview": true,
"backlinksMaxLinks": 123,
"auditMaxPages": 123,
"country": "<string>",
"language": "<string>",
"created_date": "2023-11-07T05:31:56Z",
"updated_date": "2023-11-07T05:31:56Z",
"created": true
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}Authorizations
Company API key - scoped to a specific company. Generate from the Integrations page in your dashboard.
Body
Company and domain
Response
The existing or new project
The project, with whether it was just created
Project ID (UUID)
Company ID
Monitored host, lowercased
Display name
active or paused
active, paused manual, daily, weekly or monthly
manual, daily, weekly, monthly Schedule timezone
Next scheduled run (null when manual/paused)
Last run
Runs so far
Module key -> enabled. Keys: keyword_research, domain_overview, opportunities, backlinks, link_profile, rank_tracking, competitors, ai_visibility, site_audit, lighthouse
Keywords for rank tracking and keyword research
Pinned competitor domains
Questions asked of AI platforms (AEO)
Strings that count as a brand mention
AI platform key -> enabled (chatgpt, claude, gemini, perplexity)
Also check Google AI Overview
Backlinks pulled per run (billed per link)
Pages crawled per scheduled site audit
Market country code, e.g. us
Language code, e.g. en
True when the project was created by this call

